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