Home | History | Annotate | Download | only in build
      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.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>/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.DeviceTestCase;\n" +
    215         "\n" +
    216         "public class " + sourceName + " extends DeviceTestCase {\n";
    217     }
    218 
    219     private String getShellExecJavaLine(String classpath, String mainclass) {
    220       String cmd = String.format("ANDROID_DATA=%s dalvikvm -Xint:portable -Xmx512M -Xss32K " +
    221               "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH,
    222               classpath, mainclass);
    223       return "String res = getDevice().executeShellCommand(\""+ cmd + "\");\n" +
    224              "// A sucessful adb shell command returns an empty string.\n" +
    225              "assertEquals(\"" + cmd + "\", \"\", res);";
    226     }
    227 
    228     private String getWarningMessage() {
    229         return "//Autogenerated code by " + this.getClass().getName() + "; do not edit.\n";
    230     }
    231 
    232     private void addCTSHostMethod(String pName, String method, MethodData md,
    233             Set<String> dependentTestClassNames) {
    234         curJunitFileData += "public void " + method + "() throws Exception {\n";
    235         final String targetCoreJarPath = String.format("%s/dot/junit/dexcore.jar",
    236                 TARGET_JAR_ROOT_PATH);
    237 
    238         // push class with Main jar.
    239         String mjar = "Main_" + method + ".jar";
    240         String pPath = pName.replaceAll("\\.","/");
    241         String mainJar = String.format("%s/%s/%s", TARGET_JAR_ROOT_PATH, pPath, mjar);
    242 
    243         String cp = String.format("%s:%s", targetCoreJarPath, mainJar);
    244         for (String depFqcn : dependentTestClassNames) {
    245             int lastDotPos = depFqcn.lastIndexOf('.');
    246             String sourceName = depFqcn.replaceAll("\\.", "/") + ".jar";
    247             String targetName= String.format("%s/%s", TARGET_JAR_ROOT_PATH,
    248                     sourceName);
    249             cp += ":" + targetName;
    250             // dot.junit.opcodes.invoke_interface_range.ITest
    251             // -> dot/junit/opcodes/invoke_interface_range/ITest.jar
    252         }
    253 
    254         //"dot.junit.opcodes.add_double_2addr.Main_testN2";
    255         String mainclass = pName + ".Main_" + method;
    256         curJunitFileData += "    " + getShellExecJavaLine(cp, mainclass);
    257         curJunitFileData += "}\n\n";
    258     }
    259 
    260     private void handleTests() throws IOException {
    261         System.out.println("collected " + testMethodsCnt + " test methods in " +
    262                 testClassCnt + " junit test classes");
    263         Set<BuildStep> targets = new TreeSet<BuildStep>();
    264 
    265         javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
    266 
    267 
    268         JavacBuildStep javacBuildStep = new JavacBuildStep(
    269                 CLASSES_OUTPUT_FOLDER, CLASS_PATH);
    270 
    271         for (Entry<String, List<String>> entry : map.entrySet()) {
    272 
    273             String fqcn = entry.getKey();
    274             int lastDotPos = fqcn.lastIndexOf('.');
    275             String pName = fqcn.substring(0, lastDotPos);
    276             String classOnlyName = fqcn.substring(lastDotPos + 1);
    277             String instPrefix = "new " + classOnlyName + "()";
    278 
    279             openCTSHostFileFor(pName, classOnlyName);
    280 
    281             List<String> methods = entry.getValue();
    282             Collections.sort(methods, new Comparator<String>() {
    283                 public int compare(String s1, String s2) {
    284                     // TODO sort according: test ... N, B, E, VFE
    285                     return s1.compareTo(s2);
    286                 }
    287             });
    288             for (String method : methods) {
    289                 // e.g. testN1
    290                 if (!method.startsWith("test")) {
    291                     throw new RuntimeException("no test method: " + method);
    292                 }
    293 
    294                 // generate the Main_xx java class
    295 
    296                 // a Main_testXXX.java contains:
    297                 // package <packagenamehere>;
    298                 // public class Main_testxxx {
    299                 // public static void main(String[] args) {
    300                 // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
    301                 // }
    302                 // }
    303                 MethodData md = parseTestMethod(pName, classOnlyName, method);
    304                 String methodContent = md.methodBody;
    305 
    306                 Set<String> dependentTestClassNames = parseTestClassName(pName,
    307                         classOnlyName, methodContent);
    308 
    309                 addCTSHostMethod(pName, method, md, dependentTestClassNames);
    310 
    311 
    312                 if (dependentTestClassNames.isEmpty()) {
    313                     continue;
    314                 }
    315 
    316 
    317                 String content = getWarningMessage() +
    318                 "package " + pName + ";\n" +
    319                 "import " + pName + ".d.*;\n" +
    320                 "import dot.junit.*;\n" +
    321                 "public class Main_" + method + " extends DxAbstractMain {\n" +
    322                 "    public static void main(String[] args) throws Exception {" +
    323                 methodContent + "\n}\n";
    324 
    325                 String fileName = getFileName(pName, method, ".java");
    326                 File sourceFile = getFileFromPackage(pName, method);
    327 
    328                 File classFile = new File(CLASSES_OUTPUT_FOLDER + "/" +
    329                         getFileName(pName, method, ".class"));
    330                 // if (sourceFile.lastModified() > classFile.lastModified()) {
    331                 writeToFile(sourceFile, content);
    332                 javacBuildStep.addSourceFile(sourceFile.getAbsolutePath());
    333 
    334                 BuildStep dexBuildStep = generateDexBuildStep(
    335                         CLASSES_OUTPUT_FOLDER, getFileName(pName, method, ""));
    336                 targets.add(dexBuildStep);
    337                 // }
    338 
    339                 generateBuildStepFor(pName, method, dependentTestClassNames,
    340                         targets);
    341             }
    342 
    343 
    344         }
    345 
    346         // write latest HOSTJUNIT generated file.
    347         flushHostJunitFile();
    348 
    349         if (!javacHostJunitBuildStep.build()) {
    350             System.out.println("main javac cts-host-hostjunit-classes build step failed");
    351             System.exit(1);
    352         }
    353 
    354         if (javacBuildStep.build()) {
    355             for (BuildStep buildStep : targets) {
    356                 if (!buildStep.build()) {
    357                     System.out.println("building failed. buildStep: " +
    358                             buildStep.getClass().getName() + ", " + buildStep);
    359                     System.exit(1);
    360                 }
    361             }
    362         } else {
    363             System.out.println("main javac dalvik-cts-buildutil build step failed");
    364             System.exit(1);
    365         }
    366     }
    367 
    368     private void generateBuildStepFor(String pName, String method,
    369             Set<String> dependentTestClassNames, Set<BuildStep> targets) {
    370 
    371 
    372         for (String dependentTestClassName : dependentTestClassNames) {
    373             generateBuildStepForDependant(dependentTestClassName, targets);
    374         }
    375     }
    376 
    377     private void generateBuildStepForDependant(String dependentTestClassName,
    378             Set<BuildStep> targets) {
    379 
    380         File sourceFolder = new File(JAVASRC_FOLDER);
    381         String fileName = dependentTestClassName.replace('.', '/').trim();
    382 
    383         if (new File(sourceFolder, fileName + ".dfh").exists()) {
    384 
    385             BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
    386                     JAVASRC_FOLDER, fileName + ".dfh");
    387             BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
    388                     OUTPUT_FOLDER, fileName + ".dex");
    389 
    390             DFHBuildStep buildStep = new DFHBuildStep(inputFile, dexFile);
    391 
    392             BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
    393                     OUTPUT_FOLDER, fileName + ".jar");
    394             JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
    395                     "classes.dex", jarFile, true);
    396             jarBuildStep.addChild(buildStep);
    397 
    398             targets.add(jarBuildStep);
    399             return;
    400         }
    401 
    402         if (new File(sourceFolder, fileName + ".d").exists()) {
    403 
    404             BuildStep.BuildFile inputFile = new BuildStep.BuildFile(
    405                     JAVASRC_FOLDER, fileName + ".d");
    406             BuildStep.BuildFile dexFile = new BuildStep.BuildFile(
    407                     OUTPUT_FOLDER, fileName + ".dex");
    408 
    409             DasmBuildStep buildStep = new DasmBuildStep(inputFile, dexFile);
    410 
    411             BuildStep.BuildFile jarFile = new BuildStep.BuildFile(
    412                     OUTPUT_FOLDER, fileName + ".jar");
    413 
    414             JarBuildStep jarBuildStep = new JarBuildStep(dexFile,
    415                     "classes.dex", jarFile, true);
    416             jarBuildStep.addChild(buildStep);
    417             targets.add(jarBuildStep);
    418             return;
    419         }
    420 
    421         if (new File(sourceFolder, fileName + ".java").exists()) {
    422 
    423             BuildStep dexBuildStep = generateDexBuildStep(
    424                     COMPILED_CLASSES_FOLDER, fileName);
    425             targets.add(dexBuildStep);
    426             return;
    427         }
    428 
    429         try {
    430             if (Class.forName(dependentTestClassName) != null) {
    431 
    432                 BuildStep dexBuildStep = generateDexBuildStep(
    433                         COMPILED_CLASSES_FOLDER, fileName);
    434                 targets.add(dexBuildStep);
    435                 return;
    436             }
    437         } catch (ClassNotFoundException e) {
    438             // do nothing
    439         }
    440 
    441         throw new RuntimeException("neither .dfh,.d,.java file of dependant test class found : " +
    442                 dependentTestClassName + ";" + fileName);
    443     }
    444 
    445     private BuildStep generateDexBuildStep(String classFileFolder,
    446             String classFileName) {
    447         BuildStep.BuildFile classFile = new BuildStep.BuildFile(
    448                 classFileFolder, classFileName + ".class");
    449 
    450         BuildStep.BuildFile tmpJarFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
    451                 classFileName + "_tmp.jar");
    452 
    453         JarBuildStep jarBuildStep = new JarBuildStep(classFile, classFileName +
    454                 ".class", tmpJarFile, false);
    455 
    456         BuildStep.BuildFile outputFile = new BuildStep.BuildFile(OUTPUT_FOLDER,
    457                 classFileName + ".jar");
    458 
    459         DexBuildStep dexBuildStep = new DexBuildStep(tmpJarFile, outputFile,
    460                 true);
    461 
    462         dexBuildStep.addChild(jarBuildStep);
    463         return dexBuildStep;
    464 
    465     }
    466 
    467     /**
    468      * @param pName
    469      * @param classOnlyName
    470      * @param methodSource
    471      * @return testclass names
    472      */
    473     private Set<String> parseTestClassName(String pName, String classOnlyName,
    474             String methodSource) {
    475         Set<String> entries = new HashSet<String>();
    476         String opcodeName = classOnlyName.substring(5);
    477 
    478         Scanner scanner = new Scanner(methodSource);
    479 
    480         String[] patterns = new String[] {"new\\s(T_" + opcodeName + "\\w*)",
    481                 "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
    482 
    483         String token = null;
    484         for (String pattern : patterns) {
    485             token = scanner.findWithinHorizon(pattern, methodSource.length());
    486             if (token != null) {
    487                 break;
    488             }
    489         }
    490 
    491         if (token == null) {
    492             System.err.println("warning: failed to find dependent test class name: " + pName +
    493                     ", " + classOnlyName + " in methodSource:\n" + methodSource);
    494             return entries;
    495         }
    496 
    497         MatchResult result = scanner.match();
    498 
    499         entries.add((pName + ".d." + result.group(1)).trim());
    500 
    501         // search additional @uses directives
    502         Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
    503         Matcher m = p.matcher(methodSource);
    504         while (m.find()) {
    505             String res = m.group(1);
    506             entries.add(res.trim());
    507         }
    508 
    509         // lines with the form @uses
    510         // dot.junit.opcodes.add_double.jm.T_add_double_2
    511         // one dependency per one @uses
    512         // TODO
    513 
    514         return entries;
    515     }
    516 
    517     private MethodData parseTestMethod(String pname, String classOnlyName,
    518             String method) {
    519 
    520         String path = pname.replaceAll("\\.", "/");
    521         String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName + ".java";
    522         File f = new File(absPath);
    523 
    524         Scanner scanner;
    525         try {
    526             scanner = new Scanner(f);
    527         } catch (FileNotFoundException e) {
    528             throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
    529                     ", msg:" + e.getMessage());
    530         }
    531 
    532         String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
    533 
    534         String token = scanner.findWithinHorizon(methodPattern, (int) f.length());
    535         if (token == null) {
    536             throw new RuntimeException("cannot find method source of 'public void " + method +
    537                     "' in file '" + absPath + "'");
    538         }
    539 
    540         MatchResult result = scanner.match();
    541         result.start();
    542         result.end();
    543 
    544         StringBuilder builder = new StringBuilder();
    545         //builder.append(token);
    546 
    547         try {
    548             FileReader reader = new FileReader(f);
    549             reader.skip(result.end());
    550 
    551             char currentChar;
    552             int blocks = 1;
    553             while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
    554                 switch (currentChar) {
    555                     case '}': {
    556                         blocks--;
    557                         builder.append(currentChar);
    558                         break;
    559                     }
    560                     case '{': {
    561                         blocks++;
    562                         builder.append(currentChar);
    563                         break;
    564                     }
    565                     default: {
    566                         builder.append(currentChar);
    567                         break;
    568                     }
    569                 }
    570             }
    571             if (reader != null) {
    572                 reader.close();
    573             }
    574         } catch (Exception e) {
    575             throw new RuntimeException("failed to parse", e);
    576         }
    577 
    578         // find the @title/@constraint in javadoc comment for this method
    579         Scanner scanner2;
    580         try {
    581             // using platform's default charset
    582             scanner2 = new Scanner(f);
    583         } catch (FileNotFoundException e) {
    584             throw new RuntimeException("error while reading to file: " + e.getClass().getName() +
    585                     ", msg:" + e.getMessage());
    586         }
    587         // using platform's default charset
    588         String all = new String(FileUtils.readFile(f));
    589         // System.out.println("grepping javadoc found for method " + method +
    590         // " in " + pname + "," + classOnlyName);
    591         String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
    592         Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
    593         Matcher m = p.matcher(all);
    594         String title = null, constraint = null;
    595         if (m.find()) {
    596             String res = m.group(1);
    597             // System.out.println("res: " + res);
    598             // now grep @title and @constraint
    599             Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
    600             .matcher(res);
    601             if (titleM.find()) {
    602                 title = titleM.group(1).replaceAll("\\n     \\*", "");
    603                 title = title.replaceAll("\\n", " ");
    604                 title = title.trim();
    605                 // System.out.println("title: " + title);
    606             } else {
    607                 System.err.println("warning: no @title found for method " + method + " in " + pname +
    608                         "," + classOnlyName);
    609             }
    610             // constraint can be one line only
    611             Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
    612                     res);
    613             if (constraintM.find()) {
    614                 constraint = constraintM.group(1);
    615                 constraint = constraint.trim();
    616                 // System.out.println("constraint: " + constraint);
    617             } else if (method.contains("VFE")) {
    618                 System.err
    619                 .println("warning: no @constraint for for a VFE method:" + method + " in " +
    620                         pname + "," + classOnlyName);
    621             }
    622         } else {
    623             System.err.println("warning: no javadoc found for method " + method + " in " + pname +
    624                     "," + classOnlyName);
    625         }
    626         MethodData md = new MethodData();
    627         md.methodBody = builder.toString();
    628         md.constraint = constraint;
    629         md.title = title;
    630         if (scanner != null) {
    631             scanner.close();
    632         }
    633         if (scanner2 != null) {
    634             scanner2.close();
    635         }
    636         return md;
    637     }
    638 
    639     private void writeToFileMkdir(File file, String content) {
    640         File parent = file.getParentFile();
    641         if (!parent.exists() && !parent.mkdirs()) {
    642             throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
    643         }
    644         writeToFile(file, content);
    645     }
    646 
    647     private void writeToFile(File file, String content) {
    648         try {
    649             if (file.length() == content.length()) {
    650                 FileReader reader = new FileReader(file);
    651                 char[] charContents = new char[(int) file.length()];
    652                 reader.read(charContents);
    653                 reader.close();
    654                 String contents = new String(charContents);
    655                 if (contents.equals(content)) {
    656                     // System.out.println("skipping identical: "
    657                     // + file.getAbsolutePath());
    658                     return;
    659                 }
    660             }
    661 
    662             //System.out.println("writing file " + file.getAbsolutePath());
    663 
    664             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
    665                     new FileOutputStream(file), "utf-8"));
    666             bw.write(content);
    667             bw.close();
    668         } catch (Exception e) {
    669             throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
    670                     ", msg:" + e.getMessage());
    671         }
    672     }
    673 
    674     private File getFileFromPackage(String pname, String methodName)
    675     throws IOException {
    676         // e.g. dxc.junit.argsreturns.pargsreturn
    677         String path = getFileName(pname, methodName, ".java");
    678         String absPath = MAIN_SRC_OUTPUT_FOLDER + "/" + path;
    679         File dirPath = new File(absPath);
    680         File parent = dirPath.getParentFile();
    681         if (!parent.exists() && !parent.mkdirs()) {
    682             throw new IOException("failed to create directory: " + absPath);
    683         }
    684         return dirPath;
    685     }
    686 
    687     private String getFileName(String pname, String methodName,
    688             String extension) {
    689         String path = pname.replaceAll("\\.", "/");
    690         return new File(path, "Main_" + methodName + extension).getPath();
    691     }
    692 }
    693