Home | History | Annotate | Download | only in build
      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