/*
* 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.util.*;
import mockit.internal.expectations.*;
import mockit.internal.expectations.invocation.*;
import mockit.internal.expectations.mocking.*;
import mockit.internal.util.*;
@SuppressWarnings({"ClassWithTooManyFields"})
public final class ExecutingTest
{
private RecordAndReplayExecution currentRecordAndReplay;
private RecordAndReplayExecution recordAndReplayForLastTestMethod;
private boolean shouldIgnoreMockingCallbacks;
private ParameterTypeRedefinitions parameterTypeRedefinitions;
private final Map<MockedType, Object> finalLocalMockFields = new HashMap<MockedType, Object>(4);
private final List<Object> injectableMocks = new ArrayList<Object>();
private final Map<Object, Object> originalToCapturedInstance = new IdentityHashMap<Object, Object>(4);
private final List<Object> nonStrictMocks = new ArrayList<Object>();
private final List<Object> strictMocks = new ArrayList<Object>();
private final Map<String, MockedTypeCascade> cascadingTypes = new HashMap<String, MockedTypeCascade>(4);
public final DefaultResults defaultResults = new DefaultResults();
RecordAndReplayExecution getRecordAndReplay(boolean createIfUndefined)
{
if (currentRecordAndReplay == null && createIfUndefined) {
setRecordAndReplay(new RecordAndReplayExecution());
}
return currentRecordAndReplay;
}
public RecordAndReplayExecution getRecordAndReplay()
{
recordAndReplayForLastTestMethod = null;
RecordAndReplayExecution previous = currentRecordAndReplay;
currentRecordAndReplay = null;
return previous;
}
public void setRecordAndReplay(RecordAndReplayExecution newRecordAndReplay)
{
recordAndReplayForLastTestMethod = null;
currentRecordAndReplay = newRecordAndReplay;
}
public boolean isShouldIgnoreMockingCallbacks() { return shouldIgnoreMockingCallbacks; }
public void setShouldIgnoreMockingCallbacks(boolean flag) { shouldIgnoreMockingCallbacks = flag; }
public void clearRecordAndReplayForVerifications()
{
recordAndReplayForLastTestMethod = null;
}
public RecordAndReplayExecution getRecordAndReplayForVerifications()
{
if (currentRecordAndReplay == null) {
if (recordAndReplayForLastTestMethod != null) {
currentRecordAndReplay = recordAndReplayForLastTestMethod;
}
else {
// This should only happen if no expectations at all were created by the whole test, but
// there is one (probably empty) verification block.
currentRecordAndReplay = new RecordAndReplayExecution();
}
}
return currentRecordAndReplay;
}
public ParameterTypeRedefinitions getParameterTypeRedefinitions() { return parameterTypeRedefinitions; }
public void setParameterTypeRedefinitions(ParameterTypeRedefinitions redefinitions)
{ parameterTypeRedefinitions = redefinitions; }
public void clearInjectableAndNonStrictMocks()
{
injectableMocks.clear();
clearNonStrictMocks();
originalToCapturedInstance.clear();
}
public void addInjectableMock(Object mock)
{
if (!isInjectableMock(mock)) {
injectableMocks.add(mock);
}
}
public boolean isInjectableMock(Object mock)
{
for (Object injectableMock : injectableMocks) {
if (mock == injectableMock) {
return true;
}
}
return false;
}
public void addCapturedInstanceForInjectableMock(Object originalInstance, Object capturedInstance)
{
injectableMocks.add(capturedInstance);
addCapturedInstance(originalInstance, capturedInstance);
}
public void addCapturedInstance(Object originalInstance, Object capturedInstance)
{
originalToCapturedInstance.put(capturedInstance, originalInstance);
}
public boolean isInvokedInstanceEquivalentToCapturedInstance(Object invokedInstance, Object capturedInstance)
{
return
invokedInstance == originalToCapturedInstance.get(capturedInstance) ||
capturedInstance == originalToCapturedInstance.get(invokedInstance);
}
public void discardCascadedMockWhenInjectable(Object oldMock)
{
for (int i = 0, n = injectableMocks.size(); i < n; i++) {
if (injectableMocks.get(i) == oldMock) {
injectableMocks.remove(i);
return;
}
}
}
public void addNonStrictMock(Class<?> mockedClass)
{
String mockedClassDesc = mockedClass.getName().replace('.', '/');
String uniqueClassDesc = mockedClassDesc.intern();
if (!containsNonStrictMockedClass(uniqueClassDesc)) {
nonStrictMocks.add(uniqueClassDesc);
}
}
private boolean containsNonStrictMockedClass(Object mockOrClassDesc)
{
for (Object nonStrictMock : nonStrictMocks) {
if (mockOrClassDesc == nonStrictMock) {
return true;
}
}
return false;
}
public void addNonStrictMock(Object mock)
{
if (!containsNonStrictMockedClass(mock)) {
nonStrictMocks.add(mock);
}
addNonStrictMock(mock.getClass());
}
public void addFinalLocalMockField(Object owner, MockedType typeMetadata)
{
finalLocalMockFields.put(typeMetadata, owner);
}
public void addStrictMock(Object mock, String mockClassDesc)
{
addStrictMock(mock);
if (mockClassDesc != null) {
String uniqueMockClassDesc = mockClassDesc.intern();
if (!containsStrictMock(uniqueMockClassDesc) && !containsNonStrictMockedClass(uniqueMockClassDesc)) {
strictMocks.add(uniqueMockClassDesc);
}
}
}
private void addStrictMock(Object mock)
{
if (mock != null && !containsStrictMock(mock)) {
strictMocks.add(mock);
}
}
private boolean containsStrictMock(Object mockOrClass)
{
for (Object strictMock : strictMocks) {
if (mockOrClass == strictMock) {
return true;
}
}
return false;
}
public boolean isNonStrictInvocation(Object mock, String mockClassDesc, String mockNameAndDesc)
{
boolean instanceMethod = isInstanceMethod(mock, mockNameAndDesc);
if (instanceMethod && isOverrideOfObjectMethod(mockNameAndDesc)) {
return true;
}
for (Object nonStrictMock : nonStrictMocks) {
if (!instanceMethod) {
if (nonStrictMock == mockClassDesc) {
return true;
}
}
else if (nonStrictMock == mock) {
return true;
}
}
return false;
}
private boolean isInstanceMethod(Object mock, String mockNameAndDesc)
{
return mock != null && mockNameAndDesc.charAt(0) != '<';
}
private boolean isOverrideOfObjectMethod(String mockNameAndDesc)
{
return "equals(Ljava/lang/Object;)Z hashCode()I toString()Ljava/lang/String;".contains(mockNameAndDesc);
}
public void registerAdditionalMocksFromFinalLocalMockFieldsIfAny()
{
if (!finalLocalMockFields.isEmpty()) {
for (
Iterator<Map.Entry<MockedType, Object>> itr = finalLocalMockFields.entrySet().iterator();
itr.hasNext();
) {
Map.Entry<MockedType, Object> fieldAndOwner = itr.next();
MockedType typeMetadata = fieldAndOwner.getKey();
Object mock = Utilities.getFieldValue(typeMetadata.field, fieldAndOwner.getValue());
// A null field value will occur for invocations executed during initialization of the owner instance.
if (mock != null) {
registerMock(typeMetadata, mock);
itr.remove();
}
}
}
}
public void registerMock(MockedType typeMetadata, Object mock)
{
if (typeMetadata.injectable) {
addInjectableMock(mock);
}
if (typeMetadata.nonStrict) {
addNonStrictMock(mock);
}
}
public boolean isStrictInvocation(Object mock, String mockClassDesc, String mockNameAndDesc)
{
if (isInstanceMethod(mock, mockNameAndDesc) && isOverrideOfObjectMethod(mockNameAndDesc)) {
return false;
}
for (Object strictMock : strictMocks) {
if (strictMock == mock) {
return true;
}
else if (strictMock == mockClassDesc) {
addStrictMock(mock);
return true;
}
}
return false;
}
public void clearNonStrictMocks()
{
finalLocalMockFields.clear();
nonStrictMocks.clear();
}
public void addCascadingType(String mockedTypeDesc, boolean mockFieldFromTestClass)
{
if (!cascadingTypes.containsKey(mockedTypeDesc)) {
cascadingTypes.put(mockedTypeDesc, new MockedTypeCascade(mockFieldFromTestClass));
}
}
public MockedTypeCascade getMockedTypeCascade(String mockedTypeDesc, Object mockInstance)
{
if (cascadingTypes.isEmpty()) {
return null;
}
MockedTypeCascade cascade = cascadingTypes.get(mockedTypeDesc);
if (cascade != null || mockInstance == null) {
return cascade;
}
return getMockedTypeCascade(mockedTypeDesc, mockInstance.getClass());
}
private MockedTypeCascade getMockedTypeCascade(String invokedTypeDesc, Class<?> mockedType)
{
Class<?> typeToLookFor = mockedType;
do {
String typeDesc = typeToLookFor.getName().replace('.', '/');
if (invokedTypeDesc.equals(typeDesc)) {
return null;
}
MockedTypeCascade cascade = cascadingTypes.get(typeDesc);
if (cascade != null) {
return cascade;
}
typeToLookFor = typeToLookFor.getSuperclass();
}
while (typeToLookFor != Object.class);
return null;
}
void finishExecution()
{
recordAndReplayForLastTestMethod = currentRecordAndReplay;
currentRecordAndReplay = null;
if (parameterTypeRedefinitions != null) {
parameterTypeRedefinitions.cleanUp();
parameterTypeRedefinitions = null;
}
clearNonStrictMocks();
strictMocks.clear();
clearNonSharedCascadingTypes();
defaultResults.clear();
}
private void clearNonSharedCascadingTypes()
{
Iterator<MockedTypeCascade> itr = cascadingTypes.values().iterator();
while (itr.hasNext()) {
MockedTypeCascade cascade = itr.next();
if (!cascade.mockFieldFromTestClass) {
itr.remove();
}
}
}
}