//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util.test;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.Error;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.JPFShell;
import gov.nasa.jpf.Property;
import gov.nasa.jpf.annotation.FilterField;
import gov.nasa.jpf.tool.RunTest;
import gov.nasa.jpf.util.DevNullPrintStream;
import gov.nasa.jpf.util.JPFSiteUtils;
import gov.nasa.jpf.util.Reflection;
import gov.nasa.jpf.util.TypeRef;
import gov.nasa.jpf.vm.ExceptionInfo;
import gov.nasa.jpf.vm.NoUncaughtExceptionsProperty;
import gov.nasa.jpf.vm.NotDeadlockedProperty;
/**
* base class for JPF unit tests. TestJPF mostly includes JPF invocations
* that check for occurrence or absence of certain execution results
*
* This class can be used in two modes:
*
* <ol>
* <li> wrapping a number of related tests for different SuTs into one class
* (suite) that calls the various JPF runners with complete argument lists
* (as in JPF.main(String[]args)) </li>
*
* <li> derive a class from TestJPF that uses the "..This" methods, which in
* turn use reflection to automatically append the test class and method to the
* JPF.main argument list (based on the calling class / method names). Note that
* you have to obey naming conventions for this to work:
*
* <li> the SuT class has to be the same as the test class without "Test", e.g.
* "CastTest" -> "Cast" </li>
*
* <li> the SuT method has to have the same name as the @Test method that
* invokes JPF, e.g. "CastTest {.. @Test void testArrayCast() ..}" ->
* "Cast {.. void testArrayCast()..} </li>
* </ol>
*/
public abstract class TestJPF implements JPFShell {
static PrintStream out = System.out;
public static final String UNNAMED_PACKAGE = "";
public static final String SAME_PACKAGE = null;
//--- those are only used outside of JPF execution
@FilterField protected static boolean globalRunDirectly, globalShowConfig;
@FilterField protected static boolean runDirectly; // don't run test methods through JPF, invoke it directly
@FilterField protected static boolean stopOnFailure; // stop as soon as we encounter a failed test or error
@FilterField protected static boolean showConfig; // for debugging purposes
@FilterField protected static boolean showConfigSources; // for debugging purposes
@FilterField protected static boolean hideSummary;
@FilterField protected static boolean quiet; // don't show test output
@FilterField protected String sutClassName;
static class GlobalArg {
String key;
String val;
GlobalArg (String k, String v){
key = k;
val = v;
}
}
// it seems wrong to pull globalArgs here instead of setting it from
// RunTest, but RunTest has to make sure TestJPF is loaded through the
// JPFClassLoader, i.e. cannot directly reference this class.
@FilterField static ArrayList<GlobalArg> globalArgs;
protected static ArrayList<GlobalArg> getGlobalArgs() {
// NOTE - this is only set if we execute tests from build.xml
Config globalConf = RunTest.getConfig();
if (globalConf != null){
ArrayList<GlobalArg> list = new ArrayList<GlobalArg>();
//--- the "test.<key>" specs
String[] testKeys = globalConf.getKeysStartingWith("test.");
if (testKeys.length > 0){
for (String key : testKeys){
String val = globalConf.getString(key);
// <2do> this is a hack to avoid the problem of not being able to store
// empty/nil/null values in the global config (they are removed during global config init)
if (val.equals("REMOVE")){
val = null;
}
key = key.substring(5);
list.add(new GlobalArg(key,val));
}
}
return list;
}
return null;
}
static {
if (!isJPFRun()){
globalArgs = getGlobalArgs();
}
}
//--- internal methods
public static void fail (String msg, String[] args, String cause){
StringBuilder sb = new StringBuilder();
sb.append(msg);
if (args != null){
for (String s : args){
sb.append(s);
sb.append(' ');
}
}
if (cause != null){
sb.append(':');
sb.append(cause);
}
fail(sb.toString());
}
public static void fail (){
throw new AssertionError();
}
public static void fail (String msg){
throw new AssertionError(msg);
}
public void report (String[] args) {
if (!quiet){
out.print(" running jpf with args:");
for (int i = 0; i < args.length; i++) {
out.print(' ');
out.print(args[i]);
}
out.println();
}
}
/**
* compute the SuT class name for a given JUnit test class: remove
* optionally ending "..Test", and replace package (if specified)
*
* @param testClassName the JUnit test class
* @param sutPackage optional SuT package name (without ending '.', null
* os SAME_PACKAGE means same package, "" or UNNAMED_PACKAGE means unnamed package)
* @return main class name of system under test
*/
protected static String getSutClassName (String testClassName, String sutPackage){
String sutClassName = testClassName;
int i = sutClassName.lastIndexOf('.');
if (i >= 0){ // testclass has a package
if (sutPackage == null){ // use same package
// nothing to do
} else if (sutPackage.length() > 0) { // explicit sut package
sutClassName = sutPackage + sutClassName.substring(i);
} else { // unnamed sut package
sutClassName = sutClassName.substring(i+1);
}
} else { // test class has no package
if (sutPackage == null || sutPackage.length() == 0){ // use same package
// nothing to do
} else { // explicit sut package
sutClassName = sutPackage + '.' + sutClassName;
}
}
if (sutClassName.endsWith("JPF")) {
sutClassName = sutClassName.substring(0, sutClassName.length() - 3);
}
return sutClassName;
}
// we can't set the sutClassName only from main() called methods (like
// runTestsOfThisClass()) since main() doesn't get called if this is executed
// by Ant (via <junit> task)
// the default ctor is always executed
public TestJPF () {
sutClassName = getSutClassName(getClass().getName(), SAME_PACKAGE);
}
//------ the API to be used by subclasses
/**
* to be used from default ctor of derived class if the SuT is in a different
* package
* @param sutClassName the qualified SuT class name to be checked by JPF
*/
protected TestJPF (String sutClassName){
this.sutClassName = sutClassName;
}
public static boolean isJPFRun () {
return false;
}
public static boolean isJUnitRun() {
// intercepted by native peer if this runs under JPF
Throwable t = new Throwable();
t.fillInStackTrace();
for (StackTraceElement se : t.getStackTrace()){
if (se.getClassName().startsWith("org.junit.")){
return true;
}
}
return false;
}
public static boolean isRunTestRun() {
// intercepted by native peer if this runs under JPF
Throwable t = new Throwable();
t.fillInStackTrace();
for (StackTraceElement se : t.getStackTrace()){
if (se.getClassName().equals("gov.nasa.jpf.tool.RunTest")){
return true;
}
}
return false;
}
protected static void getOptions (String[] args){
runDirectly = globalRunDirectly;
showConfig = globalShowConfig;
// hideSummary and stopOnFailure only make sense as global options anyways
if (args != null){
for (int i=0; i<args.length; i++){
String a = args[i];
if (a != null){
if (a.length() > 0){
if (a.charAt(0) == '-'){
a = a.substring(1);
if (a.equals("d")){
runDirectly = true;
} else if (a.equals("s") || a.equals("show")){
showConfig = true;
} else if (a.equals("l") || a.equals("log")){
showConfigSources = true;
} else if (a.equals("q") || a.equals("quiet")){
quiet = true;
} else if (a.equals("x")){
stopOnFailure = true;
} else if (a.equals("h")){
hideSummary = true;
}
args[i] = null; // set it consumed
} else {
break; // done, this is a test method
}
}
}
}
}
}
protected static boolean hasExplicitTestMethods(String[] args){
for (String a : args){
if (a != null){
return true;
}
}
return false;
}
protected static List<Method> getMatchingMethods(Class<? extends TestJPF> testCls,
int setModifiers, int unsetModifiers, String[] annotationNames){
List<Method> list = new ArrayList<Method>();
for (Method m : testCls.getMethods()){
if (isMatchingMethod(m, setModifiers, unsetModifiers, annotationNames)){
list.add(m);
}
}
return list;
}
protected static boolean isMatchingMethod(Method m, int setModifiers, int unsetModifiers, String[] annotationNames) {
int mod = m.getModifiers();
if (((mod & setModifiers) != 0) && ((mod & unsetModifiers) == 0)) {
if (m.getParameterTypes().length == 0) {
if (annotationNames != null){
Annotation[] annotations = m.getAnnotations();
for (int i = 0; i < annotations.length; i++) {
String annotType = annotations[i].annotationType().getName();
for (int j = 0; j < annotationNames.length; j++) {
if (annotType.equals(annotationNames[j])) {
return true;
}
}
}
} else {
return true;
}
}
}
return false;
}
protected static List<Method> getContextMethods(Class<? extends TestJPF> testCls,
int setModifiers, int unsetModifiers, String annotation){
String[] annotations = {annotation};
List<Method> list = new ArrayList<Method>();
for (Method m : testCls.getMethods()){
if (isMatchingMethod(m, setModifiers, unsetModifiers, annotations)){
list.add(m);
}
}
return list;
}
protected static List<Method> getBeforeMethods(Class<? extends TestJPF> testCls){
return getContextMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, "org.junit.Before");
}
protected static List<Method> getAfterMethods(Class<? extends TestJPF> testCls){
return getContextMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, "org.junit.After");
}
protected static List<Method> getBeforeClassMethods(Class<? extends TestJPF> testCls){
return getContextMethods(testCls, Modifier.PUBLIC | Modifier.STATIC, 0, "org.junit.BeforeClass");
}
protected static List<Method> getAfterClassMethods(Class<? extends TestJPF> testCls){
return getContextMethods(testCls, Modifier.PUBLIC | Modifier.STATIC, 0, "org.junit.AfterClass");
}
protected static boolean haveTestMethodSpecs( String[] args){
if (args != null && args.length > 0){
for (int i=0; i<args.length; i++){
if (args[i] != null){
return true;
}
}
}
return false;
}
protected static List<Method> getTestMethods(Class<? extends TestJPF> testCls, String[] args){
String[] testAnnotations = {"org.junit.Test", "org.testng.annotations.Test"};
if (haveTestMethodSpecs( args)){ // test methods specified as arguments
List<Method> list = new ArrayList<Method>();
for (String test : args){
if (test != null){
try {
Method m = testCls.getMethod(test);
if (isMatchingMethod(m, Modifier.PUBLIC, Modifier.STATIC, null /*testAnnotations*/ )){
list.add(m);
} else {
throw new RuntimeException("test method must be @Test annotated public instance method without arguments: " + test);
}
} catch (NoSuchMethodException x) {
throw new RuntimeException("method: " + test
+ "() not in test class: " + testCls.getName(), x);
}
}
}
return list;
} else { // no explicit test method specification, get all matches
return getMatchingMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, testAnnotations);
}
}
protected static void reportTestStart(String mthName){
if (!quiet){
System.out.println();
System.out.print("......................................... testing ");
System.out.print(mthName);
System.out.println("()");
}
}
protected static void reportTestInitialization(String mthName){
if (!quiet){
System.out.print(".... running test initialization: ");
System.out.print(mthName);
System.out.println("()");
}
}
protected static void reportTestCleanup(String mthName){
if (!quiet){
System.out.print(".... running test cleanup: ");
System.out.print(mthName);
System.out.println("()");
}
}
protected static void reportTestFinished(String msg){
if (!quiet){
System.out.print("......................................... ");
System.out.println(msg);
}
}
protected static void reportResults(String clsName, int nTests, int nFailures, int nErrors, List<String> results){
System.out.println();
System.out.print("......................................... execution of testsuite: " + clsName);
if (nFailures > 0 || nErrors > 0){
System.out.println(" FAILED");
} else if (nTests > 0) {
System.out.println(" SUCCEEDED");
} else {
System.out.println(" OBSOLETE");
}
if (!quiet){
if (results != null) {
int i = 0;
for (String result : results) {
System.out.print(".... [" + ++i + "] ");
System.out.println(result);
}
}
}
System.out.print(".........................................");
System.out.println(" tests: " + nTests + ", failures: " + nFailures + ", errors: " + nErrors);
}
static void invoke (Method m, Object testObject) throws IllegalAccessException, InvocationTargetException {
PrintStream sysOut = null;
try {
if (quiet){
sysOut = System.out;
System.setOut( new DevNullPrintStream());
}
m.invoke( testObject);
} finally {
if (quiet){
System.setOut( sysOut);
}
}
}
/**
* this is the main test loop if this TestJPF instance is executed directly
* or called from RunTest. It is *not* called if this is executed from JUnit
*/
public static void runTests (Class<? extends TestJPF> testCls, String... args){
int nTests = 0;
int nFailures = 0;
int nErrors = 0;
String testMethodName = null;
List<String> results = null;
getOptions(args);
globalRunDirectly = runDirectly;
globalShowConfig = showConfig;
boolean globalStopOnFailure = stopOnFailure;
try {
List<Method> testMethods = getTestMethods(testCls, args);
results = new ArrayList<String>(testMethods.size());
// check if we have JUnit style housekeeping methods (initialization and
// cleanup should use the same mechanisms as JUnit)
List<Method> beforeClassMethods = getBeforeClassMethods(testCls);
List<Method> afterClassMethods = getAfterClassMethods(testCls);
List<Method> beforeMethods = getBeforeMethods(testCls);
List<Method> afterMethods = getAfterMethods(testCls);
for (Method initMethod : beforeClassMethods) {
reportTestInitialization(initMethod.getName());
initMethod.invoke(null);
}
for (Method testMethod : testMethods) {
testMethodName = testMethod.getName();
String result = testMethodName;
try {
Object testObject = testCls.newInstance();
nTests++;
reportTestStart( testMethodName);
// run per test initialization methods
for (Method initMethod : beforeMethods){
reportTestInitialization( initMethod.getName());
invoke( initMethod, testObject);
}
// now run the test method itself
invoke( testMethod, testObject);
result += ": Ok";
// run per test initialization methods
for (Method cleanupMethod : afterMethods){
reportTestCleanup( cleanupMethod.getName());
invoke( cleanupMethod, testObject);
}
} catch (InvocationTargetException x) {
Throwable cause = x.getCause();
cause.printStackTrace();
if (cause instanceof AssertionError) {
nFailures++;
reportTestFinished("test method failed with: " + cause.getMessage());
result += ": Failed";
} else {
nErrors++;
reportTestFinished("unexpected error while executing test method: " + cause.getMessage());
result += ": Error";
}
if (globalStopOnFailure){
break;
}
}
results.add(result);
reportTestFinished(result);
}
for (Method cleanupMethod : afterClassMethods) {
reportTestCleanup( cleanupMethod.getName());
cleanupMethod.invoke(null);
}
//--- those exceptions are unexpected and represent unrecoverable test harness errors
} catch (InvocationTargetException x) {
Throwable cause = x.getCause();
cause.printStackTrace();
nErrors++;
reportTestFinished("TEST ERROR: @BeforeClass,@AfterClass method failed: " + x.getMessage());
} catch (InstantiationException x) {
nErrors++;
reportTestFinished("TEST ERROR: cannot instantiate test class: " + x.getMessage());
} catch (IllegalAccessException x) { // can't happen if getTestMethods() worked
nErrors++;
reportTestFinished("TEST ERROR: default constructor or test method not public: " + testMethodName);
} catch (IllegalArgumentException x) { // can't happen if getTestMethods() worked
nErrors++;
reportTestFinished("TEST ERROR: illegal argument for test method: " + testMethodName);
} catch (RuntimeException rx) {
nErrors++;
reportTestFinished("TEST ERROR: " + rx.toString());
}
if (!hideSummary){
reportResults(testCls.getName(), nTests, nFailures, nErrors, results);
}
if (nErrors > 0 || nFailures > 0){
if (isRunTestRun()){
// we need to reportTestFinished this test has failed
throw new RunTest.Failed();
}
}
}
static String getProperty(String key){
// intercepted by peer
return null;
}
/**
* this is the JPF entry method in case there is no main() in the test class
*
* <2do> we should support test method arguments here
*/
static void runTestMethod(String args[]) throws Throwable {
String testClsName = getProperty("target");
String testMthName = getProperty("target.test_method");
Class<?> testCls = Class.forName(testClsName);
Object target = testCls.newInstance();
Method method = testCls.getMethod(testMthName);
try {
method.invoke(target);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
/**
* NOTE: this needs to be called from the concrete test class, typically from
* its main() method, otherwise we don't know the name of the class we have
* to pass to JPF
*/
protected static void runTestsOfThisClass (String[] testMethods){
// needs to be at the same stack level, so we can't delegate
Class<? extends TestJPF> testClass = Reflection.getCallerClass(TestJPF.class);
runTests(testClass, testMethods);
}
/**
* needs to be broken up into two methods for cases that do additional
* JPF initialization (jpf-inspector)
*
* this is called from the various verifyX() methods (i.e. host VM) to
* start JPF, it is never executed under JPF
*/
protected JPF createAndRunJPF (StackTraceElement testMethod, String[] args) {
JPF jpf = createJPF( testMethod, args);
if (jpf != null){
jpf.run();
}
return jpf;
}
/**
* this is never executed under JPF
*/
protected JPF createJPF (StackTraceElement testMethod, String[] args) {
JPF jpf = null;
Config conf = new Config(args);
// --- add global args (if we run under RunTest)
if (globalArgs != null) {
for (GlobalArg ga : globalArgs) {
String key = ga.key;
String val = ga.val;
if (val != null){
conf.put(key, val);
} else {
conf.remove(key);
}
}
}
setTestTargetKeys(conf, testMethod);
// --- initialize the classpath from <projectId>.test_classpath
String projectId = JPFSiteUtils.getCurrentProjectId();
if (projectId != null) {
String testCp = conf.getString(projectId + ".test_classpath");
if (testCp != null) {
conf.append("classpath", testCp, ",");
}
}
// --- if we have any specific test property overrides, do so
conf.promotePropertyCategory("test.");
getOptions(args);
if (showConfig || showConfigSources) {
PrintWriter pw = new PrintWriter(System.out, true);
if (showConfigSources) {
conf.printSources(pw);
}
if (showConfig) {
conf.print(pw);
}
pw.flush();
}
jpf = new JPF(conf);
return jpf;
}
protected void setTestTargetKeys(Config conf, StackTraceElement testMethod) {
conf.put("target.entry", "runTestMethod([Ljava/lang/String;)V");
conf.put("target", testMethod.getClassName());
conf.put("target.test_method", testMethod.getMethodName());
}
//--- the JPFShell interface
public void start(String[] testMethods){
Class<? extends TestJPF> testClass = getClass(); // this is an instance method
runTests(testClass, testMethods);
}
protected StackTraceElement getCaller(){
StackTraceElement[] st = (new Throwable()).getStackTrace();
return st[2];
}
protected StackTraceElement setTestMethod (String clsName, String mthName){
return new StackTraceElement( clsName, mthName, null, -1);
}
protected StackTraceElement setTestMethod (String mthName){
return new StackTraceElement( getClass().getName(), mthName, null, -1);
}
//--- the JPF run test methods
/**
* run JPF expecting a AssertionError in the SuT
* @param args JPF main() arguments
*/
protected JPF assertionError (StackTraceElement testMethod, String... args){
return unhandledException( testMethod, "java.lang.AssertionError", null, args );
}
protected JPF assertionError (String... args) {
return unhandledException( getCaller(), "java.lang.AssertionError", null, args );
}
protected JPF assertionErrorDetails (StackTraceElement testMethod, String details, String... args) {
return unhandledException( testMethod, "java.lang.AssertionError", details, args );
}
protected JPF assertionErrorDetails (String details, String... args) {
return unhandledException( getCaller(), "java.lang.AssertionError", details, args );
}
protected boolean verifyAssertionErrorDetails (String details, String... args){
if (runDirectly) {
return true;
} else {
unhandledException( getCaller(), "java.lang.AssertionError", details, args);
return false;
}
}
protected boolean verifyAssertionError (String... args){
if (runDirectly) {
return true;
} else {
unhandledException( getCaller(), "java.lang.AssertionError", null, args);
return false;
}
}
/**
* run JPF expecting no SuT property violations
*/
protected JPF noPropertyViolation (StackTraceElement testMethod, String... args) {
JPF jpf = null;
report(args);
try {
jpf = createAndRunJPF( testMethod, args);
} catch (Throwable t) {
// we get as much as one little hickup and we declare it failed
t.printStackTrace();
fail("JPF internal exception executing: ", args, t.toString());
return jpf;
}
List<Error> errors = jpf.getSearchErrors();
if ((errors != null) && (errors.size() > 0)) {
fail("JPF found unexpected errors: " + (errors.get(0)).getDescription());
}
return jpf;
}
protected JPF noPropertyViolation (String... args) {
return noPropertyViolation( getCaller(), args);
}
protected boolean verifyNoPropertyViolation (String...args){
if (runDirectly) {
return true;
} else {
noPropertyViolation( getCaller(), args);
return false;
}
}
/**
* NOTE: this uses the exception class name because it might be an
* exception type that is only known to JPF (i.e. not in the native classpath)
*
* @param xClassName name of the exception base type that is expected
* @param details detail message of the expected exception
* @param args JPF arguments
*/
protected JPF unhandledException (StackTraceElement testMethod, String xClassName, String details, String... args) {
JPF jpf = null;
report(args);
try {
jpf = createAndRunJPF(testMethod, args);
} catch (Throwable t) {
t.printStackTrace();
fail("JPF internal exception executing: ", args, t.toString());
return jpf;
}
Error error = jpf.getLastError();
if (error != null){
Property errorProperty = error.getProperty();
if (errorProperty instanceof NoUncaughtExceptionsProperty){
ExceptionInfo xi = ((NoUncaughtExceptionsProperty)errorProperty).getUncaughtExceptionInfo();
// String xn = xi.getExceptionClassname();
// if (!xn.equals(xClassName)) {
// fail("JPF caught wrong exception: " + xn + ", expected: " + xClassName);
// }
HashSet<String> exceptionNameSet = xi.getExceptionClassnames();
String xn = xi.getExceptionClassname();
if (!exceptionNameSet.contains(xClassName)){
fail("JPF caught wrong exception: " + xn + ", expected: " + xClassName);
}
if (details != null) {
String gotDetails = xi.getDetails();
if (gotDetails == null) {
fail("JPF caught the right exception but no details, expected: " + details);
} else {
if (!gotDetails.endsWith(details)) {
fail("JPF caught the right exception but the details were wrong: " + gotDetails + ", expected: " + details);
}
}
}
} else { // error not a NoUncaughtExceptionsProperty
fail("JPF failed to catch exception executing: ", args, ("expected " + xClassName));
}
} else { // no error
fail("JPF failed to catch exception executing: ", args, ("expected " + xClassName));
}
return jpf;
}
protected JPF unhandledException (String xClassName, String details, String... args) {
return unhandledException( getCaller(), xClassName, details, args);
}
protected boolean verifyUnhandledExceptionDetails (String xClassName, String details, String... args){
if (runDirectly) {
return true;
} else {
unhandledException( getCaller(), xClassName, details, args);
return false;
}
}
protected boolean verifyUnhandledException (String xClassName, String... args){
if (runDirectly) {
return true;
} else {
unhandledException( getCaller(), xClassName, null, args);
return false;
}
}
/**
* run JPF expecting it to throw an exception
* NOTE - xClassName needs to be the concrete exception, not a super class
* @param args JPF main() arguments
*/
protected JPF jpfException (StackTraceElement testMethod, Class<? extends Throwable> xCls, String... args) {
JPF jpf = null;
Throwable exception = null;
report(args);
try {
jpf = createAndRunJPF( testMethod, args);
} catch (JPF.ExitException xx) {
exception = xx.getCause();
} catch (Throwable x) {
exception = x;
}
if (exception != null){
if (!xCls.isAssignableFrom(exception.getClass())){
fail("JPF produced wrong exception: " + exception + ", expected: " + xCls.getName());
}
} else {
fail("JPF failed to produce exception, expected: " + xCls.getName());
}
return jpf;
}
protected JPF jpfException (Class<? extends Throwable> xCls, String... args) {
return jpfException( getCaller(), xCls, args);
}
protected boolean verifyJPFException (TypeRef xClsSpec, String... args){
if (runDirectly) {
return true;
} else {
try {
Class<? extends Throwable> xCls = xClsSpec.asNativeSubclass(Throwable.class);
jpfException( getCaller(), xCls, args);
} catch (ClassCastException ccx){
fail("not a property type: " + xClsSpec);
} catch (ClassNotFoundException cnfx){
fail("property class not found: " + xClsSpec);
}
return false;
}
}
/**
* run JPF expecting a property violation of the SuT
* @param args JPF main() arguments
*/
protected JPF propertyViolation (StackTraceElement testMethod, Class<? extends Property> propertyCls, String... args ){
JPF jpf = null;
report(args);
try {
jpf = createAndRunJPF( testMethod, args);
} catch (Throwable t) {
t.printStackTrace();
fail("JPF internal exception executing: ", args, t.toString());
}
List<Error> errors = jpf.getSearchErrors();
if (errors != null) {
for (Error e : errors) {
if (propertyCls == e.getProperty().getClass()) {
return jpf; // success, we got the sucker
}
}
}
fail("JPF failed to detect error: " + propertyCls.getName());
return jpf;
}
protected JPF propertyViolation (Class<? extends Property> propertyCls, String... args ){
return propertyViolation( getCaller(), propertyCls, args);
}
protected boolean verifyPropertyViolation (TypeRef propertyClsSpec, String... args){
if (runDirectly) {
return true;
} else {
try {
Class<? extends Property> propertyCls = propertyClsSpec.asNativeSubclass(Property.class);
propertyViolation( getCaller(), propertyCls, args);
} catch (ClassCastException ccx){
fail("not a property type: " + propertyClsSpec);
} catch (ClassNotFoundException cnfx){
fail("property class not found: " + propertyClsSpec);
}
return false;
}
}
/**
* run JPF expecting a deadlock in the SuT
* @param args JPF main() arguments
*/
protected JPF deadlock (String... args) {
return propertyViolation( getCaller(), NotDeadlockedProperty.class, args );
}
protected boolean verifyDeadlock (String... args){
if (runDirectly) {
return true;
} else {
propertyViolation( getCaller(), NotDeadlockedProperty.class, args);
return false;
}
}
// these are the org.junit.Assert APIs, but we don't want org.junit to be
// required to run tests
public static void assertEquals(String msg, Object expected, Object actual){
if (expected == null && actual == null) {
return;
}
if (expected != null && expected.equals(actual)) {
return;
}
fail(msg);
}
public static void assertEquals(Object expected, Object actual){
assertEquals("", expected, actual);
}
public static void assertEquals(String msg, int expected, int actual){
if (expected != actual) {
fail(msg);
}
}
public static void assertEquals(int expected, int actual){
assertEquals("expected != actual : " + expected + " != " + actual, expected, actual);
}
public static void assertEquals(String msg, long expected, long actual){
if (expected != actual) {
fail(msg);
}
}
public static void assertEquals(long expected, long actual){
assertEquals("expected != actual : " + expected + " != " + actual,
expected, actual);
}
public static void assertEquals(double expected, double actual){
if (expected != actual){
fail("expected != actual : " + expected + " != " + actual);
}
}
public static void assertEquals(String msg, double expected, double actual){
if (expected != actual){
fail(msg);
}
}
public static void assertEquals(float expected, float actual){
if (expected != actual){
fail("expected != actual : " + expected + " != " + actual);
}
}
public static void assertEquals(String msg, float expected, float actual){
if (expected != actual){
fail(msg);
}
}
public static void assertEquals(String msg, double expected, double actual, double delta){
if (Math.abs(expected - actual) > delta) {
fail(msg);
}
}
public static void assertEquals(double expected, double actual, double delta){
assertEquals("Math.abs(expected - actual) > delta : " + "Math.abs(" + expected + " - " + actual + ") > " + delta,
expected, actual, delta);
}
public static void assertEquals(String msg, float expected, float actual, float delta){
if (Math.abs(expected - actual) > delta) {
fail(msg);
}
}
public static void assertEquals(float expected, float actual, float delta){
assertEquals("Math.abs(expected - actual) > delta : " + "Math.abs(" + expected + " - " + actual + ") > " + delta,
expected, actual, delta);
}
public static void assertArrayEquals(byte[] expected, byte[] actual){
if (((expected == null) != (actual == null)) ||
(expected.length != actual.length)){
fail("array sizes different");
}
for (int i=0; i<expected.length; i++){
if (expected[i] != actual[i]){
fail("array element" + i + " different, expected " + expected[i] + ", actual " + actual[i]);
}
}
}
public static void assertArrayEquals(int[] expected, int[] actual){
if (((expected == null) != (actual == null)) ||
(expected.length != actual.length)){
fail("array sizes different");
}
for (int i=0; i<expected.length; i++){
if (expected[i] != actual[i]){
fail("array element" + i + " different, expected " + expected[i] + ", actual " + actual[i]);
}
}
}
public static void assertNotNull(String msg, Object o) {
if (o == null) {
fail(msg);
}
}
public static void assertNotNull(Object o){
assertNotNull("o == null", o);
}
public static void assertNull(String msg, Object o){
if (o != null) {
fail(msg);
}
}
public static void assertNull(Object o){
assertNull("o != null", o);
}
public static void assertSame(String msg, Object expected, Object actual){
if (expected != actual) {
fail(msg);
}
}
public static void assertSame(Object expected, Object actual){
assertSame("expected != actual : " + expected + " != " + actual, expected, actual);
}
public static void assertFalse (String msg, boolean cond){
if (cond) {
fail(msg);
}
}
public static void assertFalse (boolean cond){
assertFalse("", cond);
}
public static void assertTrue (String msg, boolean cond){
if (!cond) {
fail(msg);
}
}
public static void assertTrue (boolean cond){
assertTrue("", cond);
}
}