Java tutorial
// : com:bruceeckel:simpletest:Test.java //Simple utility for testing program output. Intercepts //System.out to print both to the console and a buffer. //From 'Thinking in Java, 3rd ed.' (c) Bruce Eckel 2002 //www.BruceEckel.com. See copyright notice in CopyRight.txt. import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.regex.Pattern; public class Alias2 { private static Test monitor = new Test(); private int i; public Alias2(int ii) { i = ii; } public static void f(Alias2 reference) { reference.i++; } public static void main(String[] args) { Alias2 x = new Alias2(7); System.out.println("x: " + x.i); System.out.println("Calling f(x)"); f(x); System.out.println("x: " + x.i); monitor.expect(new String[] { "x: 7", "Calling f(x)", "x: 8" }); } } ///:~ class Test { // Bit-shifted so they can be added together: public static final int EXACT = 1 << 0, // Lines must match exactly AT_LEAST = 1 << 1, // Must be at least these lines IGNORE_ORDER = 1 << 2, // Ignore line order WAIT = 1 << 3; // Delay until all lines are output private String className; private TestStream testStream; public Test() { // Discover the name of the class this // object was created within: className = new Throwable().getStackTrace()[1].getClassName(); testStream = new TestStream(className); } public static List fileToList(String fname) { ArrayList list = new ArrayList(); try { BufferedReader in = new BufferedReader(new FileReader(fname)); try { String line; while ((line = in.readLine()) != null) { if (fname.endsWith(".txt")) list.add(line); else list.add(new TestExpression(line)); } } finally { in.close(); } } catch (IOException e) { throw new RuntimeException(e); } return list; } public static List arrayToList(Object[] array) { List l = new ArrayList(); for (int i = 0; i < array.length; i++) { if (array[i] instanceof TestExpression) { TestExpression re = (TestExpression) array[i]; for (int j = 0; j < re.getNumber(); j++) l.add(re); } else { l.add(new TestExpression(array[i].toString())); } } return l; } public void expect(Object[] exp, int flags) { if ((flags & WAIT) != 0) while (testStream.numOfLines < exp.length) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } List output = fileToList(className + "Output.txt"); if ((flags & IGNORE_ORDER) == IGNORE_ORDER) OutputVerifier.verifyIgnoreOrder(output, exp); else if ((flags & AT_LEAST) == AT_LEAST) OutputVerifier.verifyAtLeast(output, arrayToList(exp)); else OutputVerifier.verify(output, arrayToList(exp)); // Clean up the output file - see c06:Detergent.java testStream.openOutputFile(); } public void expect(Object[] expected) { expect(expected, EXACT); } public void expect(Object[] expectFirst, String fname, int flags) { List expected = fileToList(fname); for (int i = 0; i < expectFirst.length; i++) expected.add(i, expectFirst[i]); expect(expected.toArray(), flags); } public void expect(Object[] expectFirst, String fname) { expect(expectFirst, fname, EXACT); } public void expect(String fname) { expect(new Object[] {}, fname, EXACT); } } ///:~ class TestExpression implements Comparable { private Pattern p; private String expression; private boolean isRegEx; // Default to only one instance of this expression: private int duplicates = 1; public TestExpression(String s) { this.expression = s; if (expression.startsWith("%% ")) { this.isRegEx = true; expression = expression.substring(3); this.p = Pattern.compile(expression); } } // For duplicate instances: public TestExpression(String s, int duplicates) { this(s); this.duplicates = duplicates; } public String toString() { if (isRegEx) return p.pattern(); return expression; } public boolean equals(Object obj) { if (this == obj) return true; if (isRegEx) return (compareTo(obj) == 0); return expression.equals(obj.toString()); } public int compareTo(Object obj) { if ((isRegEx) && (p.matcher(obj.toString()).matches())) return 0; return expression.compareTo(obj.toString()); } public int getNumber() { return duplicates; } public String getExpression() { return expression; } public boolean isRegEx() { return isRegEx; } } ///:~ class TestStream extends PrintStream { protected int numOfLines; private PrintStream console = System.out, err = System.err, fout; // To store lines sent to System.out or err private InputStream stdin; private String className; public TestStream(String className) { super(System.out, true); // Autoflush System.setOut(this); System.setErr(this); stdin = System.in; // Save to restore in dispose() // Replace the default version with one that // automatically produces input on demand: System.setIn(new BufferedInputStream(new InputStream() { char[] input = ("test\n").toCharArray(); int index = 0; public int read() { return (int) input[index = (index + 1) % input.length]; } })); this.className = className; openOutputFile(); } // public PrintStream getConsole() { return console; } public void dispose() { System.setOut(console); System.setErr(err); System.setIn(stdin); } // This will write over an old Output.txt file: public void openOutputFile() { try { fout = new PrintStream(new FileOutputStream(new File(className + "Output.txt"))); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } // Override all possible print/println methods to send // intercepted console output to both the console and // the Output.txt file: public void print(boolean x) { console.print(x); fout.print(x); } public void println(boolean x) { numOfLines++; console.println(x); fout.println(x); } public void print(char x) { console.print(x); fout.print(x); } public void println(char x) { numOfLines++; console.println(x); fout.println(x); } public void print(int x) { console.print(x); fout.print(x); } public void println(int x) { numOfLines++; console.println(x); fout.println(x); } public void print(long x) { console.print(x); fout.print(x); } public void println(long x) { numOfLines++; console.println(x); fout.println(x); } public void print(float x) { console.print(x); fout.print(x); } public void println(float x) { numOfLines++; console.println(x); fout.println(x); } public void print(double x) { console.print(x); fout.print(x); } public void println(double x) { numOfLines++; console.println(x); fout.println(x); } public void print(char[] x) { console.print(x); fout.print(x); } public void println(char[] x) { numOfLines++; console.println(x); fout.println(x); } public void print(String x) { console.print(x); fout.print(x); } public void println(String x) { numOfLines++; console.println(x); fout.println(x); } public void print(Object x) { console.print(x); fout.print(x); } public void println(Object x) { numOfLines++; console.println(x); fout.println(x); } public void println() { if (false) console.print("println"); numOfLines++; console.println(); fout.println(); } public void write(byte[] buffer, int offset, int length) { console.write(buffer, offset, length); fout.write(buffer, offset, length); } public void write(int b) { console.write(b); fout.write(b); } } ///:~ class OutputVerifier { private static void verifyLength(int output, int expected, int compare) { if ((compare == Test.EXACT && expected != output) || (compare == Test.AT_LEAST && output < expected)) throw new NumOfLinesException(expected, output); } public static void verify(List output, List expected) { verifyLength(output.size(), expected.size(), Test.EXACT); if (!expected.equals(output)) { //find the line of mismatch ListIterator it1 = expected.listIterator(); ListIterator it2 = output.listIterator(); while (it1.hasNext() && it2.hasNext() && it1.next().equals(it2.next())) ; throw new LineMismatchException(it1.nextIndex(), it1.previous().toString(), it2.previous().toString()); } } public static void verifyIgnoreOrder(List output, Object[] expected) { verifyLength(expected.length, output.size(), Test.EXACT); if (!(expected instanceof String[])) throw new RuntimeException("IGNORE_ORDER only works with String objects"); String[] out = new String[output.size()]; Iterator it = output.iterator(); for (int i = 0; i < out.length; i++) out[i] = it.next().toString(); Arrays.sort(out); Arrays.sort(expected); int i = 0; if (!Arrays.equals(expected, out)) { while (expected[i].equals(out[i])) { i++; } throw new SimpleTestException(((String) out[i]).compareTo(expected[i]) < 0 ? "output: <" + out[i] + ">" : "expected: <" + expected[i] + ">"); } } public static void verifyAtLeast(List output, List expected) { verifyLength(output.size(), expected.size(), Test.AT_LEAST); if (!output.containsAll(expected)) { ListIterator it = expected.listIterator(); while (output.contains(it.next())) { } throw new SimpleTestException("expected: <" + it.previous().toString() + ">"); } } } ///:~ class SimpleTestException extends RuntimeException { public SimpleTestException(String msg) { super(msg); } } ///:~ class NumOfLinesException extends SimpleTestException { public NumOfLinesException(int exp, int out) { super("Number of lines of output and " + "expected output did not match.\n" + "expected: <" + exp + ">\n" + "output: <" + out + "> lines)"); } } ///:~ class LineMismatchException extends SimpleTestException { public LineMismatchException(int lineNum, String expected, String output) { super("line " + lineNum + " of output did not match expected output\n" + "expected: <" + expected + ">\n" + "output: <" + output + ">"); } } ///:~