package Sequenic.T2.export; import java.io.FileNotFoundException; import java.io.PrintStream; import Sequenic.T2.Seq.CALL_METHOD; import Sequenic.T2.Seq.CONST; import Sequenic.T2.Seq.CREATE_OBJECT; import Sequenic.T2.Seq.MkValStep; import Sequenic.T2.Seq.REF; import Sequenic.T2.Seq.Trace; import Sequenic.T2.Seq.TraceStep; /** * This class can print the steps in a trace file to a JUnit test class. * * @author Christiaan Hees */ public class JUnitPrinter extends TraceExporter { /** * If this value is true then it will show the original steps of T2 * using comments in the output. */ private boolean showT2Steps = true; /** * You can limit the amount of traces exported with this variable. */ private int maxTraceCount = 10; public JUnitPrinter(String trFileString) { super(trFileString); visitor = new JUnitVisitor(); } @Override protected void print() { printPackage(trFile.CUTname); printImports(); printClassHeader(trFile.CUTname); int traceCount = 0; for(Trace t : trFile.traces) { if(traceCount >= maxTraceCount) break; traceCount++; printTrace(t, traceCount); } printClassFooter(); } private void printPackage(String cutName) { String pack = cutName.substring(0, cutName.lastIndexOf(".")); if(!"".equals(pack)) out.println("package "+pack+";\n"); } private void printImports() { out.println("import org.junit.Test;\n"); } private void printClassHeader(String cutName) { out.println("/**"); out.println(" * This file is auto-generated by JUnitPrinter."); out.println(" * Warning: This exporter is experimental so you might want to check if"); out.println(" * the generated code matches the T2 steps in the comments."); if(maxTraceCount < Integer.MAX_VALUE) out.println(" * The first "+maxTraceCount+" traces were exported."); out.println(" */"); out.println("public class " + cutName.substring(cutName.lastIndexOf(".")+1)+"ExportedTest {"); } private void printTrace(Trace t, int traceCount) { ((JUnitVisitor)visitor).reset(); out.println("\t@Test"); out.println("\tpublic void trace"+traceCount+"() {"); if(showT2Steps) out.println("//"+t.creation); t.creation.visit(visitor); for(TraceStep ts : t.trace) { if(showT2Steps) out.println("//"+ts); ts.visit(visitor); } out.println("\t}"); } private void printClassFooter() { out.println("}"); } /** * Prints the steps in a trace file to a JUnit test class. * The first argument should be the name of the trace file. */ public static void main(String[] args) { if(args.length < 1) { System.out.println( "Usage: java Sequenic.T2.export.JUnitPrinter [OutputFile]"); System.exit(0); } JUnitPrinter jp = new JUnitPrinter(args[0]); if(args.length > 1) { try { jp.out = new PrintStream(args[1]); } catch (FileNotFoundException e) { System.out.println("ERROR: failed to open "+args[1]); System.exit(1); } } jp.print(); } private class JUnitVisitor extends Visitor { private int refCount; private int constCount; @Override public void visit(MkValStep mvs) { out.println("ERROR missing visitor: mvs "+mvs); System.exit(1); } @Override public void visit(CREATE_OBJECT co) { //out.println("DEBUG: co "+co); out.print("\t\t"+co.con.getName()+" ref"+refCount+" = "); refCount++; out.print("new "+co.con.getName()+"("); for(int i=co.params.length; i>0; i--) { out.print("const"+(constCount-i)); // FIXME // TODO ref and create object? if(i>1) out.print(", "); } out.println(");"); } @Override public void visit(TraceStep ts) { out.println("ERROR missing visitor: ts "+ts); System.exit(1); } @Override public void visit(CALL_METHOD cm) { //out.println("DEBUG: cm "+cm); // TODO check what happens when it's a static method call without a receiver // TODO check what happens when there's a mix of CONST and REF parameters if(!(cm.receiver instanceof REF)) { out.print("\t\tref"+(refCount-1)); } int consts = 0; out.print("."+cm.method.getName()+"("); StringBuffer pString = new StringBuffer(); for(int i=cm.params.length; i>0; i--) { MkValStep param = cm.params[i-1]; if(param instanceof CONST) { consts++; pString.insert(0, "const"+(constCount-consts)); } else if(param instanceof REF) { pString.insert(0, "ref"+((REF)param).index); //} else if(param instanceof CREATE_OBJECT) { // pString.insert(0, "ref?"); // TODO } else { out.println(pString); out.println("ERROR unexpected parameter: "+param); System.exit(1); } if(i>1) pString.insert(0, ", "); } out.print(pString); out.println(");"); } @Override public void visit(REF ref) { out.print("\t\tref"+ref.index); } @Override public void visit(CONST c) { out.print("\t\t"); out.print(c.getObjectClassName()); out.print(" const"+constCount+" = "); constCount++; // FIXME this could go wrong with some values? // Yes it does... if(c.isNull()) { out.println("null;"); } else { out.println("new "+c.getObjectClassName()+"("+stringToJava(c.getObject().toString())+");"); } } public void reset() { refCount = 0; constCount = 0; } private String stringToJava(String s) { return s; // TODO filter unprintable characters and quotes and newlines } } }