/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.backend.java; import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.junit.Assert.*; import abs.ABSTest; import abs.backend.java.codegeneration.JavaCode; import abs.backend.java.codegeneration.JavaCodeGenerationException; import abs.backend.java.lib.runtime.ABSException; import abs.backend.java.lib.runtime.ABSRuntime; import abs.backend.java.observing.COGView; import abs.backend.java.observing.ObjectView; import abs.backend.java.observing.SystemObserver; import abs.backend.java.scheduling.RandomSchedulingStrategy; import abs.frontend.analyser.SemanticConditionList; import abs.frontend.ast.Model; public class JavaBackendTest extends ABSTest { private static final boolean DEBUG = false; @SuppressWarnings("serial") final protected List<String> jvmArgs = new ArrayList<String>() {{ add("-Dabs.terminateOnException=true"); }}; /** * Additional arguments when running the compiled ABS program. */ final protected List<String> absArgs = new ArrayList<String>(); public final long seed; public final static long seed_UNUSED = -1; public JavaBackendTest() { seed = seed_UNUSED; } public JavaBackendTest(long randomSeed) { jvmArgs.add("-Dabs.totalscheduler="+RandomSchedulingStrategy.class.getName()); jvmArgs.add("-Dabs.randomseed="+randomSeed); assert randomSeed != seed_UNUSED : "not a valid seed value"; seed = randomSeed; } // factory method for creating the ABSRuntime protected ABSRuntime makeAbsRuntime() { return new ABSRuntime(); } void assertValidStdLib(String absCode) throws Exception { assertValidJava(getJavaCode("module JavaUnitTest; " + absCode, Config.WITH_STD_LIB, Config.WITHOUT_MODULE_NAME)); } void assertValid(String absCode) throws Exception { assertValidJava(getJavaCode("module JavaUnitTest; " + absCode, Config.WITHOUT_MODULE_NAME)); } public static void assertValidJava(JavaCode javaCode) throws Exception { try { javaCode.compile("-classpath", "bin", "-d", javaCode.getSrcDir().getAbsolutePath()+"/gen/test"); } catch (Exception e) { System.out.println(javaCode); throw e; } finally { javaCode.deleteCode(); } } /** * compiles and executes the given code * ABS assertions can be used to check the result */ protected void assertValidJavaExecution(String absFile, boolean useStdLib) throws Exception { FileReader fileReader = new FileReader(absFile); BufferedReader bufferedReader = new BufferedReader(fileReader); List<String> lines = new ArrayList<String>(); String line = null; while ((line = bufferedReader.readLine()) != null) { lines.add(line); } bufferedReader.close(); assertValidJavaExecution(useStdLib, lines.toArray(new String[lines.size()])); } void assertValidJavaExecution(boolean withStdLib, String ... codeLines) throws Exception { StringBuilder absCode = new StringBuilder(); for (String line : codeLines) { absCode.append(line); absCode.append("\n"); } JavaCode javaCode = getJavaCode(absCode.toString(), withStdLib ? Config.WITH_STD_LIB : null); try { String genDir = javaCode.getSrcDir().getAbsolutePath()+"/gen/test"; javaCode.compile("-classpath", "bin", "-d", genDir); final ABSRuntime r = makeAbsRuntime(); r.enableDebugging(true); final boolean[] finished = new boolean[] {false}; final List<ABSException> exceptions = Collections.synchronizedList(new ArrayList<ABSException>()); r.addSystemObserver(new SystemObserver() { @Override public void systemStarted() { } @Override public void systemFinished() { synchronized (finished) { finished[0] = true; finished.notifyAll(); } } @Override public void systemError(ABSException e) { exceptions.add(e); } @Override public void newCOGCreated(COGView cog, ObjectView initialObject) { } }); r.start(new File(genDir), "Test.Main"); while (!finished[0]) { synchronized (finished) { finished.wait(100); } } r.shutdown(); for (ABSException e : exceptions) { throw e; } } catch (Exception e) { System.out.println(javaCode); throw e; } finally { javaCode.deleteCode(); } } @SuppressWarnings("serial") static class NoTestResultFoundException extends RuntimeException { NoTestResultFoundException() { super("No test result was found!"); } } final static Pattern p = Pattern.compile(".*__ABS_TESTRESULT=([^\n]*)\n.*", Pattern.MULTILINE | Pattern.DOTALL); protected StringBuffer runJava(JavaCode javaCode, String... jvmargs) throws Exception { StringBuffer output = new StringBuffer(); javaCode.compile("-classpath", "bin", "-d", javaCode.getSrcDir().getAbsolutePath()+"/gen/test"); ArrayList<String> args = new ArrayList<String>(); args.add("java"); args.addAll(Arrays.asList(jvmargs)); args.addAll(Arrays.asList("-cp", "bin" + File.pathSeparator + "lib/apfloat-1.8.2.jar" + File.pathSeparator + javaCode.getSrcDir().getAbsolutePath()+"/gen/test", javaCode.getFirstMainClass())); args.addAll(absArgs); ProcessBuilder pb = new ProcessBuilder(args.toArray(new String[0])); pb.redirectErrorStream(true); Process p = pb.start(); BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); while (true) { String s; s = r.readLine(); if (s == null) break; output.append(s + "\n"); } r.close(); javaCode.deleteCode(); return output; } public boolean runJavaAndTestResult(JavaCode javaCode, boolean expectFail) throws Exception { StringBuffer output = null; try { output = runJava(javaCode, jvmArgs.toArray(new String[0])); String s = output.toString() + "\n"; String result = null; Matcher m = p.matcher(s); if (m.matches()) { result = m.group(1); } if (result == null) throw new NoTestResultFoundException(); return Boolean.valueOf(result); } catch (NoTestResultFoundException e) { if (expectFail) { throw e; } else { assert output != null; // we're sure that runJava returned. System.err.println(output.toString()); //System.out.println(javaCode); return false; } } catch (Exception e) { if (output != null) System.err.println(output.toString()); else System.err.println("NO OUTPUT"); //System.err.println(javaCode); throw e; } } protected JavaCode getJavaCode(String absCode, Config... config) throws Exception { Model model = null; String code = null; code = absCode; // if (withStdLib) // code = // "data Unit = Unit; data Bool = True | False; data Int; data String; data Fut<A>; " // + code; final int len = config.length; Config[] c2 = new Config[len+2]; for (int i =0; i<len; i++) { c2[i] = config[i]; } c2[len] = Config.TYPE_CHECK; // c2[len+1] = Config.WITH_LOC_INF; // XXX: Trips up CI. model = assertParse(code, c2); if (model.hasErrors()) { fail(model.getErrors().getFirstError().getHelpMessage()); } else { SemanticConditionList el = model.typeCheck(); if (el.containsErrors()) { fail(el.getFirstError().getMsg()); } } if (model.hasErrors()) { fail(model.getErrors().getFirstError().getHelpMessage()); return null; } return getJavaCode(model); } static JavaCode getJavaCode(Model model) throws IOException, JavaCodeGenerationException { JavaCode code = new JavaCode(); model.generateJavaCode(code, true); return code; } public void assertEvalTrue(String absCode) throws Exception { assertEvalEquals(absCode, true); } public void assertEvalEquals(String absCode, boolean value) throws Exception { JavaCode javaCode = getJavaCode(absCode, Config.WITH_STD_LIB); if (DEBUG) System.err.println(javaCode); boolean res = runJavaAndTestResult(javaCode, false); if (value != res) { //System.out.println(javaCode); } assertEquals(value, res); } public void assertEvalFails(String absCode) throws Exception { JavaCode javaCode = getJavaCode(absCode, Config.WITH_STD_LIB); try { runJavaAndTestResult(javaCode, true); System.err.println(javaCode); fail("Expected that Java run failed, but did not."); } catch (NoTestResultFoundException e) { // OK } } }