Mega Code Archive

 
Categories / Java / Development Class
 

Simple utility for testing program output

// : 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         + ">");   } } ///:~