--- /dev/null
+/*
+ * 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;
+ }
+}