package org.testory.proxy; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.testory.proxy.Invocation.invocation; import static org.testory.testing.Fakes.newObject; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; public class TestInvocation { private Object instance, otherInstance; private Method method, otherMethod; private Object argument, otherArgument, argumentA, argumentB; private List<?> arguments; private Object result; private Throwable throwable; private Object original; private Invocation invocation; private Methods methods; private int counter; @Before public void before() throws NoSuchMethodException { instance = newObject("instance"); otherInstance = newObject("otherInstance"); argument = newObject("argument"); otherArgument = newObject("otherArgument"); argumentA = newObject("argumentA"); argumentB = newObject("argumentB"); method = Object.class.getDeclaredMethod("equals", Object.class); arguments = asList(argument); result = newObject("object"); throwable = new RuntimeException("throwable"); } @Test public void constructor_sets_fields() { invocation = invocation(method, instance, arguments); assertEquals(method, invocation.method); assertEquals(arguments, invocation.arguments); assertEquals(instance, invocation.instance); } @Test public void allows_no_arguments() { method = Methods.withParameters(); instance = new Methods(); arguments = Arrays.asList(); invocation(method, instance, arguments); } @Test public void arguments_are_defensive_copied() { method = Methods.withParameters(Object.class); instance = new Methods(); arguments = asList(argumentA); original = new ArrayList<>(arguments); invocation = invocation(method, instance, arguments); ((List<Object>) arguments).set(0, argumentB); assertEquals(original, invocation.arguments); } @Test public void arguments_are_unmodifiable() { method = Methods.withParameters(Object.class); instance = new Methods(); arguments = Arrays.asList(argumentA); invocation = invocation(method, instance, arguments); try { invocation.arguments.set(0, argumentB); fail(); } catch (UnsupportedOperationException e) {} } @Test public void unwraps_argument() { method = Methods.withParameters(int.class); instance = new Methods(); arguments = Arrays.asList(5); invocation(method, instance, arguments); } @Test public void unwraps_and_converts_argument() { method = Methods.withParameters(long.class); instance = new Methods(); arguments = Arrays.asList(5); invocation(method, instance, arguments); } @Test public void argument_is_not_implicitly_converted_to_varargs() { method = Methods.withVarargsParameters(Object[].class); instance = new Methods(); arguments = Arrays.asList(arguments); try { invocation(method, instance, arguments); fail(); } catch (ProxyException e) {} } @Test public void arguments_are_not_implicitly_converted_to_varargs() { method = Methods.withVarargsParameters(Object[].class); instance = new Methods(); arguments = Arrays.asList(argumentA, argumentB); try { invocation(method, instance, arguments); fail(); } catch (ProxyException e) {} } @Test public void no_arguments_is_not_implicitly_converted_to_varargs() { method = Methods.withVarargsParameters(Object[].class); instance = new Methods(); arguments = Arrays.asList(); try { invocation(method, instance, arguments); fail(); } catch (ProxyException e) {} } @Test public void array_argument_is_converted_to_varargs() { method = Methods.withVarargsParameters(Object[].class); instance = new Methods(); arguments = Arrays.asList((Object) new Object[0]); invocation(method, instance, arguments); } @Test public void instance_cannot_be_outside_of_method_declaring_class_hierarchy() { method = Methods.withParameters(); instance = new Object(); arguments = Arrays.asList(""); try { invocation(method, instance, arguments); fail(); } catch (ProxyException e) {} } @Test public void instance_can_extend_method_declaring_class() { method = Methods.withParameters(); instance = new Methods.MethodsSubclass(); arguments = Arrays.asList(); invocation(method, instance, arguments); } @Test public void invokes_invocation() throws Throwable { method = Methods.returningArgument(); methods = new Methods() { Object returningArgument(Object arg) { assertEquals(argument, arg); return counter++; } }; invocation = invocation(method, methods, Arrays.asList(argument)); invocation.invoke(); assertEquals(1, counter); } @Test public void invoke_returns_result() throws Throwable { method = Methods.returningArgument(); methods = new Methods() { Object returningArgument(Object arg) { return result; } }; invocation = invocation(method, methods, Arrays.asList(argument)); assertEquals(result, invocation.invoke()); } @Test public void invoke_throws_throwable() throws Throwable { method = Methods.returningArgument(); methods = new Methods() { Object returningArgument(Object arg) throws Throwable { throw throwable; } }; invocation = invocation(method, methods, Arrays.asList(argument)); try { invocation.invoke(); fail(); } catch (Exception e) { assertEquals(throwable, e); } } @Test public void implements_equals() { method = Methods.withParameters(Object.class); otherMethod = Methods.otherWithParameters(Object.class); instance = new Methods() { public boolean equals(Object obj) { return true; } public int hashCode() { return 0; } }; otherInstance = new Methods() { public boolean equals(Object obj) { return true; } public int hashCode() { return 0; } }; arguments = asList(argument); invocation = invocation(method, instance, arguments); assertTrue(invocation.equals(invocation)); assertTrue(invocation.equals(invocation(method, instance, arguments))); assertTrue(invocation.equals(invocation(method, instance, asList(argument)))); assertFalse(invocation.equals(invocation(otherMethod, instance, arguments))); assertFalse(invocation.equals(invocation(method, otherInstance, arguments))); assertFalse(invocation.equals(invocation(method, instance, asList(otherArgument)))); assertFalse(invocation.equals(new Object())); assertFalse(invocation.equals(null)); assertEquals(invocation.hashCode(), invocation(method, instance, arguments).hashCode()); } @Test public void implements_to_string() { invocation = invocation(method, instance, arguments); assertEquals("invocation(" + method + ", " + instance + ", " + arguments + ")", invocation.toString()); } @Test public void checks_illegal_arguments() throws NoSuchMethodException { failsInvocation( String.class.getDeclaredMethod("valueOf", Object.class), instance, arguments); failsInvocation(null, instance, arguments); failsInvocation(method, null, arguments); failsInvocation(method, instance, null); failsInvocation( Methods.withParameters(Object.class), new Methods(), asList()); failsInvocation( Methods.withParameters(), new Methods(), asList(argument)); failsInvocation( Methods.withParameters(Object.class, Object.class), new Methods(), asList(argument, argumentA, argumentB)); failsInvocation( Methods.withParameters(Object.class, Object.class), new Methods(), asList(argument)); } private static void failsInvocation(Method method, Object instance, List<?> arguments) { try { invocation(method, instance, arguments); fail(); } catch (ProxyException e) {} } @SuppressWarnings("unused") private static class Methods { Object returningArgument(Object arg) throws Throwable { return arg; } public void invoke() {} public void invoke(Object o) {} public void invokeOther(Object o) {} public void invoke(Object a, Object b) {} public void invoke(int o) {} public void invoke(long o) {} public void invoke(Object[] o) {} public void varargs(Object... o) {} public static Method returningArgument() { try { return Methods.class.getDeclaredMethod("returningArgument", Object.class); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public static Method withParameters(Class<?>... parameters) { try { return Methods.class.getDeclaredMethod("invoke", parameters); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public static Method otherWithParameters(Class<?>... parameters) { try { return Methods.class.getDeclaredMethod("invokeOther", parameters); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public static Method subWithParameters(Class<?>... parameters) { try { return MethodsSubclass.class.getDeclaredMethod("invoke", parameters); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public static Method withVarargsParameters(Class<?>... parameters) { try { return Methods.class.getDeclaredMethod("varargs", parameters); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public static class MethodsSubclass extends Methods {} } }