1 /* 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 17 package util.build; 18 19 import com.android.dex.util.FileUtils; 20 21 import dot.junit.AllTests; 22 23 import junit.framework.TestCase; 24 import junit.framework.TestResult; 25 import junit.framework.TestSuite; 26 import junit.textui.TestRunner; 27 28 import java.io.BufferedWriter; 29 import java.io.File; 30 import java.io.FileNotFoundException; 31 import java.io.FileOutputStream; 32 import java.io.FileReader; 33 import java.io.IOException; 34 import java.io.OutputStreamWriter; 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.Comparator; 38 import java.util.HashSet; 39 import java.util.Iterator; 40 import java.util.LinkedHashMap; 41 import java.util.List; 42 import java.util.Scanner; 43 import java.util.Set; 44 import java.util.TreeSet; 45 import java.util.Map.Entry; 46 import java.util.regex.MatchResult; 47 import java.util.regex.Matcher; 48 import 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> 54 * <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 */ 57 public 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 75 private static String restrictTo = null; // e.g. restrict to "opcodes.add_double" 76 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 private boolean useJack; 82 83 /* 84 * using a linked hashmap to keep the insertion order for iterators. 85 * the junit suite/tests adding order is used to generate the order of the 86 * report. 87 * a map. key: fully qualified class name, value: a list of test methods for 88 * the given class 89 */ 90 private LinkedHashMap<String, List<String>> map = new LinkedHashMap<String, 91 List<String>>(); 92 93 private class MethodData { 94 String methodBody, constraint, title; 95 } 96 97 /** 98 * @param args 99 * args 0 must be the project root folder (where src, lib etc. 100 * resides) 101 * @throws IOException 102 */ main(String[] args)103 public static void main(String[] args) throws IOException { 104 105 parseArgs(args); 106 107 long start = System.currentTimeMillis(); 108 BuildDalvikSuite cat = new BuildDalvikSuite(false); 109 cat.compose(); 110 long end = System.currentTimeMillis(); 111 112 System.out.println("elapsed seconds: " + (end - start) / 1000); 113 } 114 parseArgs(String[] args)115 public static void parseArgs(String[] args) { 116 if (args.length > 5) { 117 JAVASRC_FOLDER = args[0]; 118 OUTPUT_FOLDER = args[1]; 119 CLASS_PATH = args[2]; 120 MAIN_SRC_OUTPUT_FOLDER = args[3]; 121 CLASSES_OUTPUT_FOLDER = MAIN_SRC_OUTPUT_FOLDER + "/classes"; 122 123 COMPILED_CLASSES_FOLDER = args[4]; 124 125 HOSTJUNIT_SRC_OUTPUT_FOLDER = args[5]; 126 HOSTJUNIT_CLASSES_OUTPUT_FOLDER = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/classes"; 127 128 if (args.length > 6) { 129 // optional: restrict to e.g. "opcodes.add_double" 130 restrictTo = args[6]; 131 System.out.println("restricting build to: " + restrictTo); 132 } 133 134 } else { 135 System.out.println("usage: java-src-folder output-folder classpath " + 136 "generated-main-files compiled_output generated-main-files " + 137 "[restrict-to-opcode]"); 138 System.exit(-1); 139 } 140 } 141 BuildDalvikSuite(boolean useJack)142 public BuildDalvikSuite(boolean useJack) { 143 this.useJack = useJack; 144 } 145 compose()146 public void compose() throws IOException { 147 System.out.println("Collecting all junit tests..."); 148 new TestRunner() { 149 @Override 150 protected TestResult createTestResult() { 151 return new TestResult() { 152 @Override 153 protected void run(TestCase test) { 154 addToTests(test); 155 } 156 157 }; 158 } 159 }.doRun(AllTests.suite()); 160 161 // for each combination of TestClass and method, generate a Main_testN1 162 // class in the respective package. 163 // for the report make sure all N... tests are called first, then B, 164 // then E, then VFE test methods. 165 // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() -> 166 // File Main_testN1.java in package dxc.junit.opcodes.aaload. 167 // 168 handleTests(); 169 } 170 171 private void addToTests(TestCase test) { 172 173 String packageName = test.getClass().getPackage().getName(); 174 packageName = packageName.substring(packageName.lastIndexOf('.')); 175 176 177 String method = test.getName(); // e.g. testVFE2 178 String fqcn = test.getClass().getName(); // e.g. 179 // dxc.junit.opcodes.iload_3.Test_iload_3 180 181 // ignore all tests not belonging to the given restriction 182 if (restrictTo != null && !fqcn.contains(restrictTo)) return; 183 184 testMethodsCnt++; 185 List<String> li = map.get(fqcn); 186 if (li == null) { 187 testClassCnt++; 188 li = new ArrayList<String>(); 189 map.put(fqcn, li); 190 } 191 li.add(method); 192 } 193 private String curJunitFileName = null; 194 private String curJunitName = null; 195 private String curJunitFileData = ""; 196 197 private SourceBuildStep hostJunitBuildStep; 198 199 private void flushHostJunitFile() { 200 if (curJunitFileName != null) { 201 File toWrite = new File(curJunitFileName); 202 String absPath = toWrite.getAbsolutePath(); 203 // add to java source files for later compilation 204 hostJunitBuildStep.addSourceFile(absPath); 205 // write file 206 curJunitFileData += "\n}\n"; 207 writeToFileMkdir(toWrite, curJunitFileData); 208 209 curJunitFileName = null; 210 curJunitFileData = ""; 211 } 212 } 213 214 private void openCTSHostFileFor(String pName, String classOnlyName) { 215 // flush previous JunitFile 216 flushHostJunitFile(); 217 String sourceName = "JUnit_" + classOnlyName; 218 219 // prepare current testcase-file 220 curJunitFileName = HOSTJUNIT_SRC_OUTPUT_FOLDER + "/" + pName.replaceAll("\\.","/") + "/" + 221 sourceName + ".java"; 222 curJunitFileData = getWarningMessage() + 223 "package " + pName + ";\n" + 224 "import java.io.IOException;\n" + 225 "import java.util.concurrent.TimeUnit;\n\n" + 226 "import com.android.tradefed.device.CollectingOutputReceiver;\n" + 227 "import com.android.tradefed.testtype.IAbi;\n" + 228 "import com.android.tradefed.testtype.IAbiReceiver;\n" + 229 "import com.android.tradefed.testtype.DeviceTestCase;\n" + 230 "import com.android.tradefed.util.AbiFormatter;\n" + 231 "\n" + 232 "public class " + sourceName + " extends DeviceTestCase implements IAbiReceiver {\n"; 233 } 234 235 private String getShellExecJavaLine(String classpath, String mainclass) { 236 String cmd = String.format("ANDROID_DATA=%s dalvikvm|#ABI#| -Xmx512M -Xss32K " + 237 "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH, 238 classpath, mainclass); 239 StringBuilder code = new StringBuilder(); 240 code.append(" String cmd = AbiFormatter.formatCmdForAbi(\"") 241 .append(cmd) 242 .append("\", mAbi.getBitness());\n") 243 .append(" CollectingOutputReceiver receiver = new CollectingOutputReceiver();\n") 244 .append(" getDevice().executeShellCommand(cmd, receiver, 6, TimeUnit.MINUTES, 1);\n") 245 .append(" // A sucessful adb shell command returns an empty string.\n") 246 .append(" assertEquals(cmd, \"\", receiver.getOutput());"); 247 return code.toString(); 248 } 249 250 private String getWarningMessage() { 251 return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n"; 252 } 253 254 private void addCTSHostMethod(String pName, String method, MethodData md, 255 Set<String> dependentTestClassNames) { 256 curJunitFileData += "public void " + method + "() throws Exception {\n"; 257 final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar", 258 TARGET_JAR_ROOT_PATH); 259 260 // push class with Main jar. 261 String mjar = "Main_" + method + ".jar"; 262 String pPath = pName.replaceAll("\\.","/"); 263 String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar); 264 265 String cp = String.format("%s:%s", targetCoreJarPath, mainJar); 266 for (String depFqcn : dependentTestClassNames) { 267 int lastDotPos = depFqcn.lastIndexOf('.'); 268 String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar"; 269 String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH, 270 sourceName); 271 cp += ":" + targetName; 272 // dot.junit.opcodes.invoke_interface_range.ITest 273 // -> dot/junit/opcodes/invoke_interface_range/ITest.jar 274 } 275 276 //"dot.junit.opcodes.add_double_2addr.Main_testN2"; 277 String mainclass = pName + ".Main_" + method; 278 curJunitFileData += getShellExecJavaLine(cp, mainclass); 279 curJunitFileData += "\n}\n\n"; 280 } 281 282 private void handleTests() throws IOException { 283 System.out.println("collected " + testMethodsCnt + " test methods in " + 284 testClassCnt + " junit test classes"); 285 String datafileContent = ""; 286 Set<BuildStep> targets = new TreeSet<BuildStep>(); 287 288 SourceBuildStep srcBuildStep; 289 hostJunitBuildStep = new JavacBuildStep( 290 HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH); 291 292 srcBuildStep = new JavacBuildStep(CLASSES_OUTPUT_FOLDER, CLASS_PATH); 293 294 for (Entry<String, List<String>> entry : map.entrySet()) { 295 296 String fqcn = entry.getKey(); 297 int lastDotPos = fqcn.lastIndexOf('.'); 298 String pName = fqcn.substring(0, lastDotPos); 299 String classOnlyName = fqcn.substring(lastDotPos + 1); 300 String instPrefix = "new " + classOnlyName + "()"; 301 302 openCTSHostFileFor(pName, classOnlyName); 303 304 curJunitFileData += "\n" + 305 "protected IAbi mAbi;\n" + 306 "@Override\n" + 307 "public void setAbi(IAbi abi) {\n" + 308 " mAbi = abi;\n" + 309 "}\n\n"; 310 311 List<String> methods = entry.getValue(); 312 Collections.sort(methods, new Comparator<String>() { 313 public int compare(String s1, String s2) { 314 // TODO sort according: test ... N, B, E, VFE 315 return s1.compareTo(s2); 316 } 317 }); 318 for (String method : methods) { 319 // e.g. testN1 320 if (!method.startsWith("test")) { 321 throw new RuntimeException("no test method: " + method); 322 } 323 324 // generate the Main_xx java class 325 326 // a Main_testXXX.java contains: 327 // package <packagenamehere>; 328 // public class Main_testxxx { 329 // public static void main(String[] args) { 330 // new dxc.junit.opcodes.aaload.Test_aaload().testN1(); 331 // } 332 // } 333 MethodData md = parseTestMethod(pName, classOnlyName, method); 334 String methodContent = md.methodBody; 335 336 Set<String> dependentTestClassNames = parseTestClassName(pName, 337 classOnlyName, methodContent); 338 339 addCTSHostMethod(pName, method, md, dependentTestClassNames); 340 341 342 if (dependentTestClassNames.isEmpty()) { 343 continue; 344 } 345 346 347 String content = getWarningMessage() + 348 "package " + pName + ";\n" + 349 "import " + pName + ".d.*;\n" + 350 "import dot.junit.*;\n" + 351 "public class Main_" + method + " extends DxAbstractMain {\n" + 352 " public static void main(String[] args) throws Exception {" + 353 methodContent + "\n}\n"; 354 355 File sourceFile = getFileFromPackage(pName, method); 356 357 writeToFile(sourceFile, content); 358 if (useJack) { 359 File jackFile = new File(CLASSES_OUTPUT_FOLDER + "/" + 360 getFileName(pName, method, ".jack")); 361 JackBuildStep step = new JackBuildStep(jackFile.getAbsolutePath(), CLASS_PATH); 362 step.addSourceFile(sourceFile.getAbsolutePath()); 363 if (!step.build()) { 364 System.out.println("main src dalvik-cts-buildutil build step failed"); 365 System.exit(1); 366 } 367 } else { 368 srcBuildStep.addSourceFile(sourceFile.getAbsolutePath()); 369 } 370 371 BuildStep dexBuildStep = generateDexBuildStep( 372 CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""), null); 373 targets.add(dexBuildStep); 374 375 376 // prepare the entry in the data file for the bash script. 377 // e.g. 378 // main class to execute; opcode/constraint; test purpose 379 // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test 380 // (#1) 381 382 char ca = method.charAt("test".length()); // either N,B,E, 383 // or V (VFE) 384 String comment; 385 switch (ca) { 386 case 'N': 387 comment = "Normal #" + method.substring(5); 388 break; 389 case 'B': 390 comment = "Boundary #" + method.substring(5); 391 break; 392 case 'E': 393 comment = "Exception #" + method.substring(5); 394 break; 395 case 'V': 396 comment = "Verifier #" + method.substring(7); 397 break; 398 default: 399 throw new RuntimeException("unknown test abbreviation:" 400 + method + " for " + fqcn); 401 } 402 403 String line = pName + ".Main_" + method + ";"; 404 for (String className : dependentTestClassNames) { 405 line += className + " "; 406 } 407 408 409 // test description 410 String[] pparts = pName.split("\\."); 411 // detail e.g. add_double 412 String detail = pparts[pparts.length-1]; 413 // type := opcode | verify 414 String type = pparts[pparts.length-2]; 415 416 String description; 417 if ("format".equals(type)) { 418 description = "format"; 419 } else if ("opcodes".equals(type)) { 420 // Beautify name, so it matches the actual mnemonic 421 detail = detail.replaceAll("_", "-"); 422 detail = detail.replace("-from16", "/from16"); 423 detail = detail.replace("-high16", "/high16"); 424 detail = detail.replace("-lit8", "/lit8"); 425 detail = detail.replace("-lit16", "/lit16"); 426 detail = detail.replace("-4", "/4"); 427 detail = detail.replace("-16", "/16"); 428 detail = detail.replace("-32", "/32"); 429 detail = detail.replace("-jumbo", "/jumbo"); 430 detail = detail.replace("-range", "/range"); 431 detail = detail.replace("-2addr", "/2addr"); 432 433 // Unescape reserved words 434 detail = detail.replace("opc-", ""); 435 436 description = detail; 437 } else if ("verify".equals(type)) { 438 description = "verifier"; 439 } else { 440 description = type + " " + detail; 441 } 442 443 String details = (md.title != null ? md.title : ""); 444 if (md.constraint != null) { 445 details = " Constraint " + md.constraint + ", " + details; 446 } 447 if (details.length() != 0) { 448 details = details.substring(0, 1).toUpperCase() 449 + details.substring(1); 450 } 451 452 line += ";" + description + ";" + comment + ";" + details; 453 454 datafileContent += line + "\n"; 455 generateBuildStepFor(pName, method, dependentTestClassNames, 456 targets); 457 } 458 459 460 } 461 462 // write latest HOSTJUNIT generated file. 463 flushHostJunitFile(); 464 465 File scriptDataDir = new File(OUTPUT_FOLDER + "/data/"); 466 scriptDataDir.mkdirs(); 467 writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent); 468 469 if (!hostJunitBuildStep.build()) { 470 System.out.println("main javac cts-host-hostjunit-classes build step failed"); 471 System.exit(1); 472 } 473 474 if (!useJack) { 475 if (!srcBuildStep.build()) { 476 System.out.println("main src dalvik-cts-buildutil build step failed"); 477 System.exit(1); 478 } 479 } 480 for (BuildStep buildStep : targets) { 481 if (!buildStep.build()) { 482 System.out.println("building failed. buildStep: " + 483 buildStep.getClass().getName() + ", " + buildStep); 484 System.exit(1); 485 } 486 } 487 } 488 489 private void generateBuildStepFor(String pName, String method, 490 Set<String> dependentTestClassNames, Set<BuildStep> targets) { 491 492 493 for (String dependentTestClassName : dependentTestClassNames) { 494 generateBuildStepForDependant(dependentTestClassName, targets); 495 } 496 } 497 498 private void generateBuildStepForDependant(String dependentTestClassName, 499 Set<BuildStep> targets) { 500 501 File sourceFolder = new File(JAVASRC_FOLDER); 502 String fileName = dependentTestClassName.replace('.', '/').trim(); 503 504 if (new File(sourceFolder, fileName + ".dfh").exists()) { 505 506 BuildStep.BuildFile inputFile = new BuildStep.BuildFile( 507 JAVASRC_FOLDER, fileName + ".dfh"); 508 BuildStep.BuildFile dexFile = new BuildStep.BuildFile( 509 OUTPUT_FOLDER, fileName + ".dex"); 510 511 DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile); 512 513 BuildStep.BuildFile jarFile = new BuildStep.BuildFile( 514 OUTPUT_FOLDER, fileName + ".jar"); 515 JarBuildStep jarBuildStep = new JarBuildStep(dexFile, 516 "classes.dex", jarFile, true); 517 jarBuildStep.addChild(buildStep); 518 519 targets.add(jarBuildStep); 520 return; 521 } 522 523 if (new File(sourceFolder, fileName + ".d").exists()) { 524 525 BuildStep.BuildFile inputFile = new BuildStep.BuildFile( 526 JAVASRC_FOLDER, fileName + ".d"); 527 BuildStep.BuildFile dexFile = new BuildStep.BuildFile( 528 OUTPUT_FOLDER, fileName + ".dex"); 529 530 DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile); 531 532 BuildStep.BuildFile jarFile = new BuildStep.BuildFile( 533 OUTPUT_FOLDER, fileName + ".jar"); 534 535 JarBuildStep jarBuildStep = new JarBuildStep(dexFile, 536 "classes.dex", jarFile, true); 537 jarBuildStep.addChild(buildStep); 538 targets.add(jarBuildStep); 539 return; 540 } 541 542 File srcFile = new File(sourceFolder, fileName + ".java"); 543 if (srcFile.exists()) { 544 JackBuildStep jackBuildStep = null; 545 if (useJack) { 546 jackBuildStep = new JackBuildStep( 547 COMPILED_CLASSES_FOLDER + File.separator + fileName + ".jack", 548 CLASS_PATH); 549 jackBuildStep.addSourceFile(srcFile.getAbsolutePath()); 550 } 551 BuildStep dexBuildStep = generateDexBuildStep( 552 COMPILED_CLASSES_FOLDER, fileName, jackBuildStep); 553 targets.add(dexBuildStep); 554 return; 555 } 556 557 try { 558 if (Class.forName(dependentTestClassName) != null) { 559 JillBuildStep jillBuildStep = null; 560 if (useJack) { 561 BuildStep.BuildFile classFile = new BuildStep.BuildFile( 562 COMPILED_CLASSES_FOLDER, fileName + ".class"); 563 564 BuildStep.BuildFile jackFile = new BuildStep.BuildFile( 565 COMPILED_CLASSES_FOLDER, 566 fileName + ".jack"); 567 568 jillBuildStep = new JillBuildStep(classFile, 569 jackFile); 570 } 571 BuildStep dexBuildStep = generateDexBuildStep( 572 COMPILED_CLASSES_FOLDER, fileName, jillBuildStep); 573 targets.add(dexBuildStep); 574 return; 575 } 576 } catch (ClassNotFoundException e) { 577 // do nothing 578 } 579 580 throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " + 581 dependentTestClassName + ";" + fileName); 582 } 583 584 private BuildStep generateDexBuildStep(String classFileFolder, 585 String classFileName, BuildStep dependency) { 586 if (!useJack) { 587 BuildStep.BuildFile classFile = new BuildStep.BuildFile( 588 classFileFolder, classFileName + ".class"); 589 590 BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile( 591 OUTPUT_FOLDER, 592 classFileName + "_tmp.jar"); 593 594 JarBuildStep jarBuildStep = new JarBuildStep(classFile, 595 classFileName + ".class", tmpJarFile, false); 596 597 if (dependency != null) { 598 jarBuildStep.addChild(dependency); 599 } 600 601 BuildStep.BuildFile outputFile = new BuildStep.BuildFile( 602 OUTPUT_FOLDER, 603 classFileName + ".jar"); 604 605 DxBuildStep dexBuildStep = new DxBuildStep(tmpJarFile, 606 outputFile, 607 true); 608 609 dexBuildStep.addChild(jarBuildStep); 610 return dexBuildStep; 611 } else { 612 BuildStep.BuildFile jackFile = new BuildStep.BuildFile( 613 classFileFolder, classFileName + ".jack"); 614 615 BuildStep.BuildFile outputFile = new BuildStep.BuildFile( 616 OUTPUT_FOLDER, 617 classFileName + ".jar"); 618 619 JackDexBuildStep dexBuildStep = new JackDexBuildStep(jackFile, 620 outputFile, 621 true); 622 623 if (dependency != null) { 624 dexBuildStep.addChild(dependency); 625 } 626 return dexBuildStep; 627 628 } 629 630 } 631 632 /** 633 * @param pName 634 * @param classOnlyName 635 * @param methodSource 636 * @return testclass names 637 */ 638 private Set<String> parseTestClassName(String pName, String classOnlyName, 639 String methodSource) { 640 Set<String> entries = new HashSet<String>(); 641 String opcodeName = classOnlyName.substring(5); 642 643 Scanner scanner = new Scanner(methodSource); 644 645 String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)", 646 "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"}; 647 648 String token = null; 649 for (String pattern : patterns) { 650 token = scanner.findWithinHorizon(pattern, methodSource.length()); 651 if (token != null) { 652 break; 653 } 654 } 655 656 if (token == null) { 657 System.err.println("warning: failed to find dependent test class name: " + pName + 658 ", " + classOnlyName + " in methodSource:\n" + methodSource); 659 return entries; 660 } 661 662 MatchResult result = scanner.match(); 663 664 entries.add((pName + ".d." + result.group(1)).trim()); 665 666 // search additional @uses directives 667 Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE); 668 Matcher m = p.matcher(methodSource); 669 while (m.find()) { 670 String res = m.group(1); 671 entries.add(res.trim()); 672 } 673 674 // search for " load(\"...\" " and add as dependency 675 Pattern loadPattern = Pattern.compile("load\\(\"([^\"]*)\"", Pattern.MULTILINE); 676 Matcher loadMatcher = loadPattern.matcher(methodSource); 677 while (loadMatcher.find()) { 678 String res = loadMatcher.group(1); 679 entries.add(res.trim()); 680 } 681 682 // search for " loadAndRun(\"...\" " and add as dependency 683 Pattern loadAndRunPattern = Pattern.compile("loadAndRun\\(\"([^\"]*)\"", Pattern.MULTILINE); 684 Matcher loadAndRunMatcher = loadAndRunPattern.matcher(methodSource); 685 while (loadAndRunMatcher.find()) { 686 String res = loadAndRunMatcher.group(1); 687 entries.add(res.trim()); 688 } 689 690 // lines with the form @uses 691 // dot.junit.opcodes.add_double.jm.T_add_double_2 692 // one dependency per one @uses 693 // TODO 694 695 return entries; 696 } 697 698 private MethodData parseTestMethod(String pname, String classOnlyName, 699 String method) { 700 701 String path = pname.replaceAll("\\.", "/"); 702 String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java"; 703 File f = new File(absPath); 704 705 Scanner scanner; 706 try { 707 scanner = new Scanner(f); 708 } catch (FileNotFoundException e) { 709 throw new RuntimeException("error while reading to file: " + e.getClass().getName() + 710 ", msg:" + e.getMessage()); 711 } 712 713 String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{"; 714 715 String token = scanner.findWithinHorizon(methodPattern, (int) f.length()); 716 if (token == null) { 717 throw new RuntimeException("cannot find method source of 'public void " + method + 718 "' in file '" + absPath + "'"); 719 } 720 721 MatchResult result = scanner.match(); 722 result.start(); 723 result.end(); 724 725 StringBuilder builder = new StringBuilder(); 726 //builder.append(token); 727 728 try { 729 FileReader reader = new FileReader(f); 730 reader.skip(result.end()); 731 732 char currentChar; 733 int blocks = 1; 734 while ((currentChar = (char) reader.read()) != -1 && blocks > 0) { 735 switch (currentChar) { 736 case '}': { 737 blocks--; 738 builder.append(currentChar); 739 break; 740 } 741 case '{': { 742 blocks++; 743 builder.append(currentChar); 744 break; 745 } 746 default: { 747 builder.append(currentChar); 748 break; 749 } 750 } 751 } 752 if (reader != null) { 753 reader.close(); 754 } 755 } catch (Exception e) { 756 throw new RuntimeException("failed to parse", e); 757 } 758 759 // find the @title/@constraint in javadoc comment for this method 760 // using platform's default charset 761 String all = new String(FileUtils.readFile(f)); 762 // System.out.println("grepping javadoc found for method " + method + 763 // " in " + pname + "," + classOnlyName); 764 String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern; 765 Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL); 766 Matcher m = p.matcher(all); 767 String title = null, constraint = null; 768 if (m.find()) { 769 String res = m.group(1); 770 // System.out.println("res: " + res); 771 // now grep @title and @constraint 772 Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL) 773 .matcher(res); 774 if (titleM.find()) { 775 title = titleM.group(1).replaceAll("\\n \\*", ""); 776 title = title.replaceAll("\\n", " "); 777 title = title.trim(); 778 // System.out.println("title: " + title); 779 } else { 780 System.err.println("warning: no @title found for method " + method + " in " + pname + 781 "," + classOnlyName); 782 } 783 // constraint can be one line only 784 Matcher constraintM = Pattern.compile("@constraint (.*)").matcher( 785 res); 786 if (constraintM.find()) { 787 constraint = constraintM.group(1); 788 constraint = constraint.trim(); 789 // System.out.println("constraint: " + constraint); 790 } else if (method.contains("VFE")) { 791 System.err 792 .println("warning: no @constraint for for a VFE method:" + method + " in " + 793 pname + "," + classOnlyName); 794 } 795 } else { 796 System.err.println("warning: no javadoc found for method " + method + " in " + pname + 797 "," + classOnlyName); 798 } 799 MethodData md = new MethodData(); 800 md.methodBody = builder.toString(); 801 md.constraint = constraint; 802 md.title = title; 803 if (scanner != null) { 804 scanner.close(); 805 } 806 return md; 807 } 808 809 private void writeToFileMkdir(File file, String content) { 810 File parent = file.getParentFile(); 811 if (!parent.exists() && !parent.mkdirs()) { 812 throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath()); 813 } 814 writeToFile(file, content); 815 } 816 817 private void writeToFile(File file, String content) { 818 try { 819 if (file.exists() && file.length() == content.length()) { 820 FileReader reader = new FileReader(file); 821 char[] charContents = new char[(int) file.length()]; 822 reader.read(charContents); 823 reader.close(); 824 String contents = new String(charContents); 825 if (contents.equals(content)) { 826 // System.out.println("skipping identical: " 827 // + file.getAbsolutePath()); 828 return; 829 } 830 } 831 832 //System.out.println("writing file " + file.getAbsolutePath()); 833 834 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 835 new FileOutputStream(file), "utf-8")); 836 bw.write(content); 837 bw.close(); 838 } catch (Exception e) { 839 throw new RuntimeException("error while writing to file: " + e.getClass().getName() + 840 ", msg:" + e.getMessage()); 841 } 842 } 843 844 private File getFileFromPackage(String pname, String methodName) 845 throws IOException { 846 // e.g. dxc.junit.argsreturns.pargsreturn 847 String path = getFileName(pname, methodName, ".java"); 848 String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path; 849 File dirPath = new File(absPath); 850 File parent = dirPath.getParentFile(); 851 if (!parent.exists() && !parent.mkdirs()) { 852 throw new IOException("failed to create directory: " + absPath); 853 } 854 return dirPath; 855 } 856 857 private String getFileName(String pname, String methodName, 858 String extension) { 859 String path = pname.replaceAll("\\.", "/"); 860 return new File(path, "Main_" + methodName + extension).getPath(); 861 } 862 } 863