mockit.internal.mockups.MockState Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for developer (unit/integration) testing.
It contains mocking APIs and other tools, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006-2013 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.mockups;
import java.lang.reflect.*;
import org.jetbrains.annotations.*;
import mockit.internal.*;
import mockit.internal.util.*;
import mockit.internal.mockups.MockMethods.MockMethod;
final class MockState
{
@NotNull final MockMethod mockMethod;
@Nullable private RealMethod realMethod;
@Nullable private Method actualMockMethod;
// Expectations on the number of invocations of the mock as specified by the @Mock annotation,
// initialized with the default values as specified in @Mock annotation definition:
int expectedInvocations = -1;
int minExpectedInvocations;
int maxExpectedInvocations = -1;
// Current mock invocation state:
private int invocationCount;
@Nullable private ThreadLocal onReentrantCall;
// Helper field just for synchronization:
@NotNull private final Object invocationCountLock = new Object();
MockState(@NotNull MockMethod mockMethod) { this.mockMethod = mockMethod; }
@NotNull Class> getRealClass() { return mockMethod.getRealClass(); }
boolean isReentrant() { return onReentrantCall != null; }
void makeReentrant()
{
onReentrantCall = new ThreadLocal() {
@Override
protected Boolean initialValue() { return false; }
};
}
boolean isWithExpectations()
{
return
expectedInvocations >= 0 || minExpectedInvocations > 0 || maxExpectedInvocations >= 0 ||
mockMethod.hasInvocationParameter;
}
void update()
{
synchronized (invocationCountLock) {
invocationCount++;
}
if (onReentrantCall != null) {
onReentrantCall.set(true);
}
}
boolean isOnReentrantCall() { return onReentrantCall != null && onReentrantCall.get(); }
void exitReentrantCall() { if (onReentrantCall != null) onReentrantCall.set(false); }
void verifyExpectations()
{
int timesInvoked = getTimesInvoked();
if (expectedInvocations >= 0 && timesInvoked != expectedInvocations) {
String message = mockMethod.errorMessage("exactly", expectedInvocations, timesInvoked);
throw timesInvoked < expectedInvocations ?
new MissingInvocation(message) : new UnexpectedInvocation(message);
}
else if (timesInvoked < minExpectedInvocations) {
throw new MissingInvocation(mockMethod.errorMessage("at least", minExpectedInvocations, timesInvoked));
}
else if (maxExpectedInvocations >= 0 && timesInvoked > maxExpectedInvocations) {
throw new UnexpectedInvocation(mockMethod.errorMessage("at most", maxExpectedInvocations, timesInvoked));
}
}
int getMinInvocations() { return expectedInvocations >= 0 ? expectedInvocations : minExpectedInvocations; }
int getMaxInvocations() { return expectedInvocations >= 0 ? expectedInvocations : maxExpectedInvocations; }
int getTimesInvoked()
{
synchronized (invocationCountLock) {
return invocationCount;
}
}
void reset()
{
synchronized (invocationCountLock) {
invocationCount = 0;
}
}
@NotNull RealMethod getRealMethod()
{
if (realMethod == null) {
if (mockMethod.isForNativeMethod()) {
throw new UnsupportedOperationException("Cannot proceed into real implementation of native method");
}
realMethod = new RealMethod(getRealClass(), mockMethod.name, mockMethod.mockedMethodDesc);
}
return realMethod;
}
@NotNull Method getMockMethod(@NotNull Class> mockClass, @NotNull Class>[] paramTypes)
{
if (actualMockMethod == null) {
actualMockMethod = MethodReflection.findCompatibleMethod(mockClass, mockMethod.name, paramTypes);
}
return actualMockMethod;
}
@Override
public boolean equals(@NotNull Object other) { return mockMethod.equals(((MockState) other).mockMethod); }
@Override
public int hashCode() { return mockMethod.hashCode(); }
}