/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.state;
import javax.annotation.*;
import mockit.internal.expectations.*;
import mockit.internal.injection.*;
import mockit.internal.expectations.mocking.*;
import mockit.internal.mockups.*;
import mockit.internal.util.*;
/**
* 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.
*/
public final class TestRun
{
private static final TestRun INSTANCE = new TestRun();
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;
@Nullable private Class<?> currentTestClass;
@Nullable private Object currentTestInstance;
@Nullable private FieldTypeRedefinitions fieldTypeRedefinitions;
@Nullable private TestedClassInstantiations testedClassInstantiations;
@Nonnull private final MockFixture mockFixture = new MockFixture();
@Nonnull private final ExecutingTest executingTest = new ExecutingTest();
@Nonnull private final MockClasses mockClasses = new MockClasses();
// Static "getters" for global state ///////////////////////////////////////////////////////////////////////////////
public static boolean isInsideNoMockingZone() { return noMockingCount.get() > 0; }
@Nullable public static Class<?> getCurrentTestClass() { return INSTANCE.currentTestClass; }
@Nullable public static Object getCurrentTestInstance() { return INSTANCE.currentTestInstance; }
public static int getTestId() { return INSTANCE.testId; }
@Nullable
public static FieldTypeRedefinitions getFieldTypeRedefinitions() { return INSTANCE.fieldTypeRedefinitions; }
@Nullable
public static TestedClassInstantiations getTestedClassInstantiations() { return INSTANCE.testedClassInstantiations; }
@Nonnull public static MockFixture mockFixture() { return INSTANCE.mockFixture; }
@Nonnull public static ExecutingTest getExecutingTest() { return INSTANCE.executingTest; }
@Nullable public static RecordAndReplayExecution getRecordAndReplayForRunningTest()
{
return INSTANCE.executingTest.getCurrentRecordAndReplay();
}
@Nonnull
public static RecordAndReplayExecution getOrCreateRecordAndReplayForRunningTest()
{
return INSTANCE.executingTest.getOrCreateRecordAndReplay();
}
@Nonnull
public static RecordAndReplayExecution getRecordAndReplayForVerifications()
{
return INSTANCE.executingTest.getRecordAndReplayForVerifications();
}
@Nonnull public static MockClasses getMockClasses() { return INSTANCE.mockClasses; }
@Nonnull public static MockStates getMockStates() { return INSTANCE.mockClasses.mockStates; }
// Static "mutators" for global state //////////////////////////////////////////////////////////////////////////////
public static void setCurrentTestClass(@Nullable Class<?> testClass)
{
INSTANCE.currentTestClass = testClass;
}
public static void prepareForNextTest()
{
INSTANCE.testId++;
INSTANCE.executingTest.setRecordAndReplay(null);
}
public static void enterNoMockingZone() { noMockingCount.set(1); }
public static void exitNoMockingZone() { noMockingCount.set(-1); }
public static void clearNoMockingZone() { noMockingCount.remove(); }
public static void clearCurrentTestInstance()
{
INSTANCE.currentTestInstance = null;
}
public static void setRunningIndividualTest(@Nonnull Object testInstance)
{
INSTANCE.currentTestInstance = testInstance;
}
public static void setFieldTypeRedefinitions(@Nullable FieldTypeRedefinitions redefinitions)
{
INSTANCE.fieldTypeRedefinitions = redefinitions;
}
public static void setTestedClassInstantiations(@Nullable TestedClassInstantiations testedClassInstantiations)
{
INSTANCE.testedClassInstantiations = testedClassInstantiations;
}
public static void finishCurrentTestExecution()
{
INSTANCE.executingTest.finishExecution();
}
// Methods to be called only from generated bytecode or from the MockingBridge /////////////////////////////////////
@SuppressWarnings({"StaticMethodOnlyUsedInOneClass", "SimplifiableIfStatement"})
public static boolean updateMockState(
@Nonnull String mockUpClassDesc, @Nullable Object mockedInstance, int mockStateIndex)
{
Object mockUp = getMock(mockUpClassDesc, mockedInstance);
if (mockUp == null) {
return false;
}
if (mockStateIndex < 0) {
return true;
}
return getMockStates().updateMockState(mockUp, mockStateIndex);
}
@Nullable
public static Object getMock(@Nonnull String mockUpClassDesc, @Nullable Object mockedInstance)
{
return INSTANCE.mockClasses.getMock(mockUpClassDesc, mockedInstance);
}
public static void ensureThatClassIsInitialized(@Nonnull Class<?> aClass)
{
boolean previousFlag = INSTANCE.executingTest.setShouldIgnoreMockingCallbacks(true);
try {
Class.forName(aClass.getName(), true, aClass.getClassLoader());
}
catch (ClassNotFoundException ignore) {}
catch (LinkageError e) {
StackTrace.filterStackTrace(e);
e.printStackTrace();
}
finally {
INSTANCE.executingTest.setShouldIgnoreMockingCallbacks(previousFlag);
}
}
}