package wyc.testing;
import static org.junit.Assert.fail;
import java.io.*;
import wyc.WycMain;
import wyc.util.WycBuildTask;
import wycc.util.Pair;
/**
* Provides some simple helper functions used by all test harnesses.
*
* @author David J. Pearce
*
*/
public class TestUtils {
/**
* Run the Whiley Compiler with the given list of arguments.
*
* @param args
* --- list of command-line arguments to provide to the Whiley
* Compiler.
* @return
*/
public static Pair<Integer,String> compile(String... args) {
ByteArrayOutputStream syserr = new ByteArrayOutputStream();
ByteArrayOutputStream sysout = new ByteArrayOutputStream();
int exitCode = new WycMain(new WycBuildTask(), WycMain.DEFAULT_OPTIONS, sysout, syserr)
.run(args);
byte[] errBytes = syserr.toByteArray();
byte[] outBytes = sysout.toByteArray();
String output = new String(errBytes) + new String(outBytes);
return new Pair<Integer,String>(exitCode,output);
}
/**
* Execute a given class file using the "java" command, and return all
* output written to stdout. In the case of some kind of failure, write the
* generated stderr stream to this processes stdout.
*
* @param classPath
* Class path to use when executing Java code. Note, directories
* can always be safely separated with '/', and path separated
* with ':'.
* @param srcDir
* Path to root of package containing class. Note, directories
* can always be safely separated with '/'.
* @param className
* Name of class to execute
* @param args
* Arguments to supply on the command-line.
* @return All output generated from the class that was written to stdout.
*/
public static String exec(String classPath, String srcDir, String className, String... args) {
try {
classPath = classPath.replace('/', File.separatorChar);
classPath = classPath.replace(':', File.pathSeparatorChar);
srcDir = srcDir.replace('/', File.separatorChar);
String tmp = "java -cp " + classPath + " " + className;
for (String arg : args) {
tmp += " " + arg;
}
Process p = Runtime.getRuntime().exec(tmp, null, new File(srcDir));
StringBuffer syserr = new StringBuffer();
StringBuffer sysout = new StringBuffer();
new StreamGrabber(p.getErrorStream(), syserr);
new StreamGrabber(p.getInputStream(), sysout);
int exitCode = p.waitFor();
if (exitCode != 0) {
System.err
.println("============================================================");
System.err.println(className);
System.err
.println("============================================================");
System.err.println(syserr);
return null;
} else {
return sysout.toString();
}
} catch (Exception ex) {
ex.printStackTrace();
fail("Problem running compiled test");
}
return null;
}
/**
* Compare the output of executing java on the test case with a reference
* file. If the output differs from the reference output, then the offending
* line is written to the stdout and an exception is thrown.
*
* @param output
* This provides the output from executing java on the test case.
* @param referenceFile
* The full path to the reference file. This should use the
* appropriate separator char for the host operating system.
*/
public static void compare(String output, String referenceFile) {
try {
BufferedReader outReader = new BufferedReader(new StringReader(
output));
BufferedReader refReader = new BufferedReader(new FileReader(
new File(referenceFile)));
while (refReader.ready() && outReader.ready()) {
String a = refReader.readLine();
String b = outReader.readLine();
if (a.equals(b)) {
continue;
} else {
System.err.println(" > " + a);
System.err.println(" < " + b);
throw new Error("Output doesn't match reference");
}
}
String l1 = outReader.readLine();
String l2 = refReader.readLine();
if (l1 == null && l2 == null)
return;
do {
l1 = outReader.readLine();
l2 = refReader.readLine();
if (l1 != null) {
System.err.println(" < " + l1);
} else if (l2 != null) {
System.err.println(" > " + l2);
}
} while (l1 != null && l2 != null);
fail("Files do not match");
} catch (Exception ex) {
ex.printStackTrace();
fail();
}
}
/**
* Grab everything produced by a given input stream until the End-Of-File
* (EOF) is reached. This is implemented as a separate thread to ensure that
* reading from other streams can happen concurrently. For example, we can
* read concurrently from <code>stdin</code> and <code>stderr</code> for
* some process without blocking that process.
*
* @author David J. Pearce
*
*/
static public class StreamGrabber extends Thread {
private InputStream input;
private StringBuffer buffer;
StreamGrabber(InputStream input, StringBuffer buffer) {
this.input = input;
this.buffer = buffer;
start();
}
public void run() {
try {
int nextChar;
// keep reading!!
while ((nextChar = input.read()) != -1) {
buffer.append((char) nextChar);
}
} catch (IOException ioe) {
}
}
}
}