package org.testory.plumbing.mock; import static java.util.Objects.deepEquals; import static org.testory.plumbing.PlumbingException.check; import static org.testory.plumbing.Stubbing.stubbing; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import org.testory.plumbing.Maker; import org.testory.plumbing.Stubbing; import org.testory.plumbing.history.History; import org.testory.proxy.Handler; import org.testory.proxy.Invocation; import org.testory.proxy.InvocationMatcher; public class SaneMockMaker implements Maker { private final Maker mockMaker; private final History history; private SaneMockMaker(Maker mockMaker, History history) { this.mockMaker = mockMaker; this.history = history; } public static Maker sane(Maker mockMaker, History history) { check(mockMaker != null); check(history != null); return new SaneMockMaker(mockMaker, history); } public <T> T make(Class<T> type, String name) { check(type != null); check(name != null); T mock = mockMaker.make(type, name); history .add(stubbingEquals(mock, name)) .add(stubbingHashCode(mock, name)) .add(stubbingToString(mock, name)); if (Throwable.class.isAssignableFrom(type)) { history.add(stubbingFillInStackTrace(mock)); history.add(stubbingPrintStackTrace(mock)); history.add(stubbingPrintStackTracePrintStream(mock)); history.add(stubbingPrintStackTracePrintWriter(mock)); } return mock; } private static Stubbing stubbingEquals(final Object mock, String name) { return stubbing(onInvocation(mock, "equals", Object.class), new Handler() { public Object handle(Invocation invocation) { return mock == invocation.arguments.get(0); } }); } private static Stubbing stubbingHashCode(Object mock, final String name) { return stubbing(onInvocation(mock, "hashCode"), new Handler() { public Object handle(Invocation invocation) { return name.hashCode(); } }); } private static Stubbing stubbingToString(Object mock, final String name) { return stubbing(onInvocation(mock, "toString"), new Handler() { public Object handle(Invocation invocation) { return name; } }); } private static Stubbing stubbingFillInStackTrace(Object mock) { return stubbing(onInvocation(mock, "fillInStackTrace"), new Handler() { public Object handle(Invocation invocation) { return invocation.instance; } }); } private static Stubbing stubbingPrintStackTrace(Object mock) { return stubbing(onInvocation(mock, "printStackTrace"), new Handler() { public Object handle(Invocation invocation) throws IOException { System.err.write(invocation.instance.toString().getBytes()); return null; } }); } private static Stubbing stubbingPrintStackTracePrintStream(Object mock) { return stubbing(onInvocation(mock, "printStackTrace", PrintStream.class), new Handler() { public Object handle(Invocation invocation) throws IOException { PrintStream printStream = (PrintStream) invocation.arguments.get(0); printStream.write(invocation.instance.toString().getBytes()); return null; } }); } private static Stubbing stubbingPrintStackTracePrintWriter(Object mock) { return stubbing(onInvocation(mock, "printStackTrace", PrintWriter.class), new Handler() { public Object handle(Invocation invocation) { PrintWriter printWriter = (PrintWriter) invocation.arguments.get(0); printWriter.write(invocation.instance.toString()); return null; } }); } private static InvocationMatcher onInvocation( final Object instance, final String methodName, final Class<?>... parameters) { return new InvocationMatcher() { public boolean matches(Invocation invocation) { return invocation.instance == instance && deepEquals(invocation.method.getName(), methodName) && deepEquals(invocation.method.getParameterTypes(), parameters); } }; } }