/*
* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package test.com.sun.max.vm.jtrun;
import static com.sun.max.lang.Classes.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import com.sun.max.ide.*;
import com.sun.max.io.*;
import com.sun.max.program.*;
import com.sun.max.program.option.*;
import com.sun.max.test.*;
import com.sun.max.test.JavaExecHarness.JavaTestCase;
import com.sun.max.test.JavaExecHarness.Run;
import com.sun.max.util.*;
public class JTGenerator {
private static final OptionSet options = new OptionSet(true);
private static final Option<Boolean> exceptionsOption = options.newBooleanOption("exceptions", true,
"Selects whether runs that are expected to throw exceptions will be tested.");
private static final Option<Boolean> runStringsOption = options.newBooleanOption("run-strings", true,
"Selects whether the input values will be reported for a test case that fails.");
private static final Option<Boolean> loadedOption = options.newBooleanOption("force-loaded", true,
"Specifies that all test classes will be loaded into the target.");
private static final Option<Boolean> resolvedOption = options.newBooleanOption("force-resolved", true,
"Specifies that all test classes and method will be resolved in the target.");
private static final Option<Boolean> restartOption = options.newBooleanOption("restart", true,
"Specifies that generated run scheme will allow starting the tests from a particular test number.");
private static final Option<Integer> verboseOption = options.newIntegerOption("verbose", 3,
"Specifies the verbose level of the generated tests.");
private static final Option<Boolean> compileOption = options.newBooleanOption("compile", true,
"Compiles the generate Java source code automatically using a Java source compiler.");
private static final Option<Boolean> forceCompileOption = options.newBooleanOption("force-compile", false,
"Forces compilation of the generated Java source code, even if it was not updated.");
private static final Option<String> packageOption = options.newStringOption("package", "some",
"");
private static final Option<Boolean> sortOption = options.newBooleanOption("alphabetical", true,
"Generates the test cases in alphabetical order.");
public static class Executor implements JavaExecHarness.Executor {
public void initialize(JavaExecHarness.JavaTestCase tc, boolean loadingPackages) {
// do nothing.
}
public Object execute(JavaTestCase c, Object[] vals) throws InvocationTargetException {
return null;
}
}
final IndentWriter writer;
/** Generate, if necessary, the runSchemeFile and the testRunsFile.
* @param extraOptions
* @param args
* @return true if either the runSchemeFile or the testRunsFile was actually updated.
*/
public static boolean generate(OptionSet extraOptions, String[] args) {
options.loadOptions(extraOptions);
return generate(args);
}
public static void main(String[] args) {
generate(options.parseArguments(args).getArguments());
}
private static boolean generate(final String[] arguments) throws ProgramError {
final Registry<TestHarness> registry = new Registry<TestHarness>(TestHarness.class, false);
final JavaExecHarness javaExecHarness = new JavaExecHarness(new Executor());
registry.registerObject("java", javaExecHarness);
final TestEngine engine = new TestEngine(registry);
engine.parseTests(arguments, sortOption.getValue());
boolean filesUpdated = false;
try {
final File jtConfigFile = sourceFile("JTConfig");
final File jtRunsFile = sourceFile("JTRuns");
final LinkedList<JavaTestCase> cases = extractJavaTests(engine);
if (generateConfigContent(jtConfigFile, cases)) {
filesUpdated = true;
System.out.println(jtConfigFile + " updated.");
}
if (generateTestRunsContent(jtRunsFile, cases)) {
filesUpdated = true;
System.out.println(jtRunsFile + " updated.");
}
if (forceCompileOption.getValue() || (filesUpdated && compileOption.getValue())) {
ToolChain.compile(JTGenerator.class, new String[] {className("JTConfig"), className("JTRuns")});
System.out.println(jtConfigFile + " recompiled.");
System.out.println(jtRunsFile + " recompiled.");
}
} catch (IOException e) {
throw ProgramError.unexpected(e);
}
return filesUpdated;
}
private static String className(String className) {
return (getPackageName(JTGenerator.class) + "." + packageOption.getValue() + ".") + className;
}
private static File sourceFile(String className) {
Classpath cp = JavaProject.getSourcePath(JTGenerator.class, false);
String qualifiedClassName = className(className).replace('.', File.separatorChar) + ".java";
File sourceFile = cp.findFile(qualifiedClassName);
assert sourceFile != null : "could not find source file for " + qualifiedClassName;
return sourceFile;
}
private static boolean generateConfigContent(final File configFile, final LinkedList<JavaTestCase> cases) throws IOException {
final Writer writer = new StringWriter();
final JTGenerator gen = new JTGenerator(writer);
gen.genClassList(cases);
writer.close();
return Files.updateGeneratedContent(configFile, ReadableSource.Static.fromString(writer.toString()), "// GENERATED TEST CLASS LIST", "// END GENERATED TEST CLASS LIST", false);
}
private static boolean generateTestRunsContent(final File testRunsFile, final LinkedList<JavaTestCase> cases) throws IOException {
final Writer writer = new StringWriter();
final JTGenerator gen = new JTGenerator(writer);
gen.genRunMethod(cases);
gen.genTestRuns(cases);
writer.close();
return Files.updateGeneratedContent(testRunsFile, ReadableSource.Static.fromString(writer.toString()), "// GENERATED TEST RUNS", "// END GENERATED TEST RUNS", false);
}
private static LinkedList<JavaExecHarness.JavaTestCase> extractJavaTests(final TestEngine engine) {
final Iterable<TestCase> tests = engine.getAllTests();
final LinkedList<JavaExecHarness.JavaTestCase> list = new LinkedList<JavaExecHarness.JavaTestCase>();
for (TestCase tc : tests) {
if (tc instanceof JavaExecHarness.JavaTestCase) {
list.add((JavaExecHarness.JavaTestCase) tc);
}
}
return list;
}
public JTGenerator(Writer w) {
writer = new IndentWriter(w);
}
public void genClassList(LinkedList<JavaExecHarness.JavaTestCase> testCases) {
writer.indent();
writer.println("private static final Class<?>[] classList = {");
writer.indent();
final Iterator<JavaExecHarness.JavaTestCase> iterator = testCases.iterator();
while (iterator.hasNext()) {
writer.print(getClassLiteral(iterator.next()));
if (iterator.hasNext()) {
writer.println(",");
} else {
writer.println("");
}
}
writer.outdent();
writer.println("};");
writer.outdent();
}
public void genRunMethod(LinkedList<JavaExecHarness.JavaTestCase> testCases) {
writer.indent();
writer.println("public static boolean runTest(int num) {");
writer.indent();
writer.println("switch(num) {");
writer.indent();
int i = 0;
for (JavaExecHarness.JavaTestCase testCase : testCases) {
writer.print("case " + (i++) + ": ");
writer.print(getTestCaseName(testCase) + "();");
writer.println(" break;");
}
writer.outdent();
writer.println("}");
writer.println("return true;");
writer.outdent();
writer.println("}");
}
private void genTestRuns(LinkedList<JavaExecHarness.JavaTestCase> testCases) {
writer.indent();
for (JavaExecHarness.JavaTestCase testCase : testCases) {
genTestCase(testCase, exceptionsOption.getValue());
}
writer.outdent();
}
public void genTestCase(JavaExecHarness.JavaTestCase testCase, boolean testExceptions) {
writer.print("static void ");
writer.println(getTestCaseName(testCase) + "() {");
writer.indent();
if (verboseOption.getValue() > 2) {
writer.println("begin(\"" + testCase.clazz.getName() + "\");");
}
writer.println("String runString = null;");
writer.println("try {");
for (JavaExecHarness.Run run : testCase.runs) {
genRun(testCase, run, testExceptions);
}
writer.println("} catch (Throwable t) {");
writer.println(" fail(runString, t);");
writer.println(" return;");
writer.println("}");
writer.println("pass();");
writer.outdent();
writer.println("}");
}
private String getTestCaseName(JavaExecHarness.JavaTestCase testCase) {
return testCase.clazz.getName().replaceAll("\\.", "_");
}
public void genRun(JavaExecHarness.JavaTestCase testCase, JavaExecHarness.Run run, boolean testExceptions) {
if (!testExceptions) {
return;
}
genRunComment(testCase, run);
genTestRun(testCase, run);
}
private void genRunComment(JavaExecHarness.JavaTestCase testCase, JavaExecHarness.Run run) {
writer.print("// ");
writer.print(JavaExecHarness.inputToString(testCase.clazz, run, false));
writer.print(" == ");
writer.print(JavaExecHarness.resultToString(run.expectedValue, run.expectedException));
writer.println();
}
private void genTestRun(JavaExecHarness.JavaTestCase testCase, JavaExecHarness.Run run) {
writer.indent();
String runString = "null";
if (runStringsOption.getValue()) {
runString = JavaExecHarness.inputToString(testCase.clazz, run, true);
}
if (run.expectedException != null) {
// an exception is expected, a value is NOT expected
writer.println("try {");
writer.indent();
writer.println("runString = " + runString + ";");
genTestCall(testCase, run);
writer.println(";");
writer.println("fail(runString);");
writer.println("return;");
writer.outdent();
writer.println("} catch (Throwable e) {");
writer.indent();
writer.println("if (e.getClass() != " + getExceptionName(run) + ") {");
writer.println(" fail(runString, e);");
writer.println(" return;");
writer.println("}");
writer.outdent();
writer.println("}");
} else {
// check the return value against the expected return value
if (run.expectedValue instanceof JavaExecHarness.MethodCall) {
writer.println("runString = " + runString + ";");
writer.print("if (!");
genValue(run.expectedValue);
writer.print("(");
genTestCall(testCase, run);
writer.println(")) {");
} else if (useEquals(run.expectedValue)) {
writer.println("runString = " + runString + ";");
writer.print("if (");
genValue(run.expectedValue);
writer.print(" != ");
genTestCall(testCase, run);
writer.println(") {");
} else {
writer.println("runString = " + runString + ";");
writer.print("if (!");
genValue(run.expectedValue);
writer.print(".equals(");
genTestCall(testCase, run);
writer.println(")) {");
}
writer.indent();
writer.println("fail(runString);");
writer.println("return;");
writer.outdent();
writer.println("}");
}
writer.outdent();
}
private void genTestCall(JavaExecHarness.JavaTestCase testCase, JavaExecHarness.Run run) {
writer.print(testCase.clazz.getName() + ".test(");
for (int i = 0; i < run.input.length; i++) {
if (i > 0) {
writer.print(", ");
}
genValue(run.input[i]);
}
writer.print(")");
}
private void genValue(Object v) {
if (v instanceof Character) {
final Character chv = (Character) v;
writer.print("(char) " + (int) chv.charValue());
} else if (v instanceof Float) {
writer.print(String.valueOf(v) + "f");
} else if (v instanceof Long) {
writer.print(String.valueOf(v) + "L");
} else if (v instanceof Short) {
writer.print("(short) " + String.valueOf(v));
} else if (v instanceof Byte) {
writer.print("(byte) " + String.valueOf(v));
} else if (v instanceof String) {
writer.print("\"" + String.valueOf(v) + "\"");
} else if (v instanceof JavaExecHarness.CodeLiteral) {
writer.print(v.toString());
} else {
writer.print(String.valueOf(v));
}
}
private boolean useEquals(Object expectedValue) {
if (expectedValue == null) {
return true;
} else if (expectedValue instanceof Integer) {
return true;
} else if (expectedValue instanceof Boolean) {
return true;
} else if (expectedValue instanceof Character) {
return true;
} else if (expectedValue instanceof Float) {
return true;
} else if (expectedValue instanceof Double) {
return true;
} else if (expectedValue instanceof Long) {
return true;
} else if (expectedValue instanceof Short) {
return true;
} else if (expectedValue instanceof Byte) {
return true;
} else if (expectedValue instanceof JavaExecHarness.CodeLiteral) {
return true;
}
return false;
}
private String getClassLiteral(JavaExecHarness.JavaTestCase testCase) {
return testCase.clazz.getName().replace('$', '.') + ".class";
}
private String getExceptionName(Run run) {
return run.expectedException.getName().replace('$', '.') + ".class";
}
}