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