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 java.io.BufferedWriter;
     20 import java.io.File;
     21 import java.io.FileOutputStream;
     22 import java.io.FileReader;
     23 import java.io.IOException;
     24 import java.io.OutputStreamWriter;
     25 import java.io.StringReader;
     26 import java.net.URL;
     27 import java.nio.charset.StandardCharsets;
     28 import java.util.ArrayList;
     29 import java.util.List;
     30 import java.util.Map.Entry;
     31 import java.util.Scanner;
     32 import java.util.regex.MatchResult;
     33 import java.util.regex.Matcher;
     34 import java.util.regex.Pattern;
     35 
     36 /**
     37  * Helper base class for code generators.
     38  */
     39 public abstract class BuildUtilBase {
     40 
     41     public static boolean DEBUG = true;
     42 
     43     public static class MethodData {
     44         String methodBody, constraint, title;
     45     }
     46 
     47     public interface TestHandler {
     48         public void handleTest(String fqcn, List<String> methods);
     49     }
     50 
     51     public void run(TestHandler handler) {
     52         System.out.println("Collecting all junit tests...");
     53         JUnitTestCollector tests = new JUnitTestCollector(getClass().getClassLoader());
     54 
     55         handleTests(tests, handler);
     56     }
     57 
     58     protected void handleTests(JUnitTestCollector tests, TestHandler handler) {
     59         System.out.println("collected " + tests.testMethodsCnt + " test methods in " +
     60                 tests.testClassCnt + " junit test classes");
     61 
     62         for (Entry<String, List<String>> entry : tests.map.entrySet()) {
     63             handler.handleTest(entry.getKey(), entry.getValue());
     64         }
     65     }
     66 
     67     private static String readURL(URL in) {
     68         // Use common scanner idiom to read a complete InputStream into a string.
     69         try (Scanner scanner = new Scanner(in.openStream(), StandardCharsets.UTF_8.toString())) {
     70             scanner.useDelimiter("\\A");  // This delimits by "start of content," of which there is
     71                                           // only one.
     72             return scanner.hasNext() ? scanner.next() : null;
     73         } catch (IOException e) {
     74             throw new RuntimeException(e);
     75         }
     76     }
     77 
     78     protected MethodData parseTestMethod(String pname, String classOnlyName,
     79             String method) {
     80         String searchPath = "src/" + pname.replaceAll("\\.", "/") + "/" + classOnlyName + ".java";
     81         String content;
     82         {
     83             URL resource = getClass().getClassLoader().getResource(searchPath);
     84             if (resource == null) {
     85                 throw new RuntimeException("Could not find " + searchPath);
     86             }
     87             content = readURL(resource);
     88             if (content == null) {
     89                 throw new RuntimeException("Could not retrieve content for " + searchPath);
     90             }
     91         }
     92 
     93         final String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
     94 
     95         int methodSkip;
     96         try (Scanner scanner = new Scanner(content)) {
     97             String token = scanner.findWithinHorizon(methodPattern, content.length());
     98             if (token == null) {
     99                 throw new RuntimeException("cannot find method source of 'public void " + method +
    100                         "' in file '" + searchPath + "'");
    101             }
    102 
    103             MatchResult result = scanner.match();
    104             result.start();
    105             methodSkip = result.end();
    106         }
    107 
    108         StringBuilder builder = new StringBuilder();
    109 
    110         try {
    111             StringReader reader = new StringReader(content);
    112             reader.skip(methodSkip);
    113 
    114             int readResult;
    115             int blocks = 1;
    116             while ((readResult = reader.read()) != -1 && blocks > 0) {
    117                 char currentChar = (char) readResult;
    118                 switch (currentChar) {
    119                     case '}': {
    120                         blocks--;
    121                         builder.append(currentChar);
    122                         break;
    123                     }
    124                     case '{': {
    125                         blocks++;
    126                         builder.append(currentChar);
    127                         break;
    128                     }
    129                     default: {
    130                         builder.append(currentChar);
    131                         break;
    132                     }
    133                 }
    134             }
    135             if (reader != null) {
    136                 reader.close();
    137             }
    138         } catch (Exception e) {
    139             throw new RuntimeException("failed to parse", e);
    140         }
    141 
    142         // find the @title/@constraint in javadoc comment for this method
    143         // using platform's default charset
    144 
    145         // System.out.println("grepping javadoc found for method " + method +
    146         // " in " + pname + "," + classOnlyName);
    147         String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
    148         Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
    149         Matcher m = p.matcher(content);
    150         String title = null, constraint = null;
    151         if (m.find()) {
    152             String res = m.group(1);
    153             // System.out.println("res: " + res);
    154             // now grep @title and @constraint
    155             Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
    156             .matcher(res);
    157             if (titleM.find()) {
    158                 title = titleM.group(1).replaceAll("\\n     \\*", "");
    159                 title = title.replaceAll("\\n", " ");
    160                 title = title.trim();
    161                 // System.out.println("title: " + title);
    162             } else {
    163                 System.err.println("warning: no @title found for method " + method + " in " + pname +
    164                         "," + classOnlyName);
    165             }
    166             // constraint can be one line only
    167             Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
    168                     res);
    169             if (constraintM.find()) {
    170                 constraint = constraintM.group(1);
    171                 constraint = constraint.trim();
    172                 // System.out.println("constraint: " + constraint);
    173             } else if (method.contains("VFE")) {
    174                 System.err
    175                 .println("warning: no @constraint for for a VFE method:" + method + " in " +
    176                         pname + "," + classOnlyName);
    177             }
    178         } else {
    179             System.err.println("warning: no javadoc found for method " + method + " in " + pname +
    180                     "," + classOnlyName);
    181         }
    182         MethodData md = new MethodData();
    183         md.methodBody = builder.toString();
    184         md.constraint = constraint;
    185         md.title = title;
    186         return md;
    187     }
    188 
    189     /**
    190      * @param pName
    191      * @param classOnlyName
    192      * @param methodSource
    193      * @return testclass names
    194      */
    195     protected static List<String> parseTestClassName(String pName, String classOnlyName,
    196             String methodSource) {
    197         List<String> entries = new ArrayList<String>(2);
    198         String opcodeName = classOnlyName.substring(5);
    199 
    200         try (Scanner scanner = new Scanner(methodSource)) {
    201             String[] patterns = new String[] { "new\\s(T_" + opcodeName + "\\w*)",
    202                     "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)" };
    203 
    204             String token = null;
    205             for (String pattern : patterns) {
    206                 token = scanner.findWithinHorizon(pattern, methodSource.length());
    207                 if (token != null) {
    208                     break;
    209                 }
    210             }
    211 
    212             if (token == null) {
    213                 System.err.println("warning: failed to find dependent test class name: " + pName
    214                         + ", " + classOnlyName + " in methodSource:\n" + methodSource);
    215                 return entries;
    216             }
    217 
    218             MatchResult result = scanner.match();
    219 
    220             entries.add((pName + ".d." + result.group(1)).trim());
    221 
    222             // search additional @uses directives
    223             Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
    224             Matcher m = p.matcher(methodSource);
    225             while (m.find()) {
    226                 String res = m.group(1);
    227                 entries.add(0, res.trim());
    228             }
    229 
    230             // search for " load(\"...\" " and add as dependency
    231             Pattern loadPattern = Pattern.compile("load\\(\"([^\"]*)\"", Pattern.MULTILINE);
    232             Matcher loadMatcher = loadPattern.matcher(methodSource);
    233             while (loadMatcher.find()) {
    234                 String res = loadMatcher.group(1);
    235                 entries.add(res.trim());
    236             }
    237 
    238             // search for " loadAndRun(\"...\" " and add as dependency
    239             Pattern loadAndRunPattern = Pattern.compile("loadAndRun\\(\"([^\"]*)\"",
    240                     Pattern.MULTILINE);
    241             Matcher loadAndRunMatcher = loadAndRunPattern.matcher(methodSource);
    242             while (loadAndRunMatcher.find()) {
    243                 String res = loadAndRunMatcher.group(1);
    244                 entries.add(res.trim());
    245             }
    246 
    247             // lines with the form @uses
    248             // dot.junit.opcodes.add_double.jm.T_add_double_2
    249             // one dependency per one @uses
    250             // TODO
    251 
    252             return entries;
    253         }
    254     }
    255 
    256     public static void writeToFileMkdir(File file, String content) {
    257         File parent = file.getParentFile();
    258         if (!parent.exists() && !parent.mkdirs()) {
    259             throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
    260         }
    261         writeToFile(file, content);
    262     }
    263 
    264     public static void writeToFile(File file, String content) {
    265         try {
    266             if (file.exists() && file.length() == content.length()) {
    267                 FileReader reader = new FileReader(file);
    268                 char[] charContents = new char[(int) file.length()];
    269                 reader.read(charContents);
    270                 reader.close();
    271                 String contents = new String(charContents);
    272                 if (contents.equals(content)) {
    273                     // System.out.println("skipping identical: "
    274                     // + file.getAbsolutePath());
    275                     return;
    276                 }
    277             }
    278 
    279             //System.out.println("writing file " + file.getAbsolutePath());
    280 
    281             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
    282                     new FileOutputStream(file), "utf-8"));
    283             bw.write(content);
    284             bw.close();
    285         } catch (Exception e) {
    286             throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
    287                     ", msg:" + e.getMessage());
    288         }
    289     }
    290 
    291 }
    292