blob: 0307375ec5ceb492b73121b90f9835c1f83e4482 [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" +
Stuart Scottfef5e2c2014-09-05 17:37:18 -0700214 "import com.android.tradefed.testtype.IAbi;\n" +
215 "import com.android.tradefed.testtype.IAbiReceiver;\n" +
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700216 "import com.android.tradefed.testtype.DeviceTestCase;\n" +
wsmlbye83c5f22014-06-02 20:50:12 -0700217 "import com.android.tradefed.util.AbiFormatter;\n" +
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700218 "\n" +
Stuart Scottfef5e2c2014-09-05 17:37:18 -0700219 "public class " + sourceName + " extends DeviceTestCase implements IAbiReceiver {\n";
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700220 }
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);
Stuart Scottfef5e2c2014-09-05 17:37:18 -0700226 return "String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mAbi.getBitness());\n" +
wsmlbye83c5f22014-06-02 20:50:12 -0700227 "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
Stuart Scottfef5e2c2014-09-05 17:37:18 -0700286 curJunitFileData += "\n" +
287 "protected IAbi mAbi;\n" +
288 "@Override\n" +
289 "public void setAbi(IAbi abi) {\n" +
290 " mAbi = abi;\n" +
291 "}\n\n";
wsmlbye83c5f22014-06-02 20:50:12 -0700292
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700293 List<String> methods = entry.getValue();
294 Collections.sort(methods, new Comparator<String>() {
295 public int compare(String s1, String s2) {
296 // TODO sort according: test ... N, B, E, VFE
297 return s1.compareTo(s2);
298 }
299 });
300 for (String method : methods) {
301 // e.g. testN1
302 if (!method.startsWith("test")) {
303 throw new RuntimeException("no test method: " + method);
304 }
305
306 // generate the Main_xx java class
307
308 // a Main_testXXX.java contains:
309 // package <packagenamehere>;
310 // public class Main_testxxx {
311 // public static void main(String[] args) {
312 // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
313 // }
314 // }
315 MethodData md = parseTestMethod(pName, classOnlyName, method);
316 String methodContent = md.methodBody;
317
318 Set<String> dependentTestClassNames = parseTestClassName(pName,
319 classOnlyName, methodContent);
320
321 addCTSHostMethod(pName, method, md, dependentTestClassNames);
322
323
324 if (dependentTestClassNames.isEmpty()) {
325 continue;
326 }
327
328
329 String content = getWarningMessage() +
330 "package " + pName + ";\n" +
331 "import " + pName + ".d.*;\n" +
332 "import dot.junit.*;\n" +
333 "public class Main_" + method + " extends DxAbstractMain {\n" +
334 " public static void main(String[] args) throws Exception {" +
335 methodContent + "\n}\n";
336
337 String fileName = getFileName(pName, method, ".java");
338 File sourceFile = getFileFromPackage(pName, method);
339
340 File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
341 getFileName(pName, method, ".class"));
342 // if (sourceFile.lastModified() > classFile.lastModified()) {
343 writeToFile(sourceFile, content);
344 javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
345
346 BuildStep dexBuildStep = generateDexBuildStep(
347 CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
348 targets.add(dexBuildStep);
349 // }
350
jeffhao4d25a2b2012-06-19 15:57:00 -0700351
352 // prepare the entry in the data file for the bash script.
353 // e.g.
354 // main class to execute; opcode/constraint; test purpose
355 // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
356 // (#1)
357
358 char ca = method.charAt("test".length()); // either N,B,E,
359 // or V (VFE)
360 String comment;
361 switch (ca) {
362 case 'N':
363 comment = "Normal #" + method.substring(5);
364 break;
365 case 'B':
366 comment = "Boundary #" + method.substring(5);
367 break;
368 case 'E':
369 comment = "Exception #" + method.substring(5);
370 break;
371 case 'V':
372 comment = "Verifier #" + method.substring(7);
373 break;
374 default:
375 throw new RuntimeException("unknown test abbreviation:"
376 + method + " for " + fqcn);
377 }
378
379 String line = pName + ".Main_" + method + ";";
380 for (String className : dependentTestClassNames) {
381 line += className + " ";
382 }
383
384
385 // test description
386 String[] pparts = pName.split("\\.");
387 // detail e.g. add_double
388 String detail = pparts[pparts.length-1];
389 // type := opcode | verify
390 String type = pparts[pparts.length-2];
391
392 String description;
393 if ("format".equals(type)) {
394 description = "format";
395 } else if ("opcodes".equals(type)) {
396 // Beautify name, so it matches the actual mnemonic
397 detail = detail.replaceAll("_", "-");
398 detail = detail.replace("-from16", "/from16");
399 detail = detail.replace("-high16", "/high16");
400 detail = detail.replace("-lit8", "/lit8");
401 detail = detail.replace("-lit16", "/lit16");
402 detail = detail.replace("-4", "/4");
403 detail = detail.replace("-16", "/16");
404 detail = detail.replace("-32", "/32");
405 detail = detail.replace("-jumbo", "/jumbo");
406 detail = detail.replace("-range", "/range");
407 detail = detail.replace("-2addr", "/2addr");
408
409 // Unescape reserved words
410 detail = detail.replace("opc-", "");
411
412 description = detail;
413 } else if ("verify".equals(type)) {
414 description = "verifier";
415 } else {
416 description = type + " " + detail;
417 }
418
419 String details = (md.title != null ? md.title : "");
420 if (md.constraint != null) {
421 details = " Constraint " + md.constraint + ", " + details;
422 }
423 if (details.length() != 0) {
424 details = details.substring(0, 1).toUpperCase()
425 + details.substring(1);
426 }
427
428 line += ";" + description + ";" + comment + ";" + details;
429
430 datafileContent += line + "\n";
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700431 generateBuildStepFor(pName, method, dependentTestClassNames,
432 targets);
433 }
434
435
436 }
437
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700438 // write latest HOSTJUNIT generated file.
439 flushHostJunitFile();
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700440
jeffhao4d25a2b2012-06-19 15:57:00 -0700441 File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
442 scriptDataDir.mkdirs();
443 writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
444
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700445 if (!javacHostJunitBuildStep.build()) {
446 System.out.println("main javac cts-host-hostjunit-classes build step failed");
447 System.exit(1);
448 }
449
450 if (javacBuildStep.build()) {
451 for (BuildStep buildStep : targets) {
452 if (!buildStep.build()) {
453 System.out.println("building failed. buildStep: " +
454 buildStep.getClass().getName() + ", " + buildStep);
455 System.exit(1);
456 }
457 }
458 } else {
459 System.out.println("main javac dalvik-cts-buildutil build step failed");
460 System.exit(1);
461 }
462 }
463
464 private void generateBuildStepFor(String pName, String method,
465 Set<String> dependentTestClassNames, Set<BuildStep> targets) {
466
467
468 for (String dependentTestClassName : dependentTestClassNames) {
469 generateBuildStepForDependant(dependentTestClassName, targets);
470 }
471 }
472
473 private void generateBuildStepForDependant(String dependentTestClassName,
474 Set<BuildStep> targets) {
475
476 File sourceFolder = new File(JAVASRC_FOLDER);
477 String fileName = dependentTestClassName.replace('.', '/').trim();
478
479 if (new File(sourceFolder, fileName + ".dfh").exists()) {
480
481 BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
482 JAVASRC_FOLDER, fileName + ".dfh");
483 BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
484 OUTPUT_FOLDER, fileName + ".dex");
485
486 DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
487
488 BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
489 OUTPUT_FOLDER, fileName + ".jar");
490 JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
491 "classes.dex", jarFile, true);
492 jarBuildStep.addChild(buildStep);
493
494 targets.add(jarBuildStep);
495 return;
496 }
497
498 if (new File(sourceFolder, fileName + ".d").exists()) {
499
500 BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
501 JAVASRC_FOLDER, fileName + ".d");
502 BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
503 OUTPUT_FOLDER, fileName + ".dex");
504
505 DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
506
507 BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
508 OUTPUT_FOLDER, fileName + ".jar");
509
510 JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
511 "classes.dex", jarFile, true);
512 jarBuildStep.addChild(buildStep);
513 targets.add(jarBuildStep);
514 return;
515 }
516
517 if (new File(sourceFolder, fileName + ".java").exists()) {
518
519 BuildStep dexBuildStep = generateDexBuildStep(
520 COMPILED_CLASSES_FOLDER, fileName);
521 targets.add(dexBuildStep);
522 return;
523 }
524
525 try {
526 if (Class.forName(dependentTestClassName) != null) {
527
528 BuildStep dexBuildStep = generateDexBuildStep(
529 COMPILED_CLASSES_FOLDER, fileName);
530 targets.add(dexBuildStep);
531 return;
532 }
533 } catch (ClassNotFoundException e) {
534 // do nothing
535 }
536
537 throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
538 dependentTestClassName + ";" + fileName);
539 }
540
541 private BuildStep generateDexBuildStep(String classFileFolder,
542 String classFileName) {
543 BuildStep.BuildFile classFile = new BuildStep.BuildFile(
544 classFileFolder, classFileName + ".class");
545
546 BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
547 classFileName + "_tmp.jar");
548
549 JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
550 ".class", tmpJarFile, false);
551
552 BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
553 classFileName + ".jar");
554
555 DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
556 true);
557
558 dexBuildStep.addChild(jarBuildStep);
559 return dexBuildStep;
560
561 }
562
563 /**
564 * @param pName
565 * @param classOnlyName
566 * @param methodSource
567 * @return testclass names
568 */
569 private Set<String> parseTestClassName(String pName, String classOnlyName,
570 String methodSource) {
571 Set<String> entries = new HashSet<String>();
572 String opcodeName = classOnlyName.substring(5);
573
574 Scanner scanner = new Scanner(methodSource);
575
576 String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
577 "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
578
579 String token = null;
580 for (String pattern : patterns) {
581 token = scanner.findWithinHorizon(pattern, methodSource.length());
582 if (token != null) {
583 break;
584 }
585 }
586
587 if (token == null) {
588 System.err.println("warning: failed to find dependent test class name: " + pName +
589 ", " + classOnlyName + " in methodSource:\n" + methodSource);
590 return entries;
591 }
592
593 MatchResult result = scanner.match();
594
595 entries.add((pName + ".d." + result.group(1)).trim());
596
597 // search additional @uses directives
598 Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
599 Matcher m = p.matcher(methodSource);
600 while (m.find()) {
601 String res = m.group(1);
602 entries.add(res.trim());
603 }
604
Andreas Gampe1d1a3a92014-08-23 00:11:27 -0700605 // search for " load(\"...\" " and add as dependency
606 Pattern loadPattern = Pattern.compile("load\\(\"([^\"]*)\"", Pattern.MULTILINE);
607 Matcher loadMatcher = loadPattern.matcher(methodSource);
608 while (loadMatcher.find()) {
609 String res = loadMatcher.group(1);
610 entries.add(res.trim());
611 }
612
613 // search for " loadAndRun(\"...\" " and add as dependency
614 Pattern loadAndRunPattern = Pattern.compile("loadAndRun\\(\"([^\"]*)\"", Pattern.MULTILINE);
615 Matcher loadAndRunMatcher = loadAndRunPattern.matcher(methodSource);
616 while (loadAndRunMatcher.find()) {
617 String res = loadAndRunMatcher.group(1);
618 entries.add(res.trim());
619 }
620
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700621 // lines with the form @uses
622 // dot.junit.opcodes.add_double.jm.T_add_double_2
623 // one dependency per one @uses
624 // TODO
625
626 return entries;
627 }
628
629 private MethodData parseTestMethod(String pname, String classOnlyName,
630 String method) {
631
632 String path = pname.replaceAll("\\.", "/");
633 String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
634 File f = new File(absPath);
635
636 Scanner scanner;
637 try {
638 scanner = new Scanner(f);
639 } catch (FileNotFoundException e) {
640 throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
641 ", msg:" + e.getMessage());
642 }
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700643
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700644 String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
Tsu Chiang Chuange6e045d2011-05-15 14:30:35 -0700645
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700646 String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
647 if (token == null) {
648 throw new RuntimeException("cannot find method source of 'public void " + method +
649 "' in file '" + absPath + "'");
650 }
651
652 MatchResult result = scanner.match();
653 result.start();
654 result.end();
655
656 StringBuilder builder = new StringBuilder();
657 //builder.append(token);
658
659 try {
660 FileReader reader = new FileReader(f);
661 reader.skip(result.end());
662
663 char currentChar;
664 int blocks = 1;
665 while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
666 switch (currentChar) {
667 case '}': {
668 blocks--;
669 builder.append(currentChar);
670 break;
671 }
672 case '{': {
673 blocks++;
674 builder.append(currentChar);
675 break;
676 }
677 default: {
678 builder.append(currentChar);
679 break;
680 }
681 }
682 }
683 if (reader != null) {
684 reader.close();
685 }
686 } catch (Exception e) {
687 throw new RuntimeException("failed to parse", e);
688 }
689
690 // find the @title/@constraint in javadoc comment for this method
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700691 // using platform's default charset
692 String all = new String(FileUtils.readFile(f));
693 // System.out.println("grepping javadoc found for method " + method +
694 // " in " + pname + "," + classOnlyName);
695 String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
696 Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
697 Matcher m = p.matcher(all);
698 String title = null, constraint = null;
699 if (m.find()) {
700 String res = m.group(1);
701 // System.out.println("res: " + res);
702 // now grep @title and @constraint
703 Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
704 .matcher(res);
705 if (titleM.find()) {
706 title = titleM.group(1).replaceAll("\\n \\*", "");
707 title = title.replaceAll("\\n", " ");
708 title = title.trim();
709 // System.out.println("title: " + title);
710 } else {
711 System.err.println("warning: no @title found for method " + method + " in " + pname +
712 "," + classOnlyName);
713 }
714 // constraint can be one line only
715 Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
716 res);
717 if (constraintM.find()) {
718 constraint = constraintM.group(1);
719 constraint = constraint.trim();
720 // System.out.println("constraint: " + constraint);
721 } else if (method.contains("VFE")) {
722 System.err
723 .println("warning: no @constraint for for a VFE method:" + method + " in " +
724 pname + "," + classOnlyName);
725 }
726 } else {
727 System.err.println("warning: no javadoc found for method " + method + " in " + pname +
728 "," + classOnlyName);
729 }
730 MethodData md = new MethodData();
731 md.methodBody = builder.toString();
732 md.constraint = constraint;
733 md.title = title;
734 if (scanner != null) {
735 scanner.close();
736 }
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700737 return md;
738 }
739
740 private void writeToFileMkdir(File file, String content) {
741 File parent = file.getParentFile();
742 if (!parent.exists() && !parent.mkdirs()) {
743 throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
744 }
745 writeToFile(file, content);
746 }
747
748 private void writeToFile(File file, String content) {
749 try {
750 if (file.length() == content.length()) {
751 FileReader reader = new FileReader(file);
752 char[] charContents = new char[(int) file.length()];
753 reader.read(charContents);
Chris Dearman7c9f67a2011-12-15 21:24:16 -0800754 reader.close();
Tsu Chiang Chuang9a223d72011-04-27 17:19:46 -0700755 String contents = new String(charContents);
756 if (contents.equals(content)) {
757 // System.out.println("skipping identical: "
758 // + file.getAbsolutePath());
759 return;
760 }
761 }
762
763 //System.out.println("writing file " + file.getAbsolutePath());
764
765 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
766 new FileOutputStream(file), "utf-8"));
767 bw.write(content);
768 bw.close();
769 } catch (Exception e) {
770 throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
771 ", msg:" + e.getMessage());
772 }
773 }
774
775 private File getFileFromPackage(String pname, String methodName)
776 throws IOException {
777 // e.g. dxc.junit.argsreturns.pargsreturn
778 String path = getFileName(pname, methodName, ".java");
779 String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
780 File dirPath = new File(absPath);
781 File parent = dirPath.getParentFile();
782 if (!parent.exists() && !parent.mkdirs()) {
783 throw new IOException("failed to create directory: " + absPath);
784 }
785 return dirPath;
786 }
787
788 private String getFileName(String pname, String methodName,
789 String extension) {
790 String path = pname.replaceAll("\\.", "/");
791 return new File(path, "Main_" + methodName + extension).getPath();
792 }
793}