package openmods.calc; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import com.google.common.base.Optional; import com.google.common.collect.Lists; import java.util.Arrays; import java.util.List; import openmods.calc.types.multi.IConverter; import openmods.calc.types.multi.TypeDomain; import openmods.calc.types.multi.TypedFunction; import openmods.calc.types.multi.TypedFunction.AmbiguousDispatchException; import openmods.calc.types.multi.TypedFunction.DispatchArg; import openmods.calc.types.multi.TypedFunction.DispatchException; import openmods.calc.types.multi.TypedFunction.MultiReturn; import openmods.calc.types.multi.TypedFunction.MultipleReturn; import openmods.calc.types.multi.TypedFunction.NonCompatibleMethodsPresent; import openmods.calc.types.multi.TypedFunction.NonStaticMethodsPresent; import openmods.calc.types.multi.TypedFunction.OptionalArgs; import openmods.calc.types.multi.TypedFunction.RawArg; import openmods.calc.types.multi.TypedFunction.RawDispatchArg; import openmods.calc.types.multi.TypedFunction.RawReturn; import openmods.calc.types.multi.TypedFunction.Variant; import openmods.calc.types.multi.TypedValue; import openmods.reflection.TypeVariableHolderHandler; import openmods.utils.OptionalInt; import org.junit.Assert; import org.junit.Test; import org.mockito.Matchers; import org.mockito.Mockito; public class TypedFunctionTest { static { TypeVariableHolderHandler.initializeClass(TypeDomain.TypeVariableHolders.class); TypeVariableHolderHandler.initializeClass(TypedFunction.class); } private static final TypeDomain domain = new TypeDomain(); static { domain.registerType(Integer.class); domain.registerType(Boolean.class); domain.registerType(String.class); domain.registerType(Number.class); domain.registerCast(Integer.class, Number.class); domain.registerConverter(new IConverter<Boolean, Integer>() { @Override public Integer convert(Boolean value) { return value? 1 : 0; } }); } private static TypedValue wrap(int v) { return domain.create(Integer.class, v); } private static TypedValue wrap(boolean v) { return domain.create(Boolean.class, v); } private static TypedValue wrap(String v) { return domain.create(String.class, v); } private static <T> void assertValueEquals(TypedValue value, Class<? extends T> expectedType, T expectedValue) { Assert.assertEquals(expectedValue, value.value); Assert.assertEquals(expectedType, value.type); Assert.assertEquals(domain, value.domain); } private static void assertValueEquals(TypedValue value, TypedValue expected) { assertValueEquals(value, expected.type, expected.value); } private static TypedValue execute(ICallable<TypedValue> f, TypedValue... values) { return execute(f, OptionalInt.of(values.length), values); } private static TypedValue execute(ICallable<TypedValue> f, OptionalInt argCount, TypedValue... values) { final Frame<TypedValue> frame = FrameFactory.createTopFrame(); frame.stack().pushAll(Arrays.asList(values)); f.call(frame, argCount, OptionalInt.absent()); final TypedValue result = frame.stack().pop(); Assert.assertTrue(frame.stack().isEmpty()); return result; } private static List<TypedValue> execute(ICallable<TypedValue> f, OptionalInt argCount, int rets, TypedValue... values) { final Frame<TypedValue> frame = FrameFactory.createTopFrame(); frame.stack().pushAll(Arrays.asList(values)); f.call(frame, argCount, OptionalInt.of(rets)); List<TypedValue> results = Lists.newArrayList(); for (int i = 0; i < rets; i++) results.add(frame.stack().pop()); Assert.assertTrue(frame.stack().isEmpty()); return Lists.reverse(results); } private static <T> ICallable<TypedValue> createFunction(T target, Class<? extends T> cls) { TypedFunction.Builder builder = TypedFunction.builder(); builder.addVariants(cls); return builder.build(domain, target); } @Test public void testSingleMethodAllMandatoryArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, String b, Number c); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(true); final TypedValue arg2 = wrap("Hello world"); final TypedValue arg3 = wrap(7); Mockito.when(mock.test(anyBoolean(), anyString(), anyInt())).thenReturn(5); assertValueEquals(execute(target, arg1, arg2, arg3), Integer.class, 5); Mockito.verify(mock).test(true, "Hello world", 7); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodArgCast() { abstract class Intf { @Variant public abstract Integer test(Integer a, Number b); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(true); final TypedValue arg2 = wrap(5); Mockito.when(mock.test(anyInt(), any(Number.class))).thenReturn(5); assertValueEquals(execute(target, arg1, arg2), Integer.class, 5); Mockito.verify(mock).test(1, 5); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodMandatoryRawArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, @RawArg TypedValue b, Number c); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(true); final TypedValue arg2 = wrap("Hello world"); final TypedValue arg3 = wrap(7); Mockito.when(mock.test(anyBoolean(), any(TypedValue.class), anyInt())).thenReturn(5); assertValueEquals(execute(target, arg1, arg2, arg3), Integer.class, 5); Mockito.verify(mock).test(true, arg2, 7); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodOptionalArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, @OptionalArgs Optional<String> b, Optional<Number> c); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(true); final TypedValue arg2 = wrap("Hello world"); final TypedValue arg3 = wrap(6); Mockito.when(mock.test(anyBoolean(), Matchers.<Optional<String>> any(), Matchers.<Optional<Number>> any())).thenReturn(7); assertValueEquals(execute(target, arg1, arg2, arg3), Integer.class, 7); Mockito.verify(mock).test(true, Optional.of("Hello world"), Optional.<Number> of(6)); assertValueEquals(execute(target, arg1, arg2), Integer.class, 7); Mockito.verify(mock).test(true, Optional.of("Hello world"), Optional.<Number> absent()); assertValueEquals(execute(target, arg1), Integer.class, 7); Mockito.verify(mock).test(true, Optional.<String> absent(), Optional.<Number> absent()); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodOptionalRawArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, @RawArg @OptionalArgs Optional<TypedValue> b, Optional<Number> c); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(true); final TypedValue arg2 = wrap("Hello world"); final TypedValue arg3 = wrap(6); Mockito.when(mock.test(anyBoolean(), Matchers.<Optional<TypedValue>> any(), Matchers.<Optional<Number>> any())).thenReturn(7); assertValueEquals(execute(target, arg1, arg2, arg3), Integer.class, 7); Mockito.verify(mock).test(true, Optional.of(arg2), Optional.<Number> of(6)); assertValueEquals(execute(target, arg1, arg2), Integer.class, 7); Mockito.verify(mock).test(true, Optional.of(arg2), Optional.<Number> absent()); assertValueEquals(execute(target, arg1), Integer.class, 7); Mockito.verify(mock).test(true, Optional.<TypedValue> absent(), Optional.<Number> absent()); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodAllOptionalArgs() { abstract class Intf { @Variant public abstract Integer test(@OptionalArgs Optional<String> a, Optional<Number> b); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap("Hello world"); final TypedValue arg2 = wrap(6); Mockito.when(mock.test(Matchers.<Optional<String>> any(), Matchers.<Optional<Number>> any())).thenReturn(7); assertValueEquals(execute(target, arg1, arg2), Integer.class, 7); Mockito.verify(mock).test(Optional.of("Hello world"), Optional.<Number> of(6)); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodVariadicArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, Integer... bs); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(false); final TypedValue arg2a = wrap(6); final TypedValue arg2b = wrap(7); Mockito.when(mock.test(anyBoolean(), Matchers.<Integer[]> anyVararg())).thenReturn(5); assertValueEquals(execute(target, arg1), Integer.class, 5); Mockito.verify(mock).test(false); assertValueEquals(execute(target, arg1, arg2a), Integer.class, 5); Mockito.verify(mock).test(false, 6); assertValueEquals(execute(target, arg1, arg2a, arg2b), Integer.class, 5); Mockito.verify(mock).test(false, 6, 7); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodVariadicRawArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, @RawArg TypedValue... bs); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(false); final TypedValue arg2a = wrap(6); final TypedValue arg2b = wrap(7); Mockito.when(mock.test(anyBoolean(), Matchers.<TypedValue[]> anyVararg())).thenReturn(5); assertValueEquals(execute(target, arg1), Integer.class, 5); Mockito.verify(mock).test(false); assertValueEquals(execute(target, arg1, arg2a), Integer.class, 5); Mockito.verify(mock).test(false, arg2a); assertValueEquals(execute(target, arg1, arg2a, arg2b), Integer.class, 5); Mockito.verify(mock).test(false, arg2a, arg2b); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodOptionalAndVariadicArgs() { abstract class Intf { @Variant public abstract Integer test(Boolean a, @OptionalArgs Optional<Number> b, Integer... cs); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue arg1 = wrap(false); final TypedValue arg2 = wrap(5); final TypedValue arg3a = wrap(6); final TypedValue arg3b = wrap(7); Mockito.when(mock.test(anyBoolean(), Matchers.<Optional<Number>> any(), Matchers.<Integer[]> anyVararg())).thenReturn(5); assertValueEquals(execute(target, arg1), Integer.class, 5); Mockito.verify(mock).test(false, Optional.<Number> absent()); assertValueEquals(execute(target, arg1, arg2), Integer.class, 5); Mockito.verify(mock).test(false, Optional.<Number> of(5)); assertValueEquals(execute(target, arg1, arg2, arg3a), Integer.class, 5); Mockito.verify(mock).test(false, Optional.<Number> of(5), 6); assertValueEquals(execute(target, arg1, arg2, arg3a, arg3b), Integer.class, 5); Mockito.verify(mock).test(false, Optional.<Number> of(5), 6, 7); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodRawReturn() { abstract class Intf { @Variant @RawReturn public abstract TypedValue test(); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue ret = wrap("Hello world"); Mockito.when(mock.test()).thenReturn(ret); assertValueEquals(execute(target, OptionalInt.absent()), ret); Mockito.verify(mock).test(); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodMultiReturn() { abstract class Intf { @Variant public abstract MultipleReturn test(); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); final TypedValue ret1 = wrap("Hello world"); final TypedValue ret2 = wrap(7); Mockito.when(mock.test()).thenReturn(MultipleReturn.wrap(ret1, ret2)); Assert.assertEquals(execute(target, OptionalInt.absent(), 2), Arrays.asList(ret1, ret2)); Mockito.verify(mock).test(); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodMultiArrayReturn() { abstract class Intf { @Variant @MultiReturn public abstract Integer[] test(); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test()).thenReturn(new Integer[] { 3, 1, 5 }); Assert.assertEquals(execute(target, OptionalInt.absent(), 3), Arrays.asList(wrap(3), wrap(1), wrap(5))); Mockito.verify(mock).test(); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleMethodMultiIterableReturn() { abstract class Intf { @Variant @MultiReturn public abstract List<String> test(); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test()).thenReturn(Lists.newArrayList("b", "c", "a", "d")); Assert.assertEquals(execute(target, OptionalInt.absent(), 4), Arrays.asList(wrap("b"), wrap("c"), wrap("a"), wrap("d"))); Mockito.verify(mock).test(); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleArgumentDispatch() { abstract class Intf { @Variant public abstract Integer test(@DispatchArg Integer v); @Variant public abstract String test(@DispatchArg String v); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(anyInt())).thenReturn(7); Mockito.when(mock.test(anyString())).thenReturn("b"); assertValueEquals(execute(target, wrap(6)), Integer.class, 7); Mockito.verify(mock).test(6); assertValueEquals(execute(target, wrap("a")), String.class, "b"); Mockito.verify(mock).test("a"); Mockito.verifyNoMoreInteractions(mock); } @Test public void testOneDispatchOneNonDispatchArguments() { abstract class Intf { @Variant public abstract Integer test(@DispatchArg Integer v, Boolean n); @Variant public abstract String test(@DispatchArg String v, Boolean n); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(anyInt(), anyBoolean())).thenReturn(7); Mockito.when(mock.test(anyString(), anyBoolean())).thenReturn("b"); assertValueEquals(execute(target, wrap(6), wrap(true)), Integer.class, 7); Mockito.verify(mock).test(6, true); assertValueEquals(execute(target, wrap("a"), wrap(true)), String.class, "b"); Mockito.verify(mock).test("a", true); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleArgumentDispatchWithExtraEntries() { abstract class Intf { @Variant public abstract Integer test(@DispatchArg(extra = { Boolean.class }) Integer v); @Variant public abstract String test(@DispatchArg String v); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(anyInt())).thenReturn(7); Mockito.when(mock.test(anyString())).thenReturn("b"); assertValueEquals(execute(target, wrap(6)), Integer.class, 7); Mockito.verify(mock).test(6); assertValueEquals(execute(target, wrap(true)), Integer.class, 7); Mockito.verify(mock).test(1); assertValueEquals(execute(target, wrap("a")), String.class, "b"); Mockito.verify(mock).test("a"); Mockito.verifyNoMoreInteractions(mock); } @Test public void testSingleRawArgumentDispatch() { abstract class Intf { @Variant public abstract Integer test(@RawDispatchArg({ Integer.class, Boolean.class }) TypedValue v); @Variant public abstract String test(@DispatchArg String v); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(any(TypedValue.class))).thenReturn(7); Mockito.when(mock.test(anyString())).thenReturn("b"); assertValueEquals(execute(target, wrap(6)), Integer.class, 7); Mockito.verify(mock).test(wrap(6)); assertValueEquals(execute(target, wrap(true)), Integer.class, 7); Mockito.verify(mock).test(wrap(true)); assertValueEquals(execute(target, wrap("a")), String.class, "b"); Mockito.verify(mock).test("a"); Mockito.verifyNoMoreInteractions(mock); } @Test public void testDoubleArgumentDispatch() { abstract class Intf { @Variant public abstract Integer test(@DispatchArg Integer v1, @DispatchArg Boolean v2); @Variant public abstract String test(@DispatchArg String v, @DispatchArg Integer v2); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(anyInt(), anyBoolean())).thenReturn(7); Mockito.when(mock.test(anyString(), anyInt())).thenReturn("b"); assertValueEquals(execute(target, wrap(6), wrap(false)), Integer.class, 7); Mockito.verify(mock).test(6, false); try { execute(target, wrap(6), wrap(5)); Assert.fail(); } catch (DispatchException e) {} assertValueEquals(execute(target, wrap("a"), wrap(5)), String.class, "b"); Mockito.verify(mock).test("a", 5); try { execute(target, wrap("a"), wrap(true)); Assert.fail(); } catch (DispatchException e) {} Mockito.verifyNoMoreInteractions(mock); } @Test public void testDifferentLengthDispatch() { abstract class Intf { @Variant public abstract Integer test(@DispatchArg Integer v1, @DispatchArg Integer v2); @Variant public abstract Integer test(@DispatchArg Integer v1); @Variant public abstract String test(); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(anyInt(), anyInt())).thenReturn(2); Mockito.when(mock.test(anyInt())).thenReturn(1); Mockito.when(mock.test()).thenReturn("zero"); assertValueEquals(execute(target, wrap(1), wrap(2)), Integer.class, 2); Mockito.verify(mock).test(1, 2); assertValueEquals(execute(target, wrap(1)), Integer.class, 1); Mockito.verify(mock).test(1); assertValueEquals(execute(target), String.class, "zero"); Mockito.verify(mock).test(); Mockito.verifyNoMoreInteractions(mock); } @Test public void testOptionalArgumentDispatch() { abstract class Intf { @Variant public abstract Integer test(@OptionalArgs @DispatchArg Optional<Integer> v); @Variant public abstract String test(@DispatchArg String v); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(Matchers.<Optional<Integer>> any())).thenReturn(7); Mockito.when(mock.test(anyString())).thenReturn("b"); assertValueEquals(execute(target, wrap(6)), Integer.class, 7); Mockito.verify(mock).test(Optional.of(6)); assertValueEquals(execute(target), Integer.class, 7); Mockito.verify(mock).test(Optional.<Integer> absent()); assertValueEquals(execute(target, wrap("a")), String.class, "b"); Mockito.verify(mock).test("a"); Mockito.verifyNoMoreInteractions(mock); } @Test public void testDoubleArgumentDispatchMixedMatch() { abstract class Intf { @Variant public abstract Integer test(@DispatchArg Integer v1, @DispatchArg Boolean v2); @Variant public abstract Integer test(@DispatchArg Integer v1, @DispatchArg String v2); @Variant public abstract String test(@DispatchArg String v, @DispatchArg Integer v2); @Variant public abstract String test(@DispatchArg Boolean v, @DispatchArg Integer v2); } final Intf mock = Mockito.mock(Intf.class); ICallable<TypedValue> target = createFunction(mock, Intf.class); Mockito.when(mock.test(anyInt(), anyBoolean())).thenReturn(7); Mockito.when(mock.test(anyInt(), anyString())).thenReturn(8); Mockito.when(mock.test(anyString(), anyInt())).thenReturn("b"); Mockito.when(mock.test(anyBoolean(), anyInt())).thenReturn("c"); assertValueEquals(execute(target, wrap(7), wrap(false)), Integer.class, 7); Mockito.verify(mock).test(7, false); assertValueEquals(execute(target, wrap(8), wrap("c")), Integer.class, 8); Mockito.verify(mock).test(8, "c"); assertValueEquals(execute(target, wrap("a"), wrap(5)), String.class, "b"); Mockito.verify(mock).test("a", 5); assertValueEquals(execute(target, wrap(true), wrap(6)), String.class, "c"); Mockito.verify(mock).test(true, 6); Mockito.verifyNoMoreInteractions(mock); } @Test(expected = AmbiguousDispatchException.class) public void testTwoMethodsNoDispatch() { class Intf { @Variant public Integer test(Integer v) { return null; } @Variant public Integer test(String v) { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = AmbiguousDispatchException.class) public void testTwoMethodsSameDispatch() { class Intf { @Variant public Integer test(Boolean pre, @DispatchArg Integer v, String post) { return null; } @Variant public Integer test(String pre, @DispatchArg Integer v, Integer post) { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = AmbiguousDispatchException.class) public void testTwoMethodsSameDispatchOneRaw() { class Intf { @Variant public Integer test(@DispatchArg Integer v) { return null; } @Variant public Integer test(@RawDispatchArg({ Integer.class }) TypedValue v) { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = AmbiguousDispatchException.class) public void testTwoMethodsSameDispatchExtraDispatch() { class Intf { @Variant public Integer test(Boolean pre, @DispatchArg Integer v, @DispatchArg String post) { return null; } @Variant public Integer test(@DispatchArg String pre, @DispatchArg Integer v, Integer post) { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = AmbiguousDispatchException.class) public void testDispatchConflictOptionalVsArgMissing() { class Intf { @Variant public Integer test(Boolean pre, @OptionalArgs @DispatchArg Optional<Integer> v) { return null; } @Variant public Integer test(String pre, @OptionalArgs @DispatchArg Optional<Boolean> v) { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = AmbiguousDispatchException.class) public void testDispatchConflictOptionalVsMandatorySameType() { class Intf { @Variant public Integer test(@OptionalArgs @DispatchArg Optional<Integer> v) { return null; } @Variant public Integer test(@DispatchArg Integer v) { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = AmbiguousDispatchException.class) public void testOptionalDispatchVsMissingArg() { class Intf { @Variant public Integer test(@OptionalArgs @DispatchArg Optional<Integer> v) { return null; } @Variant public Integer test() { return null; } } createFunction(new Intf(), Intf.class); } @Test(expected = RuntimeException.class) public void testMissingTypesOnRawDispatchArg() { class Intf { @Variant public Integer test(@RawDispatchArg({}) TypedValue v) { return null; } } createFunction(new Intf(), Intf.class); } private static class StaticMethodsTests { @Variant public static String test(@DispatchArg Integer v) { return "int"; } @Variant public static String test(@DispatchArg Boolean v) { return "bool"; } } @Test public void testStaticMethods() { TypedFunction.Builder builder = TypedFunction.builder(); builder.addVariants(StaticMethodsTests.class); final ICallable<TypedValue> function = builder.build(domain, (Object)null); assertValueEquals(execute(function, wrap(false)), wrap("bool")); assertValueEquals(execute(function, wrap(3)), wrap("int")); } @Test(expected = NonStaticMethodsPresent.class) public void testNotStaticMethodsWithNullTarget() { class Intf { @Variant public String test(@DispatchArg Integer v) { return "int"; } } TypedFunction.Builder builder = TypedFunction.builder(); builder.addVariants(Intf.class); builder.build(domain, null); } private interface TestIntfA { @Variant public String test(@DispatchArg Integer v); } private interface TestIntfB { @Variant public String test(@DispatchArg Boolean v); } @Test public void testMethodsFromDifferentInterfaces() { class Test implements TestIntfA, TestIntfB { @Override public String test(Boolean v) { return "bool"; } @Override public String test(Integer v) { return "int"; } } TypedFunction.Builder builder = TypedFunction.builder(); builder.addVariants(TestIntfA.class); builder.addVariants(TestIntfB.class); final ICallable<TypedValue> function = builder.build(domain, new Test()); assertValueEquals(execute(function, wrap(false)), wrap("bool")); assertValueEquals(execute(function, wrap(3)), wrap("int")); } @Test(expected = NonCompatibleMethodsPresent.class) public void testIncompatibleMethodsFromDifferentInterfaces() { class Test implements TestIntfB { @Override public String test(Boolean v) { return "bool"; } } TypedFunction.Builder builder = TypedFunction.builder(); builder.addVariants(TestIntfA.class); builder.addVariants(TestIntfB.class); builder.build(domain, new Test()); } }