/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* JavaWorld Library, Copyright 2011 Bryan Chadwick *
* *
* FILE: ./testing/Examples.java *
* *
* This file is part of JavaWorld. *
* *
* JavaWorld is free software: you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation, either version *
* 3 of the License, or (at your option) any later version. *
* *
* JavaWorld is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with JavaWorld. If not, see
* In order to use the {@link testing.Examples Examples} * class, you simply import it, and create your own examples * class that extends {@link testing.Examples Examples}, and insert the * following stub method at the top: *
* public static void main(String[] args){ test(); } ** * When you run your examples class in Eclipse, this method will start the * create an instance of your examples class, print * it, and run any test methods (methods who's names begin with test). The * resulting report can be used to verify that your tests pass, or see where any * checkExpects failed. * * *
* In order to test specific classes and methods, you can use the appropriate * checkExpect methods. Specially named test method i.e., * testFoo return a number of checkExpects * combined with && (anded together). *
* ** Below is a sample, which demonstrates most of the testing features: * *
* import testing.Examples; * * public class FooExamples extends Examples{ * public static void main(String[] args){ test(); } * * Foo f1 = new Foo("hello"); * Foo f2 = new Foo("Jimmy"); * Bar b1 = new Bar(f1); * Bar b2 = new Bar(f2); * * boolean testFoo(){ * return (checkExpect(f1.s, "hello") && * checkExpect(f2, new Foo("Jimmy")) && * checkExpect(f2.i, 5)); * } * boolean testBar(){ * return (checkExpect(b1.getFoo().s, "hello") && * checkExpect(b2.getFoo(), new Foo("Jimmy")) && * checkExpect(b2.getFoo().i, 5)); * } * } * * class Foo{ * int i = 5; * String s; * Foo(String s){ * this.s = s; * } * } * * class Bar{ * Foo f; * Bar(Foo f){ * this.f = f; * } * Foo getFoo(){ return f; } * } ** * * */ public class Examples{ /** Run all the tests for the invoking class, i.e., the outer * Examples class, passin whether or not to show all the examples */ public static void test(boolean show){ main(2,show); } /** Run all the tests for the invoking class, i.e., the outer * Examples class, show all examples */ public static void test(){ main(2,true); } /** Run all the tests for the invoking class, i.e., the outer * Examples class, looking up the class at depth/tt> in the call * stack. */ private static void main(int depth, boolean show){ StackTraceElement[] es = new RuntimeException().getStackTrace(); if(es.length < depth) throw new RuntimeException("Incorrect Example Class Layout"); Examples e; try{ Class> c = Class.forName(es[depth].getClassName()); Constructor> con = c.getConstructor(new Class[0]); if(!con.isAccessible())con.setAccessible(true); e = (Examples)con.newInstance(new Object[0]); }catch(Exception ex){ ex.printStackTrace(); throw new RuntimeException("Error Initializing Examples Class: "+es[1].getClassName()+ "\n Make sure YourExamples class is 'public'!"); } e.runTests(show); } /** Run all the tests and show all the instances for this * Examples class */ public boolean runTests(){ return runTests(this); } /** Run all the tests for this Examples class, tell whether * or not the Example instances should be shown */ public boolean runTests(boolean show){ return runTests(this,show); } /** Version number... */ private static double version = 0.1; /** Run all the tests and show all the instances for the * given examples class */ private boolean runTests(Examples ex){ return runTests(ex, true); } /** Run all the tests for the given examples class, tell whether * or not the Example instances should be shown */ private boolean runTests(Examples ex, boolean show){ if(!logEmpty())throw new RuntimeException("Already Tested"); rawlog(" /"+stars("Tester Version "+version, '-', MAX_WIDTH)+"\\"); String cName = ex.getClass().getSimpleName(); if(show){ rawlog(" |"+stars("Class: "+cName, '*', MAX_WIDTH)+"|"); log(" |"); try{ log(" | "+Util.display(ex)); }catch(Exception e){ log(" | !! Unable to display "+cName+ ", likely due to uninitialized Examples"); } log(" |"); } rawlog(" |"+stars("Testing", '*', MAX_WIDTH)+"|"); log(" |"); for(Method m:ex.getClass().getDeclaredMethods()){ if(m.getName().startsWith(TEST) && !Modifier.isStatic(m.getModifiers())){ if(checkSignature(m)){ try{ if(!m.isAccessible())m.setAccessible(true); boolean res = (Boolean)m.invoke(ex, new Object[0]); if(!res){ log(" |"); } }catch(Exception e){ log(" | Recieved unexpected Exception ("+e.getClass().getSimpleName()+") "+ "running method "+cName+"."+m.getName()); fail++; log(" |"); } }else{ log(" | Method "+cName+"."+m.getName()+" looks like a Test but has the "+ "wrong argument/return types"); fail++; log(" |"); } } } boolean fin = (numtests > 0 && fail == 0); if(fin){ rawlog(" \\"+stars(((numtests == 0)?"No available Tests": (numtests == 1)?"The Test Passed": (numtests == 2)?"Both Tests Passed":("All "+numtests+" Tests Passed")), '-', MAX_WIDTH)+"/"); } else{ rawlog(" \\"+stars("!! "+ ((numtests == 0)?"No Test Methods Found":(fail+" out of "+numtests+" Tests Failed"))+" !!",'-',MAX_WIDTH)+"/"); } printLog(); return fin; } /** Check the Methods signature for testing:
boolean testSomething(){ ... }
*/
private boolean checkSignature(Method m){
return (m.getReturnType().equals(Boolean.class) ||
m.getReturnType().equals(boolean.class)) &&
m.getParameterTypes().length == 0;
}
/** Make a bunch of Stars for bordering */
private String stars(String head, char one, int width){
int space = width-head.length();
return Util.repeat(one, (space+1)/2)+" "+head+" "+Util.repeat(one, space/2);
}
/** Output from all Tests... */
private static String output = "";
/** Counts for tests and errors */
private static int numtests = 0, fail = 0;
/** Print-out Width*/
private static int MAX_WIDTH = 80;
/** Just add the string to the output */
private void rawlog(String s){ output += s+"\n"; }
/** Log a given string/result */
private void log(String s){
int newline = s.indexOf('\n');
if(newline >= 0){
log(s.substring(0,newline));
log(" | "+s.substring(newline+1));
return;
}
if(s.length() <= MAX_WIDTH+4){
rawlog(s+Util.repeat(' ',MAX_WIDTH-s.length()+5)+"|");
return;
}
int i = MAX_WIDTH+4;
while(i > 0 && s.charAt(i) != ' ')i--;
if(i == 0)
rawlog(s);
else{
rawlog(s.substring(0, i)+Util.repeat(' ',MAX_WIDTH-i+5)+"|");
log(" | "+s.substring(i));
}
}
/** Log a given string/result */
private boolean logEmpty(){ return output.length() == 0; }
/***/
private void printLog(){
System.err.println(output);
}
/** Maximum difference for floating point values */
private static double DIFF = 0.000001;
/** Prefix for testing methods */
private static String TEST = "test";
/** The name of our check/expect methods */
private static String CHECK = "checkExpect";
/** Should we log check/expect failures? */
private static boolean LOGFAILS = true;
/** Check the given boolean against the Expected one */
public boolean checkExpect(boolean a, boolean b){
numtests++;
boolean ret = a == b;
if(!ret && LOGFAILS)addFailure(a,b,outerCheckExpect());
return ret;
}
/** Check the given character against the Expected one */
public boolean checkExpect(char a, char b){ return checkExpect((long)a,b); }
/** Check the given short-integer against the Expected one */
public boolean checkExpect(short a, short b){ return checkExpect((long)a,b); }
/** Check the given integer against the Expected one */
public boolean checkExpect(int a, int b){ return checkExpect((long)a,b); }
/** Check the given long-integer against the Expected one */
public boolean checkExpect(long a, long b){
numtests++;
boolean ret = a == b;
if(!ret && LOGFAILS)addFailure(a,b,outerCheckExpect());
return ret;
}
/** Check the given floating-point number against the Expected one */
public boolean checkExpect(float a, float b){ return checkExpect((double)a,b); }
/** Check the given double-precision number against the Expected one */
public boolean checkExpect(double a, double b){
numtests++;
boolean ret = Math.abs(((double)a)-b)