/*
* Copyright (c) 2006-2011 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.state;
import java.lang.reflect.*;
import java.util.*;
import static java.util.Collections.*;
import mockit.internal.annotations.*;
import mockit.internal.capturing.*;
import mockit.internal.expectations.*;
import mockit.internal.expectations.mocking.*;
/**
* A singleton which stores several data structures which in turn hold global state for individual
* test methods, test classes, and for the test run as a whole.
*/
@SuppressWarnings({"ClassWithTooManyFields"})
public final class TestRun
{
private static final TestRun STARTUP_INSTANCE = new TestRun();
private static final Map<ClassLoader, TestRun> INSTANCES = synchronizedMap(new HashMap<ClassLoader, TestRun>());
static
{
INSTANCES.put(ClassLoader.getSystemClassLoader(), STARTUP_INSTANCE);
}
private static TestRun getInstance()
{
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
TestRun instance = INSTANCES.get(contextCL);
// Certain runtime environments (OpenEJB, at least) change the context class loader to a child of the system
// class loader, so we try looking up the parent class loader before creating a new TestRun instance.
if (instance == null) {
instance = INSTANCES.get(contextCL.getParent());
}
if (instance == null) {
instance = new TestRun();
INSTANCES.put(contextCL, instance);
}
return instance;
}
private TestRun() {}
// Fields with global state ////////////////////////////////////////////////////////////////////////////////////////
private static final ThreadLocal<Integer> noMockingCount = new ThreadLocal<Integer>()
{
@Override
protected Integer initialValue() { return 0; }
@Override
public void set(Integer valueToAdd)
{
super.set(get() + valueToAdd);
}
};
// Used only by the Coverage tool:
private int testId;
private Class<?> currentTestClass;
private Object currentTestInstance;
private Method runningTestMethod;
private SavePoint savePointForTestClass;
private CaptureOfImplementationsForTestClass captureOfSubtypes;
private SharedFieldTypeRedefinitions sharedFieldTypeRedefinitions;
private final ProxyClasses proxyClasses = new ProxyClasses();
private final MockFixture mockFixture = new MockFixture();
private final ExecutingTest executingTest = new ExecutingTest();
private final MockClasses mockClasses = new MockClasses();
// Static "getters" for global state ///////////////////////////////////////////////////////////////////////////////
public static boolean isInsideNoMockingZone() { return noMockingCount.get() > 0; }
public static Class<?> getCurrentTestClass() { return getInstance().currentTestClass; }
public static Object getCurrentTestInstance() { return getInstance().currentTestInstance; }
public static int getTestId() { return getInstance().testId; }
public static SavePoint getSavePointForTestClass()
{
return getInstance().savePointForTestClass;
}
public static CaptureOfImplementationsForTestClass getCaptureOfSubtypes()
{
return getInstance().captureOfSubtypes;
}
public static SharedFieldTypeRedefinitions getSharedFieldTypeRedefinitions()
{
return getInstance().sharedFieldTypeRedefinitions;
}
public static ProxyClasses proxyClasses() { return getInstance().proxyClasses; }
public static MockFixture mockFixture() { return getInstance().mockFixture; }
public static ExecutingTest getExecutingTest() { return getInstance().executingTest; }
public static RecordAndReplayExecution getRecordAndReplayForRunningTest(boolean create)
{
TestRun testRun = getInstance();
if (testRun.currentTestInstance == null) {
return null;
}
return testRun.executingTest.getRecordAndReplay(testRun.runningTestMethod != null && create);
}
public static MockClasses getMockClasses() { return getInstance().mockClasses; }
public static void verifyExpectationsOnAnnotatedMocks()
{
getMockClasses().getMockStates().verifyExpectations();
}
// Static "mutators" for global state //////////////////////////////////////////////////////////////////////////////
public static void resetExpectationsOnAnnotatedMocks()
{
getMockClasses().getMockStates().resetExpectations();
}
public static void setCurrentTestClass(Class<?> testClass)
{
getInstance().currentTestClass = testClass;
}
public static void prepareForNextTest()
{
getInstance().testId++;
}
public static void setRunningTestMethod(Method runningTestMethod)
{
TestRun testRun = getInstance();
testRun.runningTestMethod = runningTestMethod;
if (runningTestMethod != null) {
testRun.executingTest.clearRecordAndReplayForVerifications();
}
}
public static void enterNoMockingZone() { noMockingCount.set(1); }
public static void exitNoMockingZone() { noMockingCount.set(-1); }
public static void setRunningIndividualTest(Object testInstance)
{
getInstance().currentTestInstance = testInstance;
}
public static void setSavePointForTestClass(SavePoint savePoint)
{
getInstance().savePointForTestClass = savePoint;
}
public static void setCaptureOfSubtypes(CaptureOfImplementationsForTestClass captureOfSubtypes)
{
getInstance().captureOfSubtypes = captureOfSubtypes;
}
public static void setSharedFieldTypeRedefinitions(SharedFieldTypeRedefinitions redefinitions)
{
getInstance().sharedFieldTypeRedefinitions = redefinitions;
}
public static void finishCurrentTestExecution()
{
TestRun testRun = getInstance();
testRun.runningTestMethod = null;
testRun.executingTest.finishExecution();
}
// Methods to be called only from generated bytecode or from the MockingBridge /////////////////////////////////////
public static Object getMock(int index)
{
return getMockClasses().regularMocks.getMock(index);
}
@SuppressWarnings({"UnusedDeclaration"})
public static Object getStartupMock(int index)
{
return STARTUP_INSTANCE.mockClasses.startupMocks.getMock(index);
}
@SuppressWarnings({"UnusedDeclaration"})
public static Object getMock(Class<?> mockClass, Object mockedInstance)
{
return getMockClasses().regularMocks.getMock(mockClass, mockedInstance);
}
public static boolean updateMockState(String mockClassDesc, int mockIndex)
{
AnnotatedMockStates mockStates = getMockStates(mockClassDesc);
return mockStates.updateMockState(mockClassDesc, mockIndex);
}
private static AnnotatedMockStates getMockStates(String mockClassDesc)
{
AnnotatedMockStates mockStates = getMockClasses().getMockStates();
return mockStates.hasStates(mockClassDesc) ? mockStates : STARTUP_INSTANCE.mockClasses.getMockStates();
}
public static void exitReentrantMock(String mockClassDesc, int mockIndex)
{
AnnotatedMockStates mockStates = getMockStates(mockClassDesc);
mockStates.exitReentrantMock(mockClassDesc, mockIndex);
}
}