package tc.oc.commons.reflect;
import java.io.EOFException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import java.util.function.LongConsumer;
import java.util.function.LongSupplier;
import java.util.function.Supplier;
import com.google.common.reflect.TypeToken;
import junit.framework.TestCase;
import tc.oc.commons.core.reflect.MethodFormException;
import tc.oc.commons.core.reflect.Methods;
import tc.oc.commons.core.util.ThrowingRunnable;
import static tc.oc.test.Assert.*;
public class DynamicLambdaTest extends TestCase {
static void staticRunnable() {}
static String staticFunction(Integer i) {
return String.valueOf(i);
}
public void testStaticMethodAsRunnable() throws Exception {
Methods.lambda(Runnable.class, getClass().getDeclaredMethod("staticRunnable"), null);
}
public void testStaticMethodAsFunction() throws Exception {
final Function<Integer, String> lambda = Methods.lambda(new TypeToken<Function<Integer, String>>(){},
getClass().getDeclaredMethod("staticFunction", Integer.class),
null);
assertEquals("123", lambda.apply(123));
}
public void testInstanceMethodAsRunnable() throws Exception {
final boolean[] flag = new boolean[1];
class C { void runnable() { flag[0] = true; } }
final Runnable lambda = Methods.lambda(Runnable.class, C.class.getDeclaredMethod("runnable"), new C());
lambda.run();
assertTrue(flag[0]);
}
public void testInstanceMethodAsFunction() throws Exception {
class C {
String function(Integer i) {
return String.valueOf(i);
}
}
final Function<Integer, String> lambda = Methods.lambda(new TypeToken<Function<Integer, String>>(){},
C.class.getDeclaredMethod("function", Integer.class),
new C());
assertEquals("123", lambda.apply(123));
}
public void testWidenParameter() throws Exception {
class C { void woot(Object foo) {} }
final Consumer<String> lambda = Methods.lambda(new TypeToken<Consumer<String>>(){}, C.class.getDeclaredMethod("woot", Object.class), new C());
lambda.accept("hi");
}
public void testBoxParameter() throws Exception {
class C { void woot(Integer foo) {} }
final IntConsumer lambda = Methods.lambda(IntConsumer.class, C.class.getDeclaredMethod("woot", Integer.class), new C());
lambda.accept(123);
}
public void testUnboxParameter() throws Exception {
class C { void woot(int foo) {} }
final Consumer<Integer> lambda = Methods.lambda(new TypeToken<Consumer<Integer>>(){}, C.class.getDeclaredMethod("woot", int.class), new C());
lambda.accept(123);
}
public void testPromoteParameter() throws Exception {
class C { void woot(long foo) {} }
final IntConsumer lambda = Methods.lambda(IntConsumer.class, C.class.getDeclaredMethod("woot", long.class), new C());
lambda.accept(123);
}
public void testNarrowParameterThrows() throws Exception {
class C { void woot(String foo) {} }
assertThrows(MethodFormException.class, () ->
Methods.lambda(new TypeToken<Consumer<Object>>(){}, C.class.getDeclaredMethod("woot", String.class), new C())
);
}
public void testDemoteParameterThrows() throws Exception {
class C { void woot(int foo) {} }
assertThrows(MethodFormException.class, () ->
Methods.lambda(LongConsumer.class, C.class.getDeclaredMethod("woot", int.class), new C())
);
}
public void testMissingParameterThrows() throws Exception {
class C { void woot() {} }
assertThrows(MethodFormException.class, () ->
// Missing parameter
Methods.lambda(Consumer.class, C.class.getDeclaredMethod("woot"), new C())
);
}
public void testExtraParameterThrows() throws Exception {
class C { void woot(Object doo) {} }
assertThrows(MethodFormException.class, () ->
Methods.lambda(Runnable.class, C.class.getDeclaredMethod("woot", Object.class), new C())
);
}
public void testWidenReturnType() throws Exception {
class C { String woot() { return null; } }
final Supplier<Object> lambda = Methods.lambda(new TypeToken<Supplier<Object>>(){}, C.class.getDeclaredMethod("woot"), new C());
lambda.get();
}
public void testUnboxReturnType() throws Exception {
class C { Integer woot() { return 123; } }
final IntSupplier lambda = Methods.lambda(IntSupplier.class, C.class.getDeclaredMethod("woot"), new C());
lambda.getAsInt();
}
public void testBoxReturnType() throws Exception {
class C { int woot() { return 123; } }
final Supplier<Integer> lambda = Methods.lambda(new TypeToken<Supplier<Integer>>(){}, C.class.getDeclaredMethod("woot"), new C());
lambda.get();
}
public void testPromoteReturnType() throws Exception {
class C { int woot() { return 123; } }
final LongSupplier lambda = Methods.lambda(LongSupplier.class, C.class.getDeclaredMethod("woot"), new C());
lambda.getAsLong();
}
public void testNarrowReturnTypeThrows() throws Exception {
class C { Object woot() { return new Object(); } }
assertThrows(MethodFormException.class, () ->
Methods.lambda(new TypeToken<Supplier<String>>(){}, C.class.getDeclaredMethod("woot"), new C())
);
}
public void testDemoteReturnTypeThrows() throws Exception {
class C { long woot() { return 123L; } }
assertThrows(MethodFormException.class, () ->
Methods.lambda(IntSupplier.class, C.class.getDeclaredMethod("woot"), new C())
);
}
public void testValidGenericImplementation() throws Exception {
class C<T, R> { R woot(T t) { return null; } }
Methods.lambda(new TypeToken<Function<Number, String>>(){}, C.class.getDeclaredMethod("woot", Object.class), new C<Number, String>(){});
}
public void testInvalidGenericImplementationThrows() throws Exception {
class C<T, R> { R woot(T t) { return null; } }
final TypeToken<Function<Number, String>> samType = new TypeToken<Function<Number, String>>() {};
final Method woot = C.class.getDeclaredMethod("woot", Object.class);
assertThrows(MethodFormException.class, () -> Methods.lambda(samType, woot, new C<String, String>(){})); // bad return type
assertThrows(MethodFormException.class, () -> Methods.lambda(samType, woot, new C<Number, Number>(){})); // bad param type
}
public void testHandledException() throws Exception {
class C { void woot() throws IOException {} }
Methods.lambda(ThrowingRunnable.class, C.class.getDeclaredMethod("woot"), new C()).run();
Methods.lambda(new TypeToken<ThrowingRunnable<IOException>>(){}, C.class.getDeclaredMethod("woot"), new C()).run();
}
public void testUnhandledExceptionThrows() throws Exception {
class C { void woot() throws IOException {} }
assertThrows(MethodFormException.class, () -> Methods.lambda(Runnable.class, C.class.getDeclaredMethod("woot"), new C()));
assertThrows(MethodFormException.class, () -> Methods.lambda(new TypeToken<ThrowingRunnable<EOFException>>(){}, C.class.getDeclaredMethod("woot"), new C()));
}
}