blob: 6f5226c6a257eb78ae32d68990d588435d4d2274 [file] [log] [blame]
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package util.build;
18
Jesse Wilson4c69b512011-09-18 12:13:00 -040019import com.android.dex.util.FileUtils;
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -070020
21import dot.junit.AllTests;
22
23import junit.framework.TestCase;
24import junit.framework.TestResult;
25import junit.framework.TestSuite;
26import junit.textui.TestRunner;
27
28import java.io.BufferedWriter;
29import java.io.File;
30import java.io.FileNotFoundException;
31import java.io.FileOutputStream;
32import java.io.FileReader;
33import java.io.IOException;
34import java.io.OutputStreamWriter;
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.Comparator;
38import java.util.HashSet;
39import java.util.Iterator;
40import java.util.LinkedHashMap;
41import java.util.List;
42import java.util.Scanner;
43import java.util.Set;
44import java.util.TreeSet;
45import java.util.Map.Entry;
46import java.util.regex.MatchResult;
47import java.util.regex.Matcher;
48import java.util.regex.Pattern;
49
50/**
51 * Main class to generate data from the test suite to later run from a shell
52 * script. the project's home folder.<br>
53 * <project-home>/src must contain the java sources<br>
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -070054 * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br>
55 * (one Main class for each test method in the Test_... class
56 */
57public class BuildDalvikSuite {
58
59 public static boolean DEBUG = true;
60
61 private static String JAVASRC_FOLDER = "";
62 private static String MAIN_SRC_OUTPUT_FOLDER = "";
63
64 // the folder for the generated junit-files for the cts host (which in turn
65 // execute the real vm tests using adb push/shell etc)
66 private static String HOSTJUNIT_SRC_OUTPUT_FOLDER = "";
67 private static String OUTPUT_FOLDER = "";
68 private static String COMPILED_CLASSES_FOLDER = "";
69
70 private static String CLASSES_OUTPUT_FOLDER = "";
71 private static String HOSTJUNIT_CLASSES_OUTPUT_FOLDER = "";
72
73 private static String CLASS_PATH = "";
74
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -070075 private static String restrictTo = null; // e.g. restrict to "opcodes.add_double"
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -070076
77 private static final String TARGET_JAR_ROOT_PATH = "/data/local/tmp/vm-tests";
78
79 private int testClassCnt = 0;
80 private int testMethodsCnt = 0;
81
82 /*
83 * using a linked hashmap to keep the insertion order for iterators.
84 * the junit suite/tests adding order is used to generate the order of the
85 * report.
86 * a map. key: fully qualified class name, value: a list of test methods for
87 * the given class
88 */
89 private LinkedHashMap<String, List<String>> map = new LinkedHashMap<String,
90 List<String>>();
91
92 private class MethodData {
93 String methodBody, constraint, title;
94 }
95
96 /**
97 * @param args
98 * args 0 must be the project root folder (where src, lib etc.
99 * resides)
100 * @throws IOException
101 */
102 public static void main(String[] args) throws IOException {
103
104 if (args.length > 5) {
105 JAVASRC_FOLDER = args[0];
106 OUTPUT_FOLDER = args[1];
107 CLASS_PATH = args[2];
108 MAIN_SRC_OUTPUT_FOLDER = args[3];
109 CLASSES_OUTPUT_FOLDER = MAIN_SRC_OUTPUT_FOLDER + "/classes";
110
111 COMPILED_CLASSES_FOLDER = args[4];
112
113 HOSTJUNIT_SRC_OUTPUT_FOLDER = args[5];
114 HOSTJUNIT_CLASSES_OUTPUT_FOLDER = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/classes";
115
116 if (args.length > 6) {
117 // optional: restrict to e.g. "opcodes.add_double"
118 restrictTo = args[6];
119 System.out.println("restricting build to: " + restrictTo);
120 }
121
122 } else {
123 System.out.println("usage: java-src-folder output-folder classpath " +
124 "generated-main-files compiled_output generated-main-files " +
125 "[restrict-to-opcode]");
126 System.exit(-1);
127 }
128
129 long start = System.currentTimeMillis();
130 BuildDalvikSuite cat = new BuildDalvikSuite();
131 cat.compose();
132 long end = System.currentTimeMillis();
133
134 System.out.println("elapsed seconds: " + (end - start) / 1000);
135 }
136
137 public void compose() throws IOException {
138 System.out.println("Collecting all junit tests...");
139 new TestRunner() {
140 @Override
141 protected TestResult createTestResult() {
142 return new TestResult() {
143 @Override
144 protected void run(TestCase test) {
145 addToTests(test);
146 }
147
148 };
149 }
150 }.doRun(AllTests.suite());
151
152 // for each combination of TestClass and method, generate a Main_testN1
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700153 // class in the respective package.
154 // for the report make sure all N... tests are called first, then B,
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700155 // then E, then VFE test methods.
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700156 // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() ->
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700157 // File Main_testN1.java in package dxc.junit.opcodes.aaload.
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700158 //
159 handleTests();
160 }
161
162 private void addToTests(TestCase test) {
163
164 String packageName = test.getClass().getPackage().getName();
165 packageName = packageName.substring(packageName.lastIndexOf('.'));
166
167
168 String method = test.getName(); // e.g. testVFE2
169 String fqcn = test.getClass().getName(); // e.g.
170 // dxc.junit.opcodes.iload_3.Test_iload_3
171
172 // ignore all tests not belonging to the given restriction
173 if (restrictTo != null && !fqcn.contains(restrictTo)) return;
174
175 testMethodsCnt++;
176 List<String> li = map.get(fqcn);
177 if (li == null) {
178 testClassCnt++;
179 li = new ArrayList<String>();
180 map.put(fqcn, li);
181 }
182 li.add(method);
183 }
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700184 private String curJunitFileName = null;
185 private String curJunitFileData = "";
186
187 private JavacBuildStep javacHostJunitBuildStep;
188
189 private void flushHostJunitFile() {
190 if (curJunitFileName != null) {
191 File toWrite = new File(curJunitFileName);
192 String absPath = toWrite.getAbsolutePath();
193 // add to java source files for later compilation
194 javacHostJunitBuildStep.addSourceFile(absPath);
195 // write file
196 curJunitFileData += "\n}\n";
197 writeToFileMkdir(toWrite, curJunitFileData);
198 curJunitFileName = null;
199 curJunitFileData = "";
200 }
201 }
202
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700203 private void openCTSHostFileFor(String pName, String classOnlyName) {
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700204 // flush previous JunitFile
205 flushHostJunitFile();
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700206 String sourceName = "JUnit_" + classOnlyName;
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700207
208 // prepare current testcase-file
209 curJunitFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + pName.replaceAll("\\.","/") + "/" +
210 sourceName + ".java";
211 curJunitFileData = getWarningMessage() +
212 "package " + pName + ";\n" +
213 "import java.io.IOException;\n" +
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700214 "import com.android.tradefed.testtype.DeviceTestCase;\n" +
wsmlbye83c5f22014-06-02 20:50:12 -0700215 "import com.android.tradefed.config.Option;\n" +
216 "import com.android.tradefed.config.Option.Importance;\n" +
217 "import com.android.tradefed.util.AbiFormatter;\n" +
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700218 "\n" +
219 "public class " + sourceName + " extends DeviceTestCase {\n";
220 }
221
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700222 private String getShellExecJavaLine(String classpath, String mainclass) {
wsmlbye83c5f22014-06-02 20:50:12 -0700223 String cmd = String.format("ANDROID_DATA=%s dalvikvm|#ABI#| -Xmx512M -Xss32K " +
Brian Muramatsu24e14672011-10-25 18:36:55 -0700224 "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH,
Brett Chabot44650b12011-10-25 18:09:39 -0700225 classpath, mainclass);
wsmlbye83c5f22014-06-02 20:50:12 -0700226 return "String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mForceAbi);\n" +
227 "String res = getDevice().executeShellCommand(cmd);\n" +
228 "// A sucessful adb shell command returns an empty string.\n" +
229 "assertEquals(cmd, \"\", res);";
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700230 }
231
232 private String getWarningMessage() {
233 return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n";
234 }
235
236 private void addCTSHostMethod(String pName, String method, MethodData md,
237 Set<String> dependentTestClassNames) {
238 curJunitFileData += "public void " + method + "() throws Exception {\n";
239 final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar",
240 TARGET_JAR_ROOT_PATH);
241
242 // push class with Main jar.
243 String mjar = "Main_" + method + ".jar";
244 String pPath = pName.replaceAll("\\.","/");
245 String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar);
246
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700247 String cp = String.format("%s:%s", targetCoreJarPath, mainJar);
248 for (String depFqcn : dependentTestClassNames) {
249 int lastDotPos = depFqcn.lastIndexOf('.');
250 String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar";
251 String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH,
252 sourceName);
253 cp += ":" + targetName;
254 // dot.junit.opcodes.invoke_interface_range.ITest
255 // -> dot/junit/opcodes/invoke_interface_range/ITest.jar
256 }
257
258 //"dot.junit.opcodes.add_double_2addr.Main_testN2";
259 String mainclass = pName + ".Main_" + method;
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700260 curJunitFileData += " " + getShellExecJavaLine(cp, mainclass);
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700261 curJunitFileData += "}\n\n";
262 }
263
264 private void handleTests() throws IOException {
265 System.out.println("collected " + testMethodsCnt + " test methods in " +
266 testClassCnt + " junit test classes");
jeffhao4d25a2b2012-06-19 15:57:00 -0700267 String datafileContent = "";
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700268 Set<BuildStep> targets = new TreeSet<BuildStep>();
269
270 javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
271
272
273 JavacBuildStep javacBuildStep = new JavacBuildStep(
274 CLASSES_OUTPUT_FOLDER, CLASS_PATH);
275
276 for (Entry<String, List<String>> entry : map.entrySet()) {
277
278 String fqcn = entry.getKey();
279 int lastDotPos = fqcn.lastIndexOf('.');
280 String pName = fqcn.substring(0, lastDotPos);
281 String classOnlyName = fqcn.substring(lastDotPos + 1);
282 String instPrefix = "new " + classOnlyName + "()";
283
284 openCTSHostFileFor(pName, classOnlyName);
285
wsmlbye83c5f22014-06-02 20:50:12 -0700286 curJunitFileData += "@Option(name = AbiFormatter.FORCE_ABI_STRING,\n" +
287 "description = AbiFormatter.FORCE_ABI_DESCRIPTION,\n" +
288 "importance = Importance.IF_UNSET)\nprivate String mForceAbi = null;\n\n";
289
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700290 List<String> methods = entry.getValue();
291 Collections.sort(methods, new Comparator<String>() {
292 public int compare(String s1, String s2) {
293 // TODO sort according: test ... N, B, E, VFE
294 return s1.compareTo(s2);
295 }
296 });
297 for (String method : methods) {
298 // e.g. testN1
299 if (!method.startsWith("test")) {
300 throw new RuntimeException("no test method: " + method);
301 }
302
303 // generate the Main_xx java class
304
305 // a Main_testXXX.java contains:
306 // package <packagenamehere>;
307 // public class Main_testxxx {
308 // public static void main(String[] args) {
309 // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
310 // }
311 // }
312 MethodData md = parseTestMethod(pName, classOnlyName, method);
313 String methodContent = md.methodBody;
314
315 Set<String> dependentTestClassNames = parseTestClassName(pName,
316 classOnlyName, methodContent);
317
318 addCTSHostMethod(pName, method, md, dependentTestClassNames);
319
320
321 if (dependentTestClassNames.isEmpty()) {
322 continue;
323 }
324
325
326 String content = getWarningMessage() +
327 "package " + pName + ";\n" +
328 "import " + pName + ".d.*;\n" +
329 "import dot.junit.*;\n" +
330 "public class Main_" + method + " extends DxAbstractMain {\n" +
331 " public static void main(String[] args) throws Exception {" +
332 methodContent + "\n}\n";
333
334 String fileName = getFileName(pName, method, ".java");
335 File sourceFile = getFileFromPackage(pName, method);
336
337 File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
338 getFileName(pName, method, ".class"));
339 // if (sourceFile.lastModified() > classFile.lastModified()) {
340 writeToFile(sourceFile, content);
341 javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
342
343 BuildStep dexBuildStep = generateDexBuildStep(
344 CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
345 targets.add(dexBuildStep);
346 // }
347
jeffhao4d25a2b2012-06-19 15:57:00 -0700348
349 // prepare the entry in the data file for the bash script.
350 // e.g.
351 // main class to execute; opcode/constraint; test purpose
352 // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
353 // (#1)
354
355 char ca = method.charAt("test".length()); // either N,B,E,
356 // or V (VFE)
357 String comment;
358 switch (ca) {
359 case 'N':
360 comment = "Normal #" + method.substring(5);
361 break;
362 case 'B':
363 comment = "Boundary #" + method.substring(5);
364 break;
365 case 'E':
366 comment = "Exception #" + method.substring(5);
367 break;
368 case 'V':
369 comment = "Verifier #" + method.substring(7);
370 break;
371 default:
372 throw new RuntimeException("unknown test abbreviation:"
373 + method + " for " + fqcn);
374 }
375
376 String line = pName + ".Main_" + method + ";";
377 for (String className : dependentTestClassNames) {
378 line += className + " ";
379 }
380
381
382 // test description
383 String[] pparts = pName.split("\\.");
384 // detail e.g. add_double
385 String detail = pparts[pparts.length-1];
386 // type := opcode | verify
387 String type = pparts[pparts.length-2];
388
389 String description;
390 if ("format".equals(type)) {
391 description = "format";
392 } else if ("opcodes".equals(type)) {
393 // Beautify name, so it matches the actual mnemonic
394 detail = detail.replaceAll("_", "-");
395 detail = detail.replace("-from16", "/from16");
396 detail = detail.replace("-high16", "/high16");
397 detail = detail.replace("-lit8", "/lit8");
398 detail = detail.replace("-lit16", "/lit16");
399 detail = detail.replace("-4", "/4");
400 detail = detail.replace("-16", "/16");
401 detail = detail.replace("-32", "/32");
402 detail = detail.replace("-jumbo", "/jumbo");
403 detail = detail.replace("-range", "/range");
404 detail = detail.replace("-2addr", "/2addr");
405
406 // Unescape reserved words
407 detail = detail.replace("opc-", "");
408
409 description = detail;
410 } else if ("verify".equals(type)) {
411 description = "verifier";
412 } else {
413 description = type + " " + detail;
414 }
415
416 String details = (md.title != null ? md.title : "");
417 if (md.constraint != null) {
418 details = " Constraint " + md.constraint + ", " + details;
419 }
420 if (details.length() != 0) {
421 details = details.substring(0, 1).toUpperCase()
422 + details.substring(1);
423 }
424
425 line += ";" + description + ";" + comment + ";" + details;
426
427 datafileContent += line + "\n";
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700428 generateBuildStepFor(pName, method, dependentTestClassNames,
429 targets);
430 }
431
432
433 }
434
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700435 // write latest HOSTJUNIT generated file.
436 flushHostJunitFile();
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700437
jeffhao4d25a2b2012-06-19 15:57:00 -0700438 File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
439 scriptDataDir.mkdirs();
440 writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
441
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700442 if (!javacHostJunitBuildStep.build()) {
443 System.out.println("main javac cts-host-hostjunit-classes build step failed");
444 System.exit(1);
445 }
446
447 if (javacBuildStep.build()) {
448 for (BuildStep buildStep : targets) {
449 if (!buildStep.build()) {
450 System.out.println("building failed. buildStep: " +
451 buildStep.getClass().getName() + ", " + buildStep);
452 System.exit(1);
453 }
454 }
455 } else {
456 System.out.println("main javac dalvik-cts-buildutil build step failed");
457 System.exit(1);
458 }
459 }
460
461 private void generateBuildStepFor(String pName, String method,
462 Set<String> dependentTestClassNames, Set<BuildStep> targets) {
463
464
465 for (String dependentTestClassName : dependentTestClassNames) {
466 generateBuildStepForDependant(dependentTestClassName, targets);
467 }
468 }
469
470 private void generateBuildStepForDependant(String dependentTestClassName,
471 Set<BuildStep> targets) {
472
473 File sourceFolder = new File(JAVASRC_FOLDER);
474 String fileName = dependentTestClassName.replace('.', '/').trim();
475
476 if (new File(sourceFolder, fileName + ".dfh").exists()) {
477
478 BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
479 JAVASRC_FOLDER, fileName + ".dfh");
480 BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
481 OUTPUT_FOLDER, fileName + ".dex");
482
483 DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
484
485 BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
486 OUTPUT_FOLDER, fileName + ".jar");
487 JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
488 "classes.dex", jarFile, true);
489 jarBuildStep.addChild(buildStep);
490
491 targets.add(jarBuildStep);
492 return;
493 }
494
495 if (new File(sourceFolder, fileName + ".d").exists()) {
496
497 BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
498 JAVASRC_FOLDER, fileName + ".d");
499 BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
500 OUTPUT_FOLDER, fileName + ".dex");
501
502 DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
503
504 BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
505 OUTPUT_FOLDER, fileName + ".jar");
506
507 JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
508 "classes.dex", jarFile, true);
509 jarBuildStep.addChild(buildStep);
510 targets.add(jarBuildStep);
511 return;
512 }
513
514 if (new File(sourceFolder, fileName + ".java").exists()) {
515
516 BuildStep dexBuildStep = generateDexBuildStep(
517 COMPILED_CLASSES_FOLDER, fileName);
518 targets.add(dexBuildStep);
519 return;
520 }
521
522 try {
523 if (Class.forName(dependentTestClassName) != null) {
524
525 BuildStep dexBuildStep = generateDexBuildStep(
526 COMPILED_CLASSES_FOLDER, fileName);
527 targets.add(dexBuildStep);
528 return;
529 }
530 } catch (ClassNotFoundException e) {
531 // do nothing
532 }
533
534 throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
535 dependentTestClassName + ";" + fileName);
536 }
537
538 private BuildStep generateDexBuildStep(String classFileFolder,
539 String classFileName) {
540 BuildStep.BuildFile classFile = new BuildStep.BuildFile(
541 classFileFolder, classFileName + ".class");
542
543 BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
544 classFileName + "_tmp.jar");
545
546 JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
547 ".class", tmpJarFile, false);
548
549 BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
550 classFileName + ".jar");
551
552 DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
553 true);
554
555 dexBuildStep.addChild(jarBuildStep);
556 return dexBuildStep;
557
558 }
559
560 /**
561 * @param pName
562 * @param classOnlyName
563 * @param methodSource
564 * @return testclass names
565 */
566 private Set<String> parseTestClassName(String pName, String classOnlyName,
567 String methodSource) {
568 Set<String> entries = new HashSet<String>();
569 String opcodeName = classOnlyName.substring(5);
570
571 Scanner scanner = new Scanner(methodSource);
572
573 String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
574 "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
575
576 String token = null;
577 for (String pattern : patterns) {
578 token = scanner.findWithinHorizon(pattern, methodSource.length());
579 if (token != null) {
580 break;
581 }
582 }
583
584 if (token == null) {
585 System.err.println("warning: failed to find dependent test class name: " + pName +
586 ", " + classOnlyName + " in methodSource:\n" + methodSource);
587 return entries;
588 }
589
590 MatchResult result = scanner.match();
591
592 entries.add((pName + ".d." + result.group(1)).trim());
593
594 // search additional @uses directives
595 Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
596 Matcher m = p.matcher(methodSource);
597 while (m.find()) {
598 String res = m.group(1);
599 entries.add(res.trim());
600 }
601
602 // lines with the form @uses
603 // dot.junit.opcodes.add_double.jm.T_add_double_2
604 // one dependency per one @uses
605 // TODO
606
607 return entries;
608 }
609
610 private MethodData parseTestMethod(String pname, String classOnlyName,
611 String method) {
612
613 String path = pname.replaceAll("\\.", "/");
614 String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
615 File f = new File(absPath);
616
617 Scanner scanner;
618 try {
619 scanner = new Scanner(f);
620 } catch (FileNotFoundException e) {
621 throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
622 ", msg:" + e.getMessage());
623 }
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700624
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700625 String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700626
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700627 String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
628 if (token == null) {
629 throw new RuntimeException("cannot find method source of 'public void " + method +
630 "' in file '" + absPath + "'");
631 }
632
633 MatchResult result = scanner.match();
634 result.start();
635 result.end();
636
637 StringBuilder builder = new StringBuilder();
638 //builder.append(token);
639
640 try {
641 FileReader reader = new FileReader(f);
642 reader.skip(result.end());
643
644 char currentChar;
645 int blocks = 1;
646 while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
647 switch (currentChar) {
648 case '}': {
649 blocks--;
650 builder.append(currentChar);
651 break;
652 }
653 case '{': {
654 blocks++;
655 builder.append(currentChar);
656 break;
657 }
658 default: {
659 builder.append(currentChar);
660 break;
661 }
662 }
663 }
664 if (reader != null) {
665 reader.close();
666 }
667 } catch (Exception e) {
668 throw new RuntimeException("failed to parse", e);
669 }
670
671 // find the @title/@constraint in javadoc comment for this method
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700672 // using platform's default charset
673 String all = new String(FileUtils.readFile(f));
674 // System.out.println("grepping javadoc found for method " + method +
675 // " in " + pname + "," + classOnlyName);
676 String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
677 Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
678 Matcher m = p.matcher(all);
679 String title = null, constraint = null;
680 if (m.find()) {
681 String res = m.group(1);
682 // System.out.println("res: " + res);
683 // now grep @title and @constraint
684 Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
685 .matcher(res);
686 if (titleM.find()) {
687 title = titleM.group(1).replaceAll("\\n \\*", "");
688 title = title.replaceAll("\\n", " ");
689 title = title.trim();
690 // System.out.println("title: " + title);
691 } else {
692 System.err.println("warning: no @title found for method " + method + " in " + pname +
693 "," + classOnlyName);
694 }
695 // constraint can be one line only
696 Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
697 res);
698 if (constraintM.find()) {
699 constraint = constraintM.group(1);
700 constraint = constraint.trim();
701 // System.out.println("constraint: " + constraint);
702 } else if (method.contains("VFE")) {
703 System.err
704 .println("warning: no @constraint for for a VFE method:" + method + " in " +
705 pname + "," + classOnlyName);
706 }
707 } else {
708 System.err.println("warning: no javadoc found for method " + method + " in " + pname +
709 "," + classOnlyName);
710 }
711 MethodData md = new MethodData();
712 md.methodBody = builder.toString();
713 md.constraint = constraint;
714 md.title = title;
715 if (scanner != null) {
716 scanner.close();
717 }
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700718 return md;
719 }
720
721 private void writeToFileMkdir(File file, String content) {
722 File parent = file.getParentFile();
723 if (!parent.exists() && !parent.mkdirs()) {
724 throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
725 }
726 writeToFile(file, content);
727 }
728
729 private void writeToFile(File file, String content) {
730 try {
731 if (file.length() == content.length()) {
732 FileReader reader = new FileReader(file);
733 char[] charContents = new char[(int) file.length()];
734 reader.read(charContents);
Chris Dearman7c9f67a2011-12-15 21:24:16 -0800735 reader.close();
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700736 String contents = new String(charContents);
737 if (contents.equals(content)) {
738 // System.out.println("skipping identical: "
739 // + file.getAbsolutePath());
740 return;
741 }
742 }
743
744 //System.out.println("writing file " + file.getAbsolutePath());
745
746 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
747 new FileOutputStream(file), "utf-8"));
748 bw.write(content);
749 bw.close();
750 } catch (Exception e) {
751 throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
752 ", msg:" + e.getMessage());
753 }
754 }
755
756 private File getFileFromPackage(String pname, String methodName)
757 throws IOException {
758 // e.g. dxc.junit.argsreturns.pargsreturn
759 String path = getFileName(pname, methodName, ".java");
760 String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
761 File dirPath = new File(absPath);
762 File parent = dirPath.getParentFile();
763 if (!parent.exists() && !parent.mkdirs()) {
764 throw new IOException("failed to create directory: " + absPath);
765 }
766 return dirPath;
767 }
768
769 private String getFileName(String pname, String methodName,
770 String extension) {
771 String path = pname.replaceAll("\\.", "/");
772 return new File(path, "Main_" + methodName + extension).getPath();
773 }
774}