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 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 153 // class in the respective package. 154 // for the report make sure all N... tests are called first, then B, 155 // then E, then VFE test methods. 156 // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() -> 157 // File Main_testN1.java in package dxc.junit.opcodes.aaload. 158 // 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 } 184 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 203 private void openCTSHostFileFor(String pName, String classOnlyName) { 204 // flush previous JunitFile 205 flushHostJunitFile(); 206 String sourceName = "JUnit_" + classOnlyName; 207 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" + 214 "import com.android.tradefed.testtype.IAbi;\n" + 215 "import com.android.tradefed.testtype.IAbiReceiver;\n" + 216 "import com.android.tradefed.testtype.DeviceTestCase;\n" + 217 "import com.android.tradefed.util.AbiFormatter;\n" + 218 "\n" + 219 "public class " + sourceName + " extends DeviceTestCase implements IAbiReceiver {\n"; 220 } 221 222 private String getShellExecJavaLine(String classpath, String mainclass) { 223 String cmd = String.format("ANDROID_DATA=%s dalvikvm|#ABI#| -Xmx512M -Xss32K " + 224 "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH, 225 classpath, mainclass); 226 return " String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mAbi.getBitness());\n" + 227 " String res = getDevice().executeShellCommand(cmd);\n" + 228 " // A sucessful adb shell command returns an empty string.\n" + 229 " assertEquals(cmd, \"\", res);"; 230 } 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 247 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; 260 curJunitFileData += getShellExecJavaLine(cp, mainclass); 261 curJunitFileData += "\n}\n\n"; 262 } 263 264 private void handleTests() throws IOException { 265 System.out.println("collected " + testMethodsCnt + " test methods in " + 266 testClassCnt + " junit test classes"); 267 String datafileContent = ""; 268 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 286 curJunitFileData += "\n" + 287 "protected IAbi mAbi;\n" + 288 "@Override\n" + 289 "public void setAbi(IAbi abi) {\n" + 290 " mAbi = abi;\n" + 291 "}\n\n"; 292 293 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 351 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"; 431 generateBuildStepFor(pName, method, dependentTestClassNames, 432 targets); 433 } 434 435 436 } 437 438 // write latest HOSTJUNIT generated file. 439 flushHostJunitFile(); 440 441 File scriptDataDir = new File(OUTPUT_FOLDER + "/data/"); 442 scriptDataDir.mkdirs(); 443 writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent); 444 445 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 605 // 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 621 // 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 } 643 644 String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{"; 645 646 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 691 // 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 } 737 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); 754 reader.close(); 755 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 } 794