package org.jmlspecs.openjmltest.testcases;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.*;
import org.junit.rules.TestName;
import static org.junit.Assert.*;
/** This class contains tests of the jmldoc functionality. It calls the actual
* jmldoc entry point on external java files.
* @author David Cok
*/
public class jmldoc {
@Rule
public TestName name = new TestName();
ByteArrayOutputStream berr;
ByteArrayOutputStream bout;
PrintStream savederr;
PrintStream savedout;
static String eol = System.getProperty("line.separator");
static String z = java.io.File.pathSeparator;
boolean print = false;
boolean capture = true;
@Before
public void setUp() throws Exception {
//capture = false;
//print = true;
savederr = System.err;
savedout = System.out;
if (capture) System.setErr(new PrintStream(berr=new ByteArrayOutputStream(10000)));
if (capture) System.setOut(new PrintStream(bout=new ByteArrayOutputStream(10000)));
}
@After
public void tearDown() {
berr = null;
bout = null;
System.setErr(savederr);
System.setOut(savedout);
}
/** This is a helper method that runs the compiler on the given set of
* command-line arguments, checking the result
* @param args the command-line arguments
* @param exitcode the expected exit code (0=OK, 1=completed with error messages
* 2=command-line problems, 3=system errors, 4=abort)
* @param all whether the expected output is all of (0) or just the prefix
* of (1) or a part of (2) the actual output
* @param output the expected output as one string
*/
public void helper(String[] args, int exitcode, int all, String ... output) {
int e = 4; // org.jmlspecs.openjml.jmldoc.Main.execute(args);
System.err.flush();
System.out.flush();
System.setErr(savederr);
System.setOut(savedout);
// Depending on how the log is setup, error output can go to either bout or berr
String actualOutput = capture ? bout.toString() : "";
String errOutput = capture ? berr.toString() : "";
if (print) System.out.println("EXPECTING: " + output[0]);
if (capture) try {
String tail = ""; //exitcode == 0 ? "" : "ENDING with exit code " + exitcode + eol;
if (print) System.out.println("TEST: " + name.getMethodName() + " exit=" + e + eol + errOutput);
String expected = output[0];
if (all==0) assertEquals("The error message is wrong",expected+tail,errOutput);
else if (all == -1) assertEquals("The error message is wrong",expected,errOutput);
else if (all == 1 && !errOutput.startsWith(expected)) {
fail("Output does not begin with: " + expected + eol + "Instead is: " + errOutput);
} else if (all == 2 && errOutput.indexOf(expected) == -1 ) {
fail("Output does not end with: " + expected + eol + "Instead is: " + errOutput);
}
if (output.length > 1) {
expected = output[1];
if (print) System.out.println("TEST: " + name.getMethodName() + " STANDARD OUT: " + eol + actualOutput);
if (all == 0) {
assertEquals("The standard out is wrong",expected+tail,actualOutput);
} else if (all == -1) {
assertEquals("The standard out is wrong",expected,actualOutput);
} else if (all == 2 && actualOutput.indexOf(expected) == -1) {
fail("Output does not end with: " + expected + eol + "Instead is: " + actualOutput);
}
}
assertEquals("The exit code is wrong",exitcode,e);
} catch (AssertionError ex) {
if (!print) System.out.println("TEST: " + name.getMethodName() + " exit=" + e + eol + actualOutput);
throw ex;
}
}
// FIXME - ignoring all jmldoc tests until jmldoc is implemented
@Test @Ignore
public void testTopLevelCompiler() throws Exception {
String failureMessage = "error: An entry point in org.jmlspecs.openjml.jmldoc.Main was called with a null argument" + eol;
helper(null,2,-1,failureMessage);
}
@Test @Ignore
public void testNoArgs() throws Exception {
String failureMessage = "Usage: jmldoc <options> <source files>" + eol +
"where possible options include:" + eol;
helper(new String[]{},1,1,"",failureMessage);
}
@Test @Ignore
public void testHelp() throws Exception {
String failureMessage = "Usage: jmldoc <options> <source files>" + eol +
"where possible options include:" + eol;
helper(new String[]{"-help"},0,1,"",failureMessage);
}
// FIXME - for some reason this works standalone, but fails when in sequence
@Test @Ignore
public void testBadOption() throws Exception {
String failureMessage = "jmldoc: error - invalid flag: -ZZZ" + eol;
String out = "usage: jmldoc [options] [packagenames] [sourcefiles] [@files]" + eol;
helper(new String[]{"-ZZZ"},1,1,failureMessage,out);
}
/** Tests setting the specs path through the command-line option, by using non-existent
* directories that then get complaints
* @throws Exception
*/
@Test @Ignore // FIXME - Behaves differently when run standalone vs. run with the other tests
public void _testSpecPath() throws Exception {
helper(new String[]
{"-classpath","cpath"+z+"cpath2","-sourcepath","spath","-specspath","A"+z+"$SY"+z+"$CP"+z+"$SP"+z+"Z","P"},
0,
1,
"",
// "jmldoc: warning - No source files for package P" + eol +
// "jmldoc: warning - No source files for package P" + eol +
// "jmldoc: error - No public or protected classes found to document.",
"warning: A specification path directory does not exist: A" + eol +
"warning: A specification path directory does not exist: cpath" + eol +
"warning: A specification path directory does not exist: cpath2" + eol +
"warning: A specification path directory does not exist: spath" + eol +
"warning: A specification path directory does not exist: Z" + eol
);
}
// FIXME - disabled tests - jmldoc not implemented
@Test @Ignore
public void testRecursiveCP() throws Exception {
helper(new String[]
{ "-classpath","test/testNoErrors"+z+"bin"+z+"$CP",
"-noInternalSpecs",
"test/testNoErrors/A.java",
},0,0,"warning: $CP is included in the specs path recursively or multiple times"+eol
+ "1 warning" + eol);
}
// TODO: Environment specific - backslashes
@Test @Ignore
public void testNoRuntime() throws Exception {
helper(new String[]
{ "-noInternalRuntime","-noInternalSpecs",
"-classpath","test/testNoErrors",
"test/testNoErrors/A.java",
},1,0,
"test\\testNoErrors\\A.java:1: package org.jmlspecs.lang does not exist"+eol+
"public class A {" +eol+
"^"+eol+
"1 error" + eol+
"");
}
@Test @Ignore
public void testDuplicateParse() throws Exception {
helper(new String[]
{ "-classpath","test/testNoErrors"+z+"bin",
"test/testNoErrors/A.java", "-jmlverbose", "-noInternalSpecs"
},0,2,"",
"parsing /home/projects/OpenJML/trunk/OpenJML/test/testNoErrors/A.java" + eol +
"parsing /home/projects/OpenJML/trunk/OpenJML/test/testNoErrors/A.refines-java" + eol +
"typechecking A" + eol +
"typechecked A" + eol +
//"flow checks A" + eol +
"");
}
@Test @Ignore
public void testIgnoreJava() throws Exception {
helper(new String[]
{ "-classpath","test/testJavaErrors"+z+"bin",
"test/testJavaErrors/A.java", "-jmlverbose", "-noInternalSpecs"
},0,2,"",
"parsing /home/projects/OpenJML/trunk/OpenJML/test/testJavaErrors/A.java" + eol +
"parsing /home/projects/OpenJML/trunk/OpenJML/test/testJavaErrors/A.refines-java" + eol +
"typechecking A" + eol +
"typechecked A" + eol +
//"flow checks A" + eol +
"");
}
@Test @Ignore
public void testSourcePath() throws Exception {
helper(new String[]
{ "-classpath","",
"-sourcepath","test/testNoErrors"+z+"runtime",
"-specspath","runtime",
"-noInternalSpecs",
"test/testNoErrors/A.java",
},0,0,"",
"");
}
/** Tests using source path but including java spec files - may encounter
* compilation warnings in the spec files as they evolve.
* @throws Exception
*/
@Test @Ignore
public void testSourcePathX() throws Exception {
helper(new String[]
{ "-classpath","bin",
"-sourcepath","test/testNoErrors",
"-specspath","runtime",
"-noPurityCheck",
"test/testNoErrors/A.java"
},0,0,
"Note: Some input files use unchecked or unsafe operations."+eol+
"Note: Recompile with -Xlint:unchecked for details."+eol);
}
@Test @Ignore
public void testSourcePath3() throws Exception {
helper(new String[]
{ "-classpath","",
"-sourcepath","test/testNoErrors"+z+"runtime",
"-specspath","",
"-noInternalSpecs",
"test/testNoErrors/A.java",
},0,0,"",
"");
}
// This test requires jmlruntime.jar to have been created - run the Makefile
// in the OpenJML project
@Test @Ignore
public void testSourcePath4() throws Exception {
helper(new String[]
{ "-classpath","jmlruntime.jar",
"-sourcepath","test/testNoErrors",
"-specspath","",
"-noInternalSpecs",
"test/testNoErrors/A.java",
},0,0,"",
"");
}
@Test @Ignore
public void testSourcePath5() throws Exception {
helper(new String[]
{ "-classpath","bin",
"-sourcepath","test/testNoErrors",
"-specspath","",
"-noInternalSpecs",
"test/testNoErrors/A.java",
},0,0,"",
"");
}
@Test @Ignore
public void testSourcePath2() throws Exception {
helper(new String[]
{ "-classpath","bin",
"-sourcepath","test/testNoErrors",
"-specspath","runtime",
"-noInternalSpecs",
"test/testNoErrors/A.java"
},1,0,"", // FIXME - exit code should really be 0
"");
}
@Test @Ignore
public void testSuperRead() { // TODO - file name is environment dependent
helper(new String[]
{ "-classpath","bin",
"-sourcepath","test",
"-specspath","test",
"test/testSuperRead/A.java"
},1,1
,""
,"test\\testSuperRead\\B.refines-java:3: This JML modifier is not allowed for a type declaration"
);
}
// FIXME - need to chjeck that the output of these two is correct
// @Test @Ignore
// public void testAPI() {
// System.setErr(savederr);
// System.setOut(savedout);
// try {
// java.io.File f = new java.io.File("test/testNoErrors/A.java");
// org.jmlspecs.openjml.Main m = new org.jmlspecs.openjml.Main(new String[]{});
// String s = m.prettyPrint(m.parseFiles(f).get(0),true);
// System.out.println(s);
// } catch (Exception e) {
// System.out.println(e);
// e.printStackTrace(System.out);
// }
// }
// @Test @Ignore
// public void testAPI2() {
// System.setErr(savederr);
// System.setOut(savedout);
// try {
// java.io.File f = new java.io.File("test/testNoErrors/A.java");
// org.jmlspecs.openjml.Main m = new org.jmlspecs.openjml.Main(new String[]{"-v"});
// String s = m.prettyPrint(m.parseFiles(f).get(0),true);
// System.out.println(s);
// } catch (Exception e) {
// System.out.println(e);
// e.printStackTrace(System.out);
// }
// }
}