package junit.runner; | |
import junit.framework.*; | |
import java.lang.reflect.*; | |
import java.text.NumberFormat; | |
import java.io.*; | |
import java.util.*; | |
/** | |
* Base class for all test runners. | |
* This class was born live on stage in Sardinia during XP2000. | |
*/ | |
public abstract class BaseTestRunner implements TestListener { | |
public static final String SUITE_METHODNAME= "suite"; | |
private static Properties fPreferences; | |
static int fgMaxMessageLength= 500; | |
static boolean fgFilterStack= true; | |
boolean fLoading= true; | |
/* | |
* Implementation of TestListener | |
*/ | |
public synchronized void startTest(Test test) { | |
testStarted(test.toString()); | |
} | |
protected static void setPreferences(Properties preferences) { | |
fPreferences= preferences; | |
} | |
protected static Properties getPreferences() { | |
if (fPreferences == null) { | |
fPreferences= new Properties(); | |
fPreferences.put("loading", "true"); | |
fPreferences.put("filterstack", "true"); | |
readPreferences(); | |
} | |
return fPreferences; | |
} | |
public static void savePreferences() throws IOException { | |
FileOutputStream fos= new FileOutputStream(getPreferencesFile()); | |
try { | |
getPreferences().store(fos, ""); | |
} finally { | |
fos.close(); | |
} | |
} | |
public void setPreference(String key, String value) { | |
getPreferences().setProperty(key, value); | |
} | |
public synchronized void endTest(Test test) { | |
testEnded(test.toString()); | |
} | |
public synchronized void addError(final Test test, final Throwable t) { | |
testFailed(TestRunListener.STATUS_ERROR, test, t); | |
} | |
public synchronized void addFailure(final Test test, final AssertionFailedError t) { | |
testFailed(TestRunListener.STATUS_FAILURE, test, t); | |
} | |
// TestRunListener implementation | |
public abstract void testStarted(String testName); | |
public abstract void testEnded(String testName); | |
public abstract void testFailed(int status, Test test, Throwable t); | |
/** | |
* Returns the Test corresponding to the given suite. This is | |
* a template method, subclasses override runFailed(), clearStatus(). | |
*/ | |
public Test getTest(String suiteClassName) { | |
if (suiteClassName.length() <= 0) { | |
clearStatus(); | |
return null; | |
} | |
Class testClass= null; | |
try { | |
testClass= loadSuiteClass(suiteClassName); | |
} catch (ClassNotFoundException e) { | |
String clazz= e.getMessage(); | |
if (clazz == null) | |
clazz= suiteClassName; | |
runFailed("Class not found \""+clazz+"\""); | |
return null; | |
} catch(Exception e) { | |
runFailed("Error: "+e.toString()); | |
return null; | |
} | |
Method suiteMethod= null; | |
try { | |
suiteMethod= testClass.getMethod(SUITE_METHODNAME, new Class[0]); | |
} catch(Exception e) { | |
// try to extract a test suite automatically | |
clearStatus(); | |
return new TestSuite(testClass); | |
} | |
if (! Modifier.isStatic(suiteMethod.getModifiers())) { | |
runFailed("Suite() method must be static"); | |
return null; | |
} | |
Test test= null; | |
try { | |
test= (Test)suiteMethod.invoke(null, new Class[0]); // static method | |
if (test == null) | |
return test; | |
} | |
catch (InvocationTargetException e) { | |
runFailed("Failed to invoke suite():" + e.getTargetException().toString()); | |
return null; | |
} | |
catch (IllegalAccessException e) { | |
runFailed("Failed to invoke suite():" + e.toString()); | |
return null; | |
} | |
clearStatus(); | |
return test; | |
} | |
/** | |
* Returns the formatted string of the elapsed time. | |
*/ | |
public String elapsedTimeAsString(long runTime) { | |
return NumberFormat.getInstance().format((double)runTime/1000); | |
} | |
/** | |
* Processes the command line arguments and | |
* returns the name of the suite class to run or null | |
*/ | |
protected String processArguments(String[] args) { | |
String suiteName= null; | |
for (int i= 0; i < args.length; i++) { | |
if (args[i].equals("-noloading")) { | |
setLoading(false); | |
} else if (args[i].equals("-nofilterstack")) { | |
fgFilterStack= false; | |
} else if (args[i].equals("-c")) { | |
if (args.length > i+1) | |
suiteName= extractClassName(args[i+1]); | |
else | |
System.out.println("Missing Test class name"); | |
i++; | |
} else { | |
suiteName= args[i]; | |
} | |
} | |
return suiteName; | |
} | |
/** | |
* Sets the loading behaviour of the test runner | |
*/ | |
public void setLoading(boolean enable) { | |
fLoading= enable; | |
} | |
/** | |
* Extract the class name from a String in VA/Java style | |
*/ | |
public String extractClassName(String className) { | |
if(className.startsWith("Default package for")) | |
return className.substring(className.lastIndexOf(".")+1); | |
return className; | |
} | |
/** | |
* Truncates a String to the maximum length. | |
*/ | |
public static String truncate(String s) { | |
if (fgMaxMessageLength != -1 && s.length() > fgMaxMessageLength) | |
s= s.substring(0, fgMaxMessageLength)+"..."; | |
return s; | |
} | |
/** | |
* Override to define how to handle a failed loading of | |
* a test suite. | |
*/ | |
protected abstract void runFailed(String message); | |
/** | |
* Returns the loaded Class for a suite name. | |
*/ | |
protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException { | |
return getLoader().load(suiteClassName); | |
} | |
/** | |
* Clears the status message. | |
*/ | |
protected void clearStatus() { // Belongs in the GUI TestRunner class | |
} | |
/** | |
* Returns the loader to be used. | |
*/ | |
public TestSuiteLoader getLoader() { | |
if (useReloadingTestSuiteLoader()) | |
return new ReloadingTestSuiteLoader(); | |
return new StandardTestSuiteLoader(); | |
} | |
protected boolean useReloadingTestSuiteLoader() { | |
return getPreference("loading").equals("true") && !inVAJava() && fLoading; | |
} | |
private static File getPreferencesFile() { | |
String home= System.getProperty("user.home"); | |
return new File(home, "junit.properties"); | |
} | |
private static void readPreferences() { | |
InputStream is= null; | |
try { | |
is= new FileInputStream(getPreferencesFile()); | |
setPreferences(new Properties(getPreferences())); | |
getPreferences().load(is); | |
} catch (IOException e) { | |
try { | |
if (is != null) | |
is.close(); | |
} catch (IOException e1) { | |
} | |
} | |
} | |
public static String getPreference(String key) { | |
return getPreferences().getProperty(key); | |
} | |
public static int getPreference(String key, int dflt) { | |
String value= getPreference(key); | |
int intValue= dflt; | |
if (value == null) | |
return intValue; | |
try { | |
intValue= Integer.parseInt(value); | |
} catch (NumberFormatException ne) { | |
} | |
return intValue; | |
} | |
public static boolean inVAJava() { | |
try { | |
Class.forName("com.ibm.uvm.tools.DebugSupport"); | |
} | |
catch (Exception e) { | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Returns a filtered stack trace | |
*/ | |
public static String getFilteredTrace(Throwable t) { | |
StringWriter stringWriter= new StringWriter(); | |
PrintWriter writer= new PrintWriter(stringWriter); | |
t.printStackTrace(writer); | |
StringBuffer buffer= stringWriter.getBuffer(); | |
String trace= buffer.toString(); | |
return BaseTestRunner.getFilteredTrace(trace); | |
} | |
/** | |
* Filters stack frames from internal JUnit classes | |
*/ | |
public static String getFilteredTrace(String stack) { | |
if (showStackRaw()) | |
return stack; | |
StringWriter sw= new StringWriter(); | |
PrintWriter pw= new PrintWriter(sw); | |
StringReader sr= new StringReader(stack); | |
BufferedReader br= new BufferedReader(sr); | |
String line; | |
try { | |
while ((line= br.readLine()) != null) { | |
if (!filterLine(line)) | |
pw.println(line); | |
} | |
} catch (Exception IOException) { | |
return stack; // return the stack unfiltered | |
} | |
return sw.toString(); | |
} | |
protected static boolean showStackRaw() { | |
return !getPreference("filterstack").equals("true") || fgFilterStack == false; | |
} | |
static boolean filterLine(String line) { | |
String[] patterns= new String[] { | |
"junit.framework.TestCase", | |
"junit.framework.TestResult", | |
"junit.framework.TestSuite", | |
"junit.framework.Assert.", // don't filter AssertionFailure | |
"junit.swingui.TestRunner", | |
"junit.awtui.TestRunner", | |
"junit.textui.TestRunner", | |
"java.lang.reflect.Method.invoke(" | |
}; | |
for (int i= 0; i < patterns.length; i++) { | |
if (line.indexOf(patterns[i]) > 0) | |
return true; | |
} | |
return false; | |
} | |
static { | |
fgMaxMessageLength= getPreference("maxmessage", fgMaxMessageLength); | |
} | |
} |