/*
* Copyright (c) 2006-2011 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import java.io.*;
import static mockit.Mockit.*;
import static org.junit.Assert.*;
import org.junit.*;
public final class ReentrantMockTest
{
public static class RealClass
{
String foo() { return "real value"; }
}
@MockClass(realClass = RealClass.class)
public static class AnnotatedMockClass
{
private static Boolean fakeIt;
public RealClass it;
@Mock(reentrant = true)
public String foo()
{
if (fakeIt == null) {
throw new IllegalStateException("null fakeIt");
}
else if (fakeIt) {
return "fake value";
}
else {
return it.foo();
}
}
}
@Test
public void callMockMethod()
{
setUpMocks(AnnotatedMockClass.class);
AnnotatedMockClass.fakeIt = true;
String foo = new RealClass().foo();
assertEquals("fake value", foo);
}
@Test
public void callOriginalMethod()
{
setUpMocks(AnnotatedMockClass.class);
AnnotatedMockClass.fakeIt = false;
String foo = new RealClass().foo();
assertEquals("real value", foo);
}
@Test(expected = IllegalStateException.class)
public void calledMockThrowsException()
{
setUpMocks(AnnotatedMockClass.class);
AnnotatedMockClass.fakeIt = null;
new RealClass().foo();
}
@MockClass(realClass = Runtime.class)
public static class MockRuntime
{
public Runtime it;
private int runFinalizationCount;
@Mock(reentrant = true, minInvocations = 3)
public void runFinalization()
{
if (runFinalizationCount < 2) {
it.runFinalization();
}
runFinalizationCount++;
}
@Mock(reentrant = true)
public boolean removeShutdownHook(Thread hook)
{
if (hook == null) {
//noinspection AssignmentToMethodParameter
hook = Thread.currentThread();
}
return it.removeShutdownHook(hook);
}
@Mock(invocations = 1)
public void runFinalizersOnExit(boolean value)
{
assertTrue(value);
}
}
@Test
public void callMockMethodForJREClass()
{
Runtime runtime = Runtime.getRuntime();
setUpMocks(MockRuntime.class);
runtime.runFinalization();
runtime.runFinalization();
runtime.runFinalization();
assertFalse(runtime.removeShutdownHook(null));
//noinspection deprecation
Runtime.runFinalizersOnExit(true);
}
@MockClass(realClass = Runtime.class)
public static class ReentrantMockForNativeMethod
{
@Mock(reentrant = true)
public int availableProcessors() { return 5; }
}
@Test(expected = IllegalArgumentException.class)
public void attemptToSetUpReentrantMockForNativeMethod()
{
setUpMocks(ReentrantMockForNativeMethod.class);
}
@MockClass(realClass = RealClass.class)
static class MultiThreadedMock
{
public RealClass it;
private static boolean nobodyEntered = true;
@Mock(reentrant = true)
public String foo() throws InterruptedException
{
String value = it.foo();
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
{
setUpMocks(MultiThreadedMock.class);
final StringBuilder first = new StringBuilder();
final StringBuilder second = new StringBuilder();
Thread thread1 = new Thread(new Runnable()
{
public void run() { first.append(new RealClass().foo()); }
});
thread1.start();
Thread thread2 = new Thread(new Runnable()
{
public void run() { second.append(new RealClass().foo()); }
});
thread2.start();
thread1.join();
thread2.join();
assertEquals("fake value", first.toString());
assertEquals("fake value", second.toString());
}
static final class RealClass2
{
int firstMethod() { return 1; }
int secondMethod() { return 2; }
}
@Test
public void reentrantMockForNonJREClassWhichCallsAnotherFromADifferentThread()
{
new MockUp<RealClass2>()
{
RealClass2 it;
int value;
@Mock(reentrant = true)
int firstMethod() { return it.firstMethod(); }
@Mock(reentrant = true)
int secondMethod() throws InterruptedException
{
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");
new MockUp<System>()
{
String property;
@Mock(reentrant = true)
String getProperty(String key) { return System.getProperty(key); }
@Mock(reentrant = true)
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>()
{
File it;
@Mock(reentrant = true)
boolean exists() { return !it.exists(); }
};
// Cause the JVM/JRE to load a new class, calling the mocked File#exists() method in the process:
new Runnable() { public void run() {} };
assertTrue(new File("noFile").exists());
}
}