/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import java.io.*;
import org.junit.*;
import static org.junit.Assert.*;
import static mockit.internal.util.Utilities.*;
public final class ReentrantMockTest
{
public static class RealClass
{
public String foo() { return "real value"; }
protected static int staticRecursiveMethod(int i) { return i <= 0 ? 0 : 2 + staticRecursiveMethod(i - 1); }
public int recursiveMethod(int i) { return i <= 0 ? 0 : 2 + recursiveMethod(i - 1); }
protected static int nonRecursiveStaticMethod(int i) { return -i; }
public int nonRecursiveMethod(int i) { return -i; }
}
public static class AnnotatedMockClass extends MockUp<RealClass>
{
private static Boolean fakeIt;
@Mock
public String foo(Invocation inv)
{
if (fakeIt == null) {
throw new IllegalStateException("null fakeIt");
}
else if (fakeIt) {
return "fake value";
}
else {
return inv.proceed();
}
}
}
@Test
public void callMockMethod()
{
new AnnotatedMockClass();
AnnotatedMockClass.fakeIt = true;
String foo = new RealClass().foo();
assertEquals("fake value", foo);
}
@Test
public void callOriginalMethod()
{
new AnnotatedMockClass();
AnnotatedMockClass.fakeIt = false;
String foo = new RealClass().foo();
assertEquals("real value", foo);
}
@Test(expected = IllegalStateException.class)
public void calledMockThrowsException()
{
new AnnotatedMockClass();
AnnotatedMockClass.fakeIt = null;
new RealClass().foo();
}
public static class MockRuntime extends MockUp<Runtime>
{
private int runFinalizationCount;
@Mock
public void runFinalization(Invocation inv)
{
if (runFinalizationCount < 2) {
inv.proceed();
}
runFinalizationCount++;
}
@Mock
public boolean removeShutdownHook(Invocation inv, Thread hook)
{
if (hook == null) {
//noinspection AssignmentToMethodParameter
hook = Thread.currentThread();
}
return inv.proceed(hook);
}
@Mock
public void runFinalizersOnExit(boolean value)
{
assertTrue(value);
}
}
@Test
public void callMockMethodForJREClass()
{
Runtime runtime = Runtime.getRuntime();
new MockRuntime();
runtime.runFinalization();
runtime.runFinalization();
runtime.runFinalization();
assertFalse(runtime.removeShutdownHook(null));
//noinspection deprecation
Runtime.runFinalizersOnExit(true);
}
public static class ReentrantMockForNativeMethod extends MockUp<Runtime>
{
@Mock
public int availableProcessors(Invocation inv)
{
assertNotNull(inv.getInvokedInstance());
return 5;
}
}
@Test
public void setUpReentrantMockForNativeJREMethod()
{
new ReentrantMockForNativeMethod();
assertEquals(5, Runtime.getRuntime().availableProcessors());
}
static class MultiThreadedMock extends MockUp<RealClass>
{
private static boolean nobodyEntered = true;
@Mock
public String foo(Invocation inv) throws InterruptedException
{
String value = inv.proceed();
synchronized (MultiThreadedMock.class) {
if (nobodyEntered) {
nobodyEntered = false;
//noinspection WaitNotInLoop
MultiThreadedMock.class.wait(5000);
}
else {
MultiThreadedMock.class.notifyAll();
}
}
return value.replace("real", "fake");
}
}
@Test(timeout = 1000)
public void twoConcurrentThreadsCallingTheSameReentrantMock() throws Exception
{
new MultiThreadedMock();
final StringBuilder first = new StringBuilder();
final StringBuilder second = new StringBuilder();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() { first.append(new RealClass().foo()); }
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() { second.append(new RealClass().foo()); }
});
thread2.start();
thread1.join();
thread2.join();
assertEquals("fake value", first.toString());
assertEquals("fake value", second.toString());
}
public static final class RealClass2
{
public int firstMethod() { return 1; }
public int secondMethod() { return 2; }
}
@Test
public void reentrantMockForNonJREClassWhichCallsAnotherFromADifferentThread()
{
new MockUp<RealClass2>() {
int value;
@Mock
int firstMethod(Invocation inv) { return inv.proceed(); }
@Mock
int secondMethod(Invocation inv) throws InterruptedException
{
final RealClass2 it = inv.getInvokedInstance();
Thread t = new Thread() {
@Override
public void run() { value = it.firstMethod(); }
};
t.start();
t.join();
return value;
}
};
RealClass2 r = new RealClass2();
assertEquals(1, r.firstMethod());
assertEquals(1, r.secondMethod());
}
@Test
public void reentrantMockForJREClassWhichCallsAnotherFromADifferentThread()
{
System.setProperty("a", "1");
System.setProperty("b", "2");
if (HOTSPOT_VM) { // causes main thread to hang up on IBM JRE
new MockUp<System>()
{
String property;
@Mock
String getProperty(Invocation inv, String key) { return inv.proceed(); }
@Mock
String clearProperty(final String key) throws InterruptedException
{
Thread t = new Thread()
{
@Override
public void run() { property = System.getProperty(key); }
};
t.start();
t.join();
return property;
}
};
}
assertEquals("1", System.getProperty("a"));
assertEquals("2", System.clearProperty("b"));
}
@Test
public void mockFileAndForceJREToCallReentrantMockedMethod()
{
new MockUp<File>() {
@Mock
boolean exists(Invocation inv) { boolean exists = inv.proceed(); return !exists; }
};
// Cause the JVM/JRE to load a new class, calling the mocked File#exists() method in the process:
new Runnable() { @Override public void run() {} };
assertTrue(new File("noFile").exists());
}
public static final class RealClass3
{
public RealClass3 newInstance() { return new RealClass3(); }
}
@Test
public void reentrantMockForMethodWhichInstantiatesAndReturnsNewInstanceOfTheMockedClass()
{
new MockUp<RealClass3>() {
@Mock
RealClass3 newInstance(Invocation inv) { return null; }
};
assertNull(new RealClass3().newInstance());
}
public static final class MockClassWithReentrantMockForRecursiveMethod extends MockUp<RealClass>
{
@Mock
int recursiveMethod(Invocation inv, int i) { int j = inv.proceed(); return 1 + j; }
@Mock
static int staticRecursiveMethod(Invocation inv, int i) { int j = inv.proceed(); return 1 + j; }
}
@Test
public void reentrantMockMethodForRecursiveMethods()
{
assertEquals(0, RealClass.staticRecursiveMethod(0));
assertEquals(2, RealClass.staticRecursiveMethod(1));
RealClass r = new RealClass();
assertEquals(0, r.recursiveMethod(0));
assertEquals(2, r.recursiveMethod(1));
new MockClassWithReentrantMockForRecursiveMethod();
assertEquals(1, RealClass.staticRecursiveMethod(0));
assertEquals(1 + 2 + 1, RealClass.staticRecursiveMethod(1));
assertEquals(1, r.recursiveMethod(0));
assertEquals(4, r.recursiveMethod(1));
}
@Test
public void mockUpThatProceedsIntoRecursiveMethod()
{
RealClass r = new RealClass();
assertEquals(0, r.recursiveMethod(0));
assertEquals(2, r.recursiveMethod(1));
new MockUp<RealClass>() {
@Mock
int recursiveMethod(Invocation inv, int i)
{
int ret = inv.proceed();
return 1 + ret;
}
};
assertEquals(1, r.recursiveMethod(0));
assertEquals(4, r.recursiveMethod(1));
}
@Test
public void recursiveMockMethodWithoutInvocationParameter()
{
new MockUp<RealClass>() {
@Mock
int nonRecursiveStaticMethod(int i)
{
if (i > 1) return i;
return RealClass.nonRecursiveStaticMethod(i + 1);
}
};
int result = RealClass.nonRecursiveStaticMethod(1);
assertEquals(2, result);
}
@Test
public void recursiveMockMethodWithInvocationParameterNotUsedForProceeding()
{
new MockUp<RealClass>() {
@Mock
int nonRecursiveMethod(Invocation inv, int i)
{
if (i > 1) return i;
RealClass it = inv.getInvokedInstance();
return it.nonRecursiveMethod(i + 1);
}
};
int result = new RealClass().nonRecursiveMethod(1);
assertEquals(2, result);
}
@Test
public void nonRecursiveMockMethodWithInvocationParameterUsedForProceeding()
{
new MockUp<RealClass>() {
@Mock
int nonRecursiveMethod(Invocation inv, int i)
{
if (i > 1) return i;
return inv.proceed(i + 1);
}
};
int result = new RealClass().nonRecursiveMethod(1);
assertEquals(-2, result);
}
@Test
public void recursiveDelegateMethodWithoutInvocationParameter()
{
new Expectations(RealClass.class) {{
RealClass.nonRecursiveStaticMethod(anyInt);
result = new Delegate() {
@Mock
int delegate(int i)
{
if (i > 1) return i;
return RealClass.nonRecursiveStaticMethod(i + 1);
}
};
}};
int result = RealClass.nonRecursiveStaticMethod(1);
assertEquals(2, result);
}
@Test
public void recursiveDelegateMethodWithInvocationParameterNotUsedForProceeding(@Injectable final RealClass rc)
{
new Expectations() {{
rc.nonRecursiveMethod(anyInt);
result = new Delegate() {
@Mock
int delegate(Invocation inv, int i)
{
if (i > 1) return i;
RealClass it = inv.getInvokedInstance();
return it.nonRecursiveMethod(i + 1);
}
};
}};
int result = rc.nonRecursiveMethod(1);
assertEquals(2, result);
}
@Test
public void nonRecursiveDelegateMethodWithInvocationParameterUsedForProceeding(@Injectable final RealClass rc)
{
new Expectations() {{
rc.nonRecursiveMethod(anyInt);
result = new Delegate() {
@Mock
int nonRecursiveMethod(Invocation inv, int i)
{
if (i > 1) return i;
return inv.proceed(i + 1);
}
};
}};
int result = rc.nonRecursiveMethod(1);
assertEquals(-2, result);
}
}