import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileReader; import java.io.PrintWriter; import javax.tools.JavaFileManager; import junit.framework.TestCase; import org.junit.Test; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.comp.AttrContext; import com.sun.tools.javac.comp.Env; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.main.OptionName; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.Pretty; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JavacFileManager; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Options; import com.sun.tools.javac.util.Pair; /** * Base class for JUnit test cases for Deterministic Parallel Java. * * <p> * Subclasses may call {@link #parse(String)} to parse a DPJ program stored in * the test/dpj-programs directory and {@link #analyze(JCCompilationUnit)} to * perform attribution (symbol table construction and type checking), flow * analysis, and desugaring. * * @author Jeff Overbey */ // Code based on Main#compile and JavaCompiler#compile public abstract class DPJTestCase extends TestCase { protected Context context; protected JCCompilationUnit parse(String filename) { context = new Context(); JavacFileManager.preRegister(context); // Force Java 1.5 parsing and code generation Options.instance(context).put(OptionName.SOURCE, Source.JDK1_5.name); Options.instance(context).put(OptionName.TARGET, Target.JDK1_5.name); JavaCompiler comp = JavaCompiler.instance(context); JavacFileManager fileMgr = (JavacFileManager)context.get(JavaFileManager.class); JCCompilationUnit ast = comp.parse(fileMgr.getRegularFile(loadFile(filename))); assertNotNull(ast); return ast; } protected File loadFile(String filename) { String curdir = new File(".").getAbsolutePath(); String target = "Compiler/"; int index = curdir.lastIndexOf(target); if (index < 0) throw new Error("Cannot locate directory " + target + "test/dpj-programs"); int len = index + target.length(); String dir = curdir.substring(0, len) + "test/dpj-programs/"; return new File(dir + filename); } protected List<Pair<Env<AttrContext>, JCClassDecl>> analyze( int codeGenMode, JCCompilationUnit ast) throws Throwable { return analyze(codeGenMode, ast, false, 0); } protected List<Pair<Env<AttrContext>, JCClassDecl>> analyzeExpectingError( int codeGenMode, JCCompilationUnit ast) throws Throwable { return analyze(codeGenMode, ast, true, 0); } protected List<Pair<Env<AttrContext>, JCClassDecl>> analyzeExpectingWarnings( int warnings, JCCompilationUnit ast) throws Throwable { return analyze(Pretty.NONE, ast, false, warnings); } // protected abstract List<Pair<Env<AttrContext>, JCClassDecl>> analyze( // JCCompilationUnit ast, boolean expectErrors) throws Throwable; private List<Pair<Env<AttrContext>, JCClassDecl>> analyze( int codeGenMode, JCCompilationUnit ast, boolean expectErrors, int expectWarnings) throws Throwable { assertNotNull(context); // Must call #parse first JavaCompiler comp = JavaCompiler.instance(context); comp.eraseDPJ = false; // Keep DPJ annotations so we can ensure they're there comp.enterTrees(List.of(ast)); comp.totallyBogusFlag = true; // Turn off erasure of generics and regions List<Pair<Env<AttrContext>, JCClassDecl>> result = comp.desugar(comp.flow(comp.checkEffects(comp.attribute(comp.todo)))); assertNotNull(result); if (!expectErrors) assertFalse(result.isEmpty()); context.get(JavaFileManager.class).close(); assertEquals(expectErrors, Log.instance(context).nerrors != 0); if (expectWarnings > 0) assertEquals(expectWarnings, Log.instance(context).nwarnings); // Don't run unless we're specifically checking the typechecker return result; } // abstract protected void compareWithExpected(String filename, String expectedName) throws Throwable; protected void compareWithExpected(int codeGenMode, String filename, String expectedName) throws Throwable { StringBuilder actual = new StringBuilder(); for (Pair<Env<AttrContext>, JCClassDecl> pair : analyze(codeGenMode, parse(filename))) { JCTree.codeGenMode = codeGenMode; actual.append(pair.snd); } ByteArrayOutputStream expected = new ByteArrayOutputStream(); PrintWriter p = new PrintWriter(expected); BufferedReader r = new BufferedReader(new FileReader(loadFile(expectedName))); for (String line = r.readLine(); line != null; line = r.readLine()) { p.print(line); p.println(); } p.flush(); expected.close(); assertEquals(expected.toString().trim(), actual.toString().trim()); } protected void compareWithExpected(int codeGenMode, String filename) throws Throwable { compareWithExpected(codeGenMode, filename, filename + ".expected"); } }