gw.test.TestExecutionManager Maven / Gradle / Ivy
/*
* Copyright 2014 Guidewire Software, Inc.
*/
package gw.test;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.junit.internal.TextListener;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The TestExecutionManager class is responsible for the actual execution of tests, including executing the before/after
* hooks.
*/
public class TestExecutionManager {
private TestEnvironment _environment;
private boolean _beforeTestSuiteRun = false;
private boolean _typeSystemInitialized = false;
private Throwable _beforeTestSuiteFailed;
private Map _beforeTestClassFailed = new HashMap();
private final Map _testInfos = new HashMap();
private List _testWrappers;
private long _suiteStartTime = 0L;
private long _suiteTimeoutInMillis = 0L;
private boolean _suiteHasTimedOut = false;
private boolean _assertionsMustBeEnabled = true;
// Timing info
private long _testClassStartTime = 0L;
// TODO - AHK - Combine with _suiteStartTime
private long _suiteStartTimeNs = 0L;
private static boolean INCLUDE_TEST_TIMING_INFO = Boolean.getBoolean("gw.test.timing.info");
public void setEnvironment(TestEnvironment environment) {
_environment = environment;
}
public void setTestsFromSuite(List testWrappers) {
for (TestSuite suite : testWrappers) {
_testInfos.put(suite.getName(), new TestInfo(suite.countTestCases()));
}
_testWrappers = new ArrayList(testWrappers);
}
public void setSuiteTimeoutInMillis(long suiteTimeoutInMillis) {
_suiteTimeoutInMillis = suiteTimeoutInMillis;
}
public boolean assertionsMustBeEnabled() {
return _assertionsMustBeEnabled;
}
public void setAssertionsMustBeEnabled(boolean assertionsMustBeEnabled) {
_assertionsMustBeEnabled = assertionsMustBeEnabled;
}
public TestEnvironment getEnvironment() {
return _environment;
}
//=============================================================================================
// Implementation details
//=============================================================================================
protected void runTestClass(TestClass testClass, TestResult result) {
if (_suiteStartTime == 0) {
_suiteStartTime = System.currentTimeMillis();
}
if (_suiteStartTimeNs == 0) {
_suiteStartTimeNs = System.nanoTime();
}
// If we're configured to remotely execute and the test isn't already a remote test, then swap it out before we proceed.
// This will happen if a test is run from IntelliJ, since IntelliJ will instantiate the test object before we get a chance
// to intercept it
if (_environment.isRemoteExecutionEnvironment() && !(testClass instanceof BaseRemoteTestClass)) {
TestInfo testInfo = _testInfos.get(testClass.getTypeName());
if (testInfo == null) {
// We could be running the whole test, or just one method, so we get the number of test instances that were created
// out of the static map on TestClass; we have no access to the suite, so that's basically the only way to tell
// how many test methods within a given test class are going to be run
testInfo = new TestInfo(TestClass.getNumberOfInstancesOfTestClassCreated(testClass.getTypeName()));
_testInfos.put(testClass.getTypeName(), testInfo);
}
testClass = ((IForwardingTestEnvironment) _environment).makeRemoteTestClassIDEExecutionWrapper(testClass.getTypeName(), testClass.getName(), testInfo._testCount, this, testClass);
}
try {
maybeInitTypeSystem();
long testStartTimeNs = -1;
try {
try {
maybeCallBeforeTestSuite();
} catch (Throwable e) {
// Since the test result.startTest method only gets called if we get to reallyRun,
// if we fail before then we need to poke the TestResult object so that listeners
// get notified that the test is starting
result.startTest(maybeUnwrapTestClass(testClass));
if (_beforeTestSuiteFailed != null) {
_beforeTestSuiteFailed = e;
}
result.addError(testClass, e);
result.endTest(testClass);
throw e;
}
try {
maybeCallBeforeTestClass(testClass);
} catch (Throwable e) {
// Since the test result.startTest method only gets called if we get to reallyRun,
// if we fail before then we need to poke the TestResult object so that listeners
// get notified that the test is starting
result.startTest(maybeUnwrapTestClass(testClass));
if (!_beforeTestClassFailed.containsKey(testClass.getTypeName())) {
_beforeTestClassFailed.put(testClass.getTypeName(), e);
}
result.addError(testClass, e);
result.endTest(testClass);
throw e;
}
testStartTimeNs = System.nanoTime();
testClass.reallyRun(result);
} finally {
if (testStartTimeNs != -1) {
printTestRunTime("Method " + testClass.getTypeName() + " " + testClass.getName(), System.nanoTime() - testStartTimeNs);
}
try {
maybeCallAfterTestClass(testClass);
} finally {
maybeCallAfterTestSuite(testClass);
}
}
} catch (AssertionFailedError e) {
result.addFailure(maybeUnwrapTestClass(testClass), e);
} catch (ThreadDeath e) { // don't catch ThreadDeath by accident
throw e;
} catch (Throwable e) {
result.addError(maybeUnwrapTestClass(testClass), e);
}
}
protected TestClass maybeUnwrapTestClass(TestClass testClass) {
if (testClass instanceof IRemoteTestClassIDEExecutionWrapper) {
return ((IRemoteTestClassIDEExecutionWrapper) testClass).getWrapped();
} else {
return testClass;
}
}
protected void runTestClassBare(TestClass testClass) throws Throwable {
callBeforeTestMethod(testClass);
try {
testClass.reallyRunBare();
} catch (Throwable e) {
callAfterTestMethod(testClass, e);
throw e;
}
callAfterTestMethod(testClass, null);
}
public void maybeInitTypeSystem() {
if (!_typeSystemInitialized) {
_typeSystemInitialized = true;
_environment.initializeTypeSystem();
}
}
private void callAfterTestMethod(TestClass testClass, Throwable e) {
testClass.afterTestMethod(e);
_environment.afterTestMethod();
}
private void callBeforeTestMethod(TestClass testClass) {
_environment.beforeTestMethod();
testClass.beforeTestMethod();
}
private void maybeCallBeforeTestSuite() {
if (_beforeTestSuiteFailed != null) {
throw new RuntimeException("beforeTestSuite() failed on a previous test", _beforeTestSuiteFailed);
}
if (!_beforeTestSuiteRun) {
_beforeTestSuiteRun = true;
_environment.beforeTestSuite();
}
}
/**
* A convenience method for running this suite from a main method. Subclasses of
* Suite can create a main method like so:
*
* public static void main(String[] args) {
* System.exit( new GosuSuite().runSuite() ? 0 : 1 );
* }
*
*
* @return a boolean saying if all tests passed
*/
public final boolean runViaStaticSuiteMethod() {
IType iType = TypeSystem.getTypeFromObject(this);
IMethodInfo method = iType.getTypeInfo().getMethod("suite");
Test test = (Test) method.getCallHandler().handleCall(null);
return runImpl(test).wasSuccessful();
}
private Result runImpl(Test test) {
JUnitCore runner = new JUnitCore();
TextListener txtListener = new TextListener(System.out);
runner.addListener(txtListener);
return runner.run(test);
}
private void maybeCallBeforeTestClass(TestClass testClass) {
if (_beforeTestClassFailed.containsKey(testClass.getTypeName())) {
throw new RuntimeException("beforeTestClass() failed on a previous test of test class " + testClass.getTypeName(), _beforeTestClassFailed.get(testClass.getTypeName()));
}
TestInfo testInfo = _testInfos.get(testClass.getTypeName());
if (testInfo == null) {
Integer testsCount = TestClass.getNumberOfInstancesOfTestClassCreated(testClass.getTypeName());
testInfo = new TestInfo(testsCount);
_testInfos.put(testClass.getTypeName(), testInfo);
}
if (testInfo._testsRun == 0) {
_testClassStartTime = System.nanoTime();
_environment.beforeTestClass();
testClass.beforeTestClass();
}
testInfo._testsRun++;
}
private void maybeCallAfterTestClass(TestClass testClass) {
TestInfo testInfo = _testInfos.get(testClass.getTypeName());
if (testInfo != null && testInfo.isAtLastTest()) {
testClass.afterTestClass();
_environment.afterTestClass();
printTestRunTime("Class " + testClass.getTypeName(), (System.nanoTime() - _testClassStartTime));
}
}
private void maybeCallAfterTestSuite(TestClass test) {
TestInfo testInfo = _testInfos.get(test.getTypeName());
if (testInfo != null
&& testInfo.isAtLastTest()
&& isLastTestInSuite(test)) {
_environment.afterTestSuite();
printTestRunTime("Suite", (System.nanoTime() - _suiteStartTimeNs));
}
}
private void printTestRunTime(String msg, long nanoTime) {
if (INCLUDE_TEST_TIMING_INFO) {
System.out.println("***** TestRunTime [" + msg + "] " + nanoTime + " *****");
}
}
private boolean isLastTestInSuite(TestClass test) {
boolean isLastTestInSuite = false;
if (_testWrappers == null) {
isLastTestInSuite = true;
} else if (_testWrappers.size() > 0) {
final String testTypeName = test.getTypeName();
final String testClassName = test.getClass().getName();
final String testWrapperName = _testWrappers.get(_testWrappers.size() - 1).getName();
isLastTestInSuite = testTypeName.equals(testWrapperName) || testClassName.equals(testWrapperName);
}
return isLastTestInSuite;
}
public final boolean hasTimeOut() {
return _suiteTimeoutInMillis != 0L;
}
public final long getTimeoutForCurrentTest() {
long timeSoFar = System.currentTimeMillis() - _suiteStartTime;
return Math.max(1L, _suiteTimeoutInMillis - timeSoFar);
}
final void markTimedOut() {
_suiteHasTimedOut = true;
}
public final boolean hasTimedOut() {
return _suiteHasTimedOut;
}
public static class TestInfo {
public int _testsRun;
private int _testCount;
public TestInfo(int testCount) {
_testCount = testCount;
}
public boolean isAtLastTest() {
return _testsRun == _testCount;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy