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