OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / cts / tools / dx-tests / src / util / CollectAllTests.java
diff --git a/cts/tools/dx-tests/src/util/CollectAllTests.java b/cts/tools/dx-tests/src/util/CollectAllTests.java
new file mode 100644 (file)
index 0000000..028ec27
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package util;
+
+import dxc.junit.AllTests;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.textui.TestRunner;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Main class to generate data from the test suite to later run from a shell
+ * script. the project's home folder.<br>
+ * <project-home>/src must contain the java sources<br>
+ * <project-home>/data/scriptdata will be generated<br>
+ * <project-home>/src/<for-each-package>/Main_testN1.java will be generated<br>
+ * (one Main class for each test method in the Test_... class
+ */
+public class CollectAllTests {
+
+    private static String PROJECT_FOLDER = "";
+    private static String PROJECT_FOLDER_OUT = "missing out folder!";
+    private static String JAVASRC_FOLDER = PROJECT_FOLDER + "/src";
+    private static HashSet<String> OPCODES = null;
+
+    /*
+     * a map. key: fully qualified class name, value: a list of test methods for
+     * the given class
+     */
+    private TreeMap<String, List<String>> map = new TreeMap<String, List<String>>();
+
+    private int testClassCnt = 0;
+    private int testMethodsCnt = 0;
+
+    private class MethodData {
+        String methodBody, constraint, title;
+    }
+
+    /**
+     * @param args
+     *            args 0 must be the project root folder (where src, lib etc.
+     *            resides)
+     *            args 1 must be the project out root folder (where the Main_*.java files
+     *            are put, and also data/scriptdata)
+     */
+    public static void main(String[] args) {
+        if (args.length >= 2) {
+            PROJECT_FOLDER = args[0];
+            PROJECT_FOLDER_OUT = args[1];
+            JAVASRC_FOLDER = PROJECT_FOLDER + "/src";
+        } else {
+            System.out.println("usage: args 0 must be the project root folder (where src, lib etc. resides)" +
+                    "and args 1 must be the project out root folder (where the Main_*.java file" +
+                    " are put, and also data/scriptdata)");
+            return;
+        }
+        
+
+        for (int i = 2; i < args.length; i++) {
+            if (OPCODES == null) {
+                OPCODES = new HashSet<String>();
+            }
+            OPCODES.add(args[i]);
+        }
+        
+        System.out.println("using java src:"+JAVASRC_FOLDER);
+        CollectAllTests cat = new CollectAllTests();
+        cat.compose();
+    }
+
+    public void compose() {
+        System.out.println("Collecting all junit tests...");
+        new TestRunner() {
+            @Override
+            protected TestResult createTestResult() {
+                return new TestResult() {
+                    @Override
+                    protected void run(TestCase test) {
+                        addToTests(test);
+                    }
+
+                };
+            }
+        }.doRun(AllTests.suite());
+
+        // for each combination of TestClass and method, generate a Main_testN1
+        // etc.
+        // class in the respective package.
+        // for the report make sure all N... tests are called first, then B,
+        // then
+        // E, then VFE test methods.
+        // so we need x Main_xxxx methods in a package, and x entries in the
+        // global scriptdata file (read by a bash script for the tests)
+        // e.g. dxc.junit.opcodes.aaload.Test_aaload - testN1() ->
+        // File Main_testN1.java in package dxc.junit.opcodes.aaload
+        // and entry dxc.junit.opcodes.aaload.Main_testN1 in class execution
+        // table.
+        //
+        handleTests();
+    }
+
+    private void addToTests(TestCase test) {
+        
+        String packageName = test.getClass().getPackage().getName();
+        packageName = packageName.substring(packageName.lastIndexOf('.')+1);
+        if (OPCODES != null && !OPCODES.contains(packageName)) {
+            return;
+        }
+
+        
+        String method = test.getName(); // e.g. testVFE2
+        String fqcn = test.getClass().getName(); // e.g.
+        // dxc.junit.opcodes.iload_3.Test_iload_3
+        // order: take the order of the test-suites for the classes,
+        // TODO and for methods: take Nx, then Bx, then Ex, then VFEx
+        //System.out.println("collecting test:" + test.getName() + ", class "
+        //        + test.getClass().getName());
+        testMethodsCnt++;
+        List<String> li = map.get(fqcn);
+        if (li == null) {
+            testClassCnt++;
+            li = new ArrayList<String>();
+            map.put(fqcn, li);
+        }
+        li.add(method);
+    }
+
+    private void handleTests() {
+        System.out.println("collected "+testMethodsCnt+" test methods in "+testClassCnt+" junit test classes");
+        String datafileContent = "";
+
+        for (Entry<String, List<String>> entry : map.entrySet()) {
+            
+            String fqcn = entry.getKey();
+            int lastDotPos = fqcn.lastIndexOf('.');
+            String pName = fqcn.substring(0, lastDotPos);
+            String classOnlyName = fqcn.substring(lastDotPos + 1);
+            String instPrefix = "new " + classOnlyName + "()";
+            
+            String[] nameParts = pName.split("\\.");
+            if (nameParts.length != 4) {
+                throw new RuntimeException(
+                        "package name does not comply to naming scheme: " + pName);
+            }
+            
+           
+            List<String> methods = entry.getValue();
+            Collections.sort(methods, new Comparator<String>() {
+                public int compare(String s1, String s2) {
+                    // TODO sort according: test ... N, B, E, VFE
+                    return s1.compareTo(s2);
+                }
+            });
+            for (String method : methods) {
+                // e.g. testN1
+                if (!method.startsWith("test")) {
+                    throw new RuntimeException("no test method: " + method);
+                }
+                
+                // generate the Main_xx java class
+
+                // a Main_testXXX.java contains:
+                // package <packagenamehere>;
+                // public class Main_testxxx {
+                // public static void main(String[] args) {
+                // new dxc.junit.opcodes.aaload.Test_aaload().testN1();
+                // }
+                // }
+
+                MethodData md = parseTestMethod(pName, classOnlyName, method);
+                String methodContent = md.methodBody;
+
+                Set<String> dependentTestClassNames = parseTestClassName(pName,
+                        classOnlyName, methodContent);
+                
+                if (dependentTestClassNames.isEmpty())
+                {
+                    continue;
+                }
+                
+
+                String content = "//autogenerated by "
+                        + this.getClass().getName()
+                        + ", do not change\n"
+                        + "package "
+                        + pName
+                        + ";\n"
+                        + "import "
+                        + pName
+                        + ".jm.*;\n"
+                        + "import dxc.junit.*;\n"
+                        + "public class Main_"
+                        + method
+                        + " extends DxAbstractMain {\n"
+                        + "public static void main(String[] args) throws Exception {\n"
+                        + "new Main_" + method + "()." + method + "();\n"
+                        + "}\n" + methodContent + "\n}\n";
+
+                writeToFile(getFileFromPackage(pName, method), content);
+
+                // prepare the entry in the data file for the bash script.
+                // e.g.
+                // main class to execute; opcode/constraint; test purpose
+                // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
+                // (#1)
+
+                char ca = method.charAt("test".length()); // either N,B,E, oradd_double
+                // V (VFE)
+                String comment;
+                switch (ca) {
+                case 'N':
+                    comment = "Normal #" + method.substring(5);
+                    break;
+                case 'B':
+                    comment = "Boundary #" + method.substring(5);
+                    break;
+                case 'E':
+                    comment = "Exception #" + method.substring(5);
+                    break;
+                case 'V':
+                    comment = "Verifier #" + method.substring(7);
+                    break;
+                default:
+                    throw new RuntimeException("unknown test abbreviation:"
+                            + method + " for " + fqcn);
+                }
+
+                String opcConstr = pName.substring(pName.lastIndexOf('.') + 1);
+                // beautify test title
+                if (opcConstr.startsWith("t4")) {
+                    opcConstr = "verifier"; //  + opcConstr.substring(1);
+                } else if (opcConstr.startsWith("pargs")) {
+                    opcConstr = "sanity";
+                } else if (opcConstr.startsWith("opc_")) {
+                    // unescape reserved words
+                    opcConstr = opcConstr.substring(4);
+                }
+                
+                String line = pName + ".Main_" + method + ";";                
+                for (String className : dependentTestClassNames) {
+                    try {
+                        Class.forName(className);
+                    } catch (ClassNotFoundException e) {
+                        throw new RuntimeException(
+                                "dependent class not found : " + className);
+                    } catch (Throwable e) {
+                        // ignore
+                    }
+                    
+                    line += className + " ";
+                }
+                
+                String details = (md.title != null ? md.title : "");
+                if (md.constraint != null) {
+                    details = "Constraint " + md.constraint + ", " + details;
+                }
+                if (details.length() != 0) {
+                    details = details.substring(0, 1).toUpperCase() 
+                            + details.substring(1);
+                }
+
+                line += ";" + opcConstr + ";"+ comment + ";" + details;
+                
+                datafileContent += line + "\n";
+                
+            }
+
+
+        }
+        new File(PROJECT_FOLDER_OUT + "/data").mkdirs();
+        writeToFile(new File(PROJECT_FOLDER_OUT + "/data/scriptdata"),
+                datafileContent);
+    }
+
+
+
+    /**
+     * 
+     * @param pName
+     * @param classOnlyName
+     * @param methodSource
+     * @return a set
+     */
+    private Set<String> parseTestClassName(String pName, String classOnlyName,
+            String methodSource) {
+        Set<String> entries = new HashSet<String>();
+        String opcodeName = classOnlyName.substring(5);
+
+        Scanner scanner = new Scanner(methodSource);
+
+        String[] patterns = new String[] {
+                "new\\s(T_" + opcodeName + "\\w*)",
+                "(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)"};
+
+        String token = null;
+        for (String pattern : patterns) {
+            token = scanner.findWithinHorizon(pattern, methodSource.length());
+            if (token != null) {
+                break;
+            }
+        }
+
+        if (token == null) {
+            System.err.println("warning: failed to find dependent test class name: "+pName+", "+classOnlyName);
+            return entries;
+        }
+
+        MatchResult result = scanner.match();
+
+        entries.add((pName + ".jm." + result.group(1)).trim());
+        
+        // search additional @uses directives
+        Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
+        Matcher m = p.matcher(methodSource);
+        while (m.find()) {
+            String res = m.group(1);
+            entries.add(res.trim());
+        }
+        
+        //lines with the form @uses dx.junit.opcodes.add_double.jm.T_add_double_2
+        // one dependency per one @uses
+        //TODO
+        
+        return entries;
+    }
+
+    private MethodData parseTestMethod(String pname, String classOnlyName,
+            String method) {
+
+        String path = pname.replaceAll("\\.", "/");
+        String absPath = JAVASRC_FOLDER + "/" + path + "/" + classOnlyName
+                + ".java";
+        File f = new File(absPath);
+
+        Scanner scanner;
+        try {
+            scanner = new Scanner(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("error while reading from file: "
+                    + e.getClass().getName() + ", msg:" + e.getMessage());
+        }
+
+        String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
+
+        String token = scanner.findWithinHorizon(methodPattern, (int) f
+                .length());
+        if (token == null) {
+            throw new RuntimeException(
+                    "cannot find method source of 'public void" + method
+                            + "' in file '" + absPath + "'");
+        }
+
+        MatchResult result = scanner.match();
+        result.start();
+        result.end();
+
+        StringBuilder builder = new StringBuilder();
+        builder.append(token);
+
+        try {
+            FileReader reader = new FileReader(f);
+            reader.skip(result.end());
+
+            char currentChar;
+            int blocks = 1;
+            while ((currentChar = (char) reader.read()) != -1 && blocks > 0) {
+                switch (currentChar) {
+                case '}': {
+                    blocks--;
+                    builder.append(currentChar);
+                    break;
+                }
+                case '{': {
+                    blocks++;
+                    builder.append(currentChar);
+                    break;
+                }
+                default: {
+                    builder.append(currentChar);
+                    break;
+                }
+                }
+            }
+           if (reader != null) {
+               reader.close();
+           }
+        } catch (Exception e) {
+            throw new RuntimeException("failed to parse", e);
+        }
+
+        // find the @title/@constraint in javadoc comment for this method
+        Scanner scanner2;
+        try {
+            // using platform's default charset
+            scanner2 = new Scanner(f);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("error while reading from file: "
+                    + e.getClass().getName() + ", msg:" + e.getMessage());
+        }
+        
+        // using platform's default charset
+        String all = new String(readFile(f));
+        // System.out.println("grepping javadoc found for method "+method +
+        // " in "+pname+","+classOnlyName);
+        String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
+        Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
+        Matcher m = p.matcher(all);
+        String title = null, constraint = null;
+        if (m.find()) {
+            String res = m.group(1);
+            // System.out.println("res: "+res);
+            // now grep @title and @constraint
+            Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
+                    .matcher(res);
+            if (titleM.find()) {
+                title = titleM.group(1).replaceAll("\\n     \\*", "");
+                title = title.replaceAll("\\n", " ");
+                title = title.trim();
+                // System.out.println("title: " + title);
+            } else {
+                System.err.println("warning: no @title found for method "
+                        + method + " in " + pname + "," + classOnlyName);
+            }
+            // constraint can be one line only
+            Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
+                    res);
+            if (constraintM.find()) {
+                constraint = constraintM.group(1);
+                constraint = constraint.trim();
+                // System.out.println("constraint: " + constraint);
+            } else if (method.contains("VFE")) {
+                System.err
+                        .println("warning: no @constraint for for a VFE method:"
+                                + method + " in " + pname + "," + classOnlyName);
+            }
+        } else {
+            System.err.println("warning: no javadoc found for method " + method
+                    + " in " + pname + "," + classOnlyName);
+        }
+        MethodData md = new MethodData();
+        md.methodBody = builder.toString();
+        md.constraint = constraint;
+        md.title = title;
+       if (scanner != null) {
+           scanner.close();
+       }
+       if (scanner2 != null) {
+           scanner2.close();
+       }
+        return md;
+    }
+
+    private void writeToFile(File file, String content) {
+        //System.out.println("writing file " + file.getAbsolutePath());
+        try {
+            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
+                    new FileOutputStream(file), "utf-8"));
+            bw.write(content);
+            bw.close();
+        } catch (Exception e) {
+            throw new RuntimeException("error while writing to file: "
+                    + e.getClass().getName() + ", msg:" + e.getMessage());
+        }
+    }
+
+    private File getFileFromPackage(String pname, String methodName) {
+        // e.g. dxc.junit.argsreturns.pargsreturn
+        String path = pname.replaceAll("\\.", "/");
+        String absPath = PROJECT_FOLDER_OUT + "/" + path;
+        new File(absPath).mkdirs();
+        return new File(absPath + "/Main_" + methodName + ".java");
+    }
+    
+    private byte[] readFile(File file) {
+        int len = (int) file.length();
+        byte[] res = new byte[len];
+        try {
+            FileInputStream in = new FileInputStream(file);
+            int pos = 0;
+            while (len > 0) {
+                int br = in.read(res, pos, len);
+                if (br == -1) {
+                    throw new RuntimeException("unexpected EOF for file: "+file);
+                }
+                pos += br;
+                len -= br;
+            }
+            in.close();
+        } catch (IOException ex) {
+            throw new RuntimeException("error reading file:"+file, ex);
+        }
+        return res;
+    }
+}