/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import java.lang.reflect.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.sax.*;
import static java.util.Arrays.*;
import org.junit.*;
import static org.junit.Assert.*;
public final class MockingUpEverythingTest
{
final List<String> traces = new ArrayList<String>();
void traceEntry(Invocation inv) { traces.add("Entered " + getDescription(inv)); }
void traceExit(Invocation inv) { traces.add("Exited " + getDescription(inv)); }
String getDescription(Invocation inv)
{
Member member = inv.getInvokedMember();
String args = Arrays.toString(inv.getInvokedArguments());
Object instance = inv.getInvokedInstance();
return member.getDeclaringClass().getSimpleName() + '#' + member.getName() + " with " + args + " on " + instance;
}
@Test
public void mockEveryMethodInSingleClass()
{
new MockUp<TargetClass>() {
@Mock
Object $advice(Invocation inv)
{
traceEntry(inv);
try {
return inv.proceed();
}
finally {
traceExit(inv);
}
}
@Mock
void validateSomething(Invocation inv)
{
Method m = inv.getInvokedMember();
assertEquals("validateSomething", m.getName());
}
};
TargetClass.staticMethod(123);
final TargetClass tc0 = new TargetClass();
assertEquals(4, tc0.doSomething("test", true));
tc0.performAction(new Runnable() {
@Override public void run() { tc0.doSomething("internal", false); }
@Override public String toString() { return "action"; }
});
TargetClass tc1 = new TargetClass(1);
tc1.performAction(null);
tc1.validateSomething();
List<String> expectedTraces = asList(
"Entered TargetClass#staticMethod with [123] on null",
"Exited TargetClass#staticMethod with [123] on null",
"Entered TargetClass#doSomething with [test, true] on TargetClass0",
"Exited TargetClass#doSomething with [test, true] on TargetClass0",
"Entered TargetClass#performAction with [action] on TargetClass0",
"Entered TargetClass#doSomething with [internal, false] on TargetClass0",
"Exited TargetClass#doSomething with [internal, false] on TargetClass0",
"Exited TargetClass#performAction with [action] on TargetClass0",
"Entered TargetClass#performAction with [null] on TargetClass1",
"Exited TargetClass#performAction with [null] on TargetClass1"
);
assertEquals(expectedTraces, traces);
}
@Test
public void mockEveryMethodInSingleClassWithAdviceOnly()
{
new MockUp<TargetClass>() {
@Mock
Object $advice(Invocation inv)
{
Integer i = inv.proceed();
return i + 2;
}
};
assertEquals(1, new TargetClass().doSomething("", false));
}
@Test
public <B extends TargetClass> void mockEveryMethodInClassHierarchy()
{
new MockUp<B>() {
@Mock
Object $advice(Invocation inv)
{
traceEntry(inv);
try {
return inv.proceed();
}
finally {
traceExit(inv);
}
}
};
final TargetSubclass s1 = new TargetSubclass(1);
assertEquals(4, s1.doSomething("test", true));
assertEquals("123", s1.additionalMethod(123));
s1.performAction(new Runnable() {
@Override public void run() { assertSame(s1, this); }
@Override public String toString() { return "sub-action"; }
});
TargetClass s2 = new TargetClass(2);
s2.performAction(null);
try {
s2.validateSomething();
fail();
}
catch (IllegalArgumentException e) {
assertEquals("Invalid something", e.getMessage());
}
List<String> expectedTraces = asList(
"Entered TargetClass#doSomething with [test, true] on TargetSubclass1",
"Exited TargetClass#doSomething with [test, true] on TargetSubclass1",
"Entered TargetSubclass#additionalMethod with [123] on TargetSubclass1",
"Exited TargetSubclass#additionalMethod with [123] on TargetSubclass1",
"Entered TargetSubclass#performAction with [sub-action] on TargetSubclass1",
"Entered TargetSubclass#additionalMethod with [45] on TargetSubclass1",
"Exited TargetSubclass#additionalMethod with [45] on TargetSubclass1",
"Exited TargetSubclass#performAction with [sub-action] on TargetSubclass1",
"Entered TargetClass#performAction with [null] on TargetClass2",
"Exited TargetClass#performAction with [null] on TargetClass2",
"Entered TargetClass#validateSomething with [] on TargetClass2",
"Exited TargetClass#validateSomething with [] on TargetClass2"
);
assertEquals(expectedTraces, traces);
}
static final class XMLSourceTimingAspect<S extends Source> extends MockUp<S>
{
final Map<String, List<Long>> executionTimes = new HashMap<String, List<Long>>();
@Mock
Object $advice(Invocation invocation)
{
long startTime = System.nanoTime() / 1000000;
try {
return invocation.proceed();
}
finally {
long endTime = System.nanoTime() / 1000000;
long dt = endTime - startTime;
Method invokedMethod = invocation.getInvokedMember();
addMethodExecutionTime(invokedMethod, dt);
}
}
private void addMethodExecutionTime(Method invokedMethod, long executionTime)
{
String methodId = invokedMethod.getName();
List<Long> methodTimes = executionTimes.get(methodId);
if (methodTimes == null) {
methodTimes = new ArrayList<Long>();
executionTimes.put(methodId, methodTimes);
}
methodTimes.add(executionTime);
}
void assertTimes(String methodId, int... expectedTimes)
{
List<Long> actualExecutionTimes = executionTimes.get(methodId);
assertEquals(expectedTimes.length, actualExecutionTimes.size());
for (int i = 0; i < expectedTimes.length; i++) {
long expectedTime = expectedTimes[i];
long executionTime = actualExecutionTimes.get(i);
assertEquals(expectedTime, executionTime, 5);
}
}
}
static void takeSomeTime(int millis)
{
try { Thread.sleep(millis); } catch (InterruptedException ignore) {}
}
static class TestSource implements Source
{
@Override
public void setSystemId(String systemId)
{
takeSomeTime(30);
}
@Override
public String getSystemId()
{
takeSomeTime(20);
return null;
}
}
@Test
public void mockEveryMethodInAllClassesImplementingAnInterface() throws Exception
{
XMLSourceTimingAspect<?> timingAspect = new XMLSourceTimingAspect<Source>();
Source src1 = new TestSource();
src1.setSystemId("Abc");
src1.getSystemId();
Source src2 = new TestSource();
src2.getSystemId();
src2.setSystemId("Gh34");
src2.getSystemId();
// From the JRE, not mocked:
new SAXSource().setSystemId("sax");
new DOMSource().getSystemId();
timingAspect.assertTimes("getSystemId", 20, 20, 20);
timingAspect.assertTimes("setSystemId", 30, 30);
}
public static final class PublicMockUp extends MockUp<TargetClass>
{
@Mock
public static Object $advice(Invocation inv)
{
Object[] args = inv.getInvokedArguments();
if (args.length > 0) {
Integer i = (Integer) args[0];
return -i;
}
return null;
}
}
@Test
public void publicAdviceMethodInPublicMockUpClass()
{
new PublicMockUp();
new TargetClass().validateSomething();
int i = TargetClass.staticMethod(123);
assertEquals(-123, i);
}
}
class TargetClass
{
final int value;
TargetClass() { value = 0; }
TargetClass(int value) { this.value = value; }
public static int staticMethod(int i) { return i; }
int doSomething(String s, boolean b) { return b ? s.length() : -1; }
protected void performAction(Runnable action) { if (action != null) action.run(); }
protected void validateSomething() { throw new IllegalArgumentException("Invalid something"); }
@Override
public String toString() { return getClass().getSimpleName() + value; }
}
final class TargetSubclass extends TargetClass
{
TargetSubclass(int value) { super(value); }
String additionalMethod(int i) { return String.valueOf(i); }
@Override
protected void performAction(Runnable action)
{
additionalMethod(45);
super.performAction(action);
}
}