package org.enumerable.lambda; import org.enumerable.lambda.annotation.LambdaLocal; import org.enumerable.lambda.annotation.LambdaParameter; import org.enumerable.lambda.annotation.NewLambda; import org.enumerable.lambda.exception.LambdaWeavingNotEnabledException; import org.enumerable.lambda.primitives.Fn1ItoB; import org.enumerable.lambda.primitives.Fn1ItoI; import org.junit.Test; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; import java.util.Timer; import java.util.concurrent.Callable; import static java.lang.Thread.sleep; import static org.enumerable.lambda.Lambda.delegate; import static org.enumerable.lambda.Lambda.λ; import static org.enumerable.lambda.Parameters.*; import static org.enumerable.lambda.primitives.LambdaPrimitives.λ; import static org.junit.Assert.*; public class LambdaTest extends TestBase { @Test public void lambdaWithOnePrimitiveArgument() throws Exception { Fn1<Integer, Integer> nTimesTwo = λ(n, n * 2); assertTrue(nTimesTwo instanceof Fn1ItoI); assertEquals(4, (int) nTimesTwo.call(2)); } @Test public void creatingLambdaWithNoArguments() throws Exception { Fn0<String> hello = λ("hello"); assertEquals("hello", hello.call()); } @Test public void creatingLambdaWithNoExpression() throws Exception { Fn0<?> none = λ(); assertNull(none.call()); } @Test public void nestedLambdas() throws Exception { int four = λ(n, λ(n, n * n).call(n)).call(2); assertEquals(4, four); } @Test public void nestedNestedLambdas() throws Exception { int eight = λ(n, λ(n, n * λ(n, n * n).call(n)).call(n)).call(2); assertEquals(8, eight); } @Test public void nestedLambdasClosingOverLocalVariable() throws Exception { int two = 2; int eight = λ(n, λ(n, n * two).call(n)).call(4); assertEquals(8, eight); } @Test public void nestedNestedLambdasClosingOverLocalVariable() throws Exception { int two = 2; int four = 4; int thirtytwo = λ(n, λ(n, two * n * λ(n, n * four).call(n)).call(n)).call(2); assertEquals(32, thirtytwo); } @Test public void nestedLambdasShadowingParentLambdaParameter() throws Exception { int sixteen = λ(n, λ(n, n * n).call(n * 2)).call(2); assertEquals(16, sixteen); } @Test public void nestedLambdasClosingOverParentLambdaParameter() throws Exception { int four = λ(n, λ(m, n * m).call(n)).call(2); assertEquals(4, four); } @Test public void nestedNestedLambdasClosingOverParentLambdaParameter() throws Exception { int two = 2; int four = 4; int thirtytwo = λ(n, λ(m, two * n * λ(m * four).call()).call(n)).call(2); assertEquals(32, thirtytwo); } @Test public void nestedZeroArgumentLambdasInOneArgumentLambda() throws Exception { int four = λ(n, λ(4).call()).call(0); assertEquals(4, four); } @Test public void nestedZeroArgumentLambdas() throws Exception { int four = λ(λ(4).call()).call(); assertEquals(4, four); } @Test public void nestedLambdasClosingOverMutableParentLambdaParameter() throws Exception { int eight = λ(n, λ(m, n = n * m).call(n) + n).call(2); assertEquals(8, eight); } @Test public void nestedNesterLambdasClosingOverMutableParentLambdaParameter() throws Exception { int eight = λ(n, λ(m, λ(n = n * m).call()).call(n) + n).call(2); assertEquals(8, eight); } @LambdaParameter static String a1, a2; @Test public void nestedLambdasClosingOverObjectLambdaParameters() throws Exception { assertEquals("helloworld", λ(a1, λ(a2, a1 + a2).call("world")).call("hello")); } @Test public void applyWithOneArgument() throws Exception { Fn1<Integer, Integer> nTimesTwo = λ(n, n * 2); assertEquals(4, (int) nTimesTwo.apply(2)); assertEquals(8, (int) nTimesTwo.apply(new Object[] { 4 })); } @Test public void applyWithTwoArguments() throws Exception { Fn2<Integer, Integer, Integer> nTimesMtimesTwo = λ(n, m, n * m * 2); assertEquals(8, (int) nTimesMtimesTwo.apply(2, 2)); assertEquals(64, (int) nTimesMtimesTwo.apply(new Object[] { 4, 8 })); } @Test public void applyWithThreeArguments() throws Exception { Fn3<String, Integer, Integer, String> addWithPrefixString = λ(s, n, m, s + (n + m)); assertEquals("total: 4", addWithPrefixString.apply("total: ", 2, 2)); assertEquals("total: 12", (addWithPrefixString.apply(new Object[] { "total: ", 4, 8 }))); } @Test(expected = NullPointerException.class) public void applyWithOneArgumentWhenTwoAreUsedMayThrowException() throws Exception { Fn2<Integer, Integer, Integer> nTimesMtimesTwo = λ(n, m, n * m * 2); nTimesMtimesTwo.apply(2); } @Test(expected = NullPointerException.class) public void applyWithTwoArgumentsWhenThreeAreUsedMayThrowException() throws Exception { Fn3<String, Integer, Integer, String> nTimesMtimesTwoPlusS = λ(s, n, m, n * m * 2 + s); nTimesMtimesTwoPlusS.apply(2, 4); } @Test public void applyWithTwoArgumentsWhenThirdArgumentIsNotUsed() throws Exception { Fn3<Integer, Integer, String, Integer> nTimesMtimesTwo = λ(n, m, s, n * m); assertEquals(8, (int) nTimesMtimesTwo.apply(2, 4)); } @Test public void applyWithOneArgumentWhenSecondArgumentIsNotUsed() throws Exception { Fn2<Integer, Object, Integer> firstArgument = λ(n, obj, n); assertEquals(2, (int) firstArgument.apply(2)); } @Test public void applyWithOneArgumentSetsSecondArgumentToNull() throws Exception { Fn2<String, String, String> secondArgument = λ(s, t, t); assertNull(secondArgument.apply("hello")); } @Test(expected = NullPointerException.class) public void applyWithNoArgumentWhenOneIsUsedMayThrowException() throws Exception { Fn1<Integer, Integer> inc = λ(n, n + 1); inc.apply(); } @Test public void applyWithNoArgumentWhenArgumentIsNotUsed() throws Exception { Fn0<Integer> firstArgument = λ(2); assertEquals(2, (int) firstArgument.apply()); } @Test(expected = NullPointerException.class) public void applyWithNoArgumentWhenArgumentIsPrimitiveThrowsNullPointerException() throws Exception { Fn0<Integer> firstArgument = λ(n, 2); assertEquals(2, (int) firstArgument.apply()); } @Test public void applyWithNoArgumentSetsArgumentsToNull() throws Exception { Fn1<String, String> firstArgument = λ(s, s); assertNull(firstArgument.apply()); Fn2<String, String, String> secondArgument = λ(s, t, t); assertNull(secondArgument.apply()); Fn3<Object, String, String, String> thirdArgument = λ(obj, s, t, t); assertNull(thirdArgument.apply()); } @Test public void defaultValueForSecondArgument() throws Exception { Fn2<Integer, Integer, Integer> nTimesM = λ(n, m = 3, n * m); assertEquals(4, (int) nTimesM.call(2, 2)); assertEquals(6, (int) nTimesM.call(2)); } @Test public void defaultComputatedValueForSecondArgument() throws Exception { Fn2<Integer, Integer, Integer> nTimesM = λ(n, m = 3 * 2, n * m); assertEquals(4, (int) nTimesM.call(2, 2)); assertEquals(12, (int) nTimesM.call(2)); } @Test public void defaultReferenceValueForSecondArgument() throws Exception { Fn2<Integer, String, String> nWithPrefix = λ(n, s = "", s + n); assertEquals("prefix 2", nWithPrefix.call(2, "prefix ")); assertEquals("2", nWithPrefix.call(2)); } @Test public void defaultValueForThirdArgument() throws Exception { Fn3<String, Integer, Integer, String> addWithPrefixString = λ(s, n, m = 4, s + (n + m)); assertEquals("total: 4", addWithPrefixString.call("total: ", 2, 2)); assertEquals("total: 6", addWithPrefixString.call("total: ", 2)); } @Test public void defaultValueForSecondAndThirdArgument() throws Exception { Fn3<String, Integer, Integer, String> addWithPrefixString = λ(s, n = 8, m = 4, s + (n + m)); assertEquals("total: 4", addWithPrefixString.call("total: ", 2, 2)); assertEquals("total: 6", addWithPrefixString.call("total: ", 2)); assertEquals("total: 12", addWithPrefixString.call("total: ")); } @Test public void arities() throws Exception { assertEquals(0, λ().arity()); assertEquals(0, λ(null).arity()); assertEquals(1, λ(n, null).arity()); assertEquals(2, λ(n, m, null).arity()); assertEquals(3, λ(n, m, s, null).arity()); } @Test public void assignLambdaParameter() throws Exception { assertEquals(1, λ(n, n = 1).call(5)); } @Test public void incrementLambdaParameter() throws Exception { assertEquals(1, λ(n, ++n).call(0)); } @LambdaParameter static int[] ints; @Test public void callLambdaWithArrayArgument() throws Exception { Fn3<int[], Integer, Integer, Integer> storeInArray = λ(ints, idx, n, ints[idx] = n); int[] array = { 2 }; storeInArray.call(array, 0, 4); assertEquals(4, array[0]); } @Test public void callLambdaInLambda() throws Exception { Fn1<Integer, Integer> timesTwo = λ(n, n * 2); assertEquals(6, λ(n, m, timesTwo.call(n) + m).call(2, 2)); } @LambdaParameter static Fn1<String, String> stringToString; @Test public void callLambdaWithLambdaArgument() throws Exception { Fn1<String, String> toUpperCase = λ(s, s.toUpperCase()); assertEquals("HELLO", λ(stringToString, stringToString.call("hello")).call(toUpperCase)); } Fn1<Integer, Integer> fib; @Test public void recursion() throws Exception { fib = λ(n, n <= 1 ? n : fib.call(n - 1) + fib.call(n - 2)); assertEquals(55, (int) fib.call(10)); } Fn0<?> self; @Test public void returningThisOfLambda() throws Exception { self = λ(self); assertSame(self, self.call()); } @Test public <R> void returnAnonymousInnerClassFromLambda() throws Exception { Fn0<? extends Callable<String>> returnsCallable = λ(new Callable<String>() { public String call() throws Exception { return "hello"; } }); Callable<String> callable = returnsCallable.call(); assertEquals("hello", callable.call()); assertNotSame(callable, returnsCallable.call()); } @NewLambda static Runnable runnable(Object block) { throw new LambdaWeavingNotEnabledException(); } @Test public void createSingleMethodInterfaceUsingNewLambda() throws Exception { String hello = ""; Runnable runnable = runnable(hello = "hello"); runnable.run(); assertEquals("hello", hello); assertFalse(runnable instanceof Fn0<?>); } @NewLambda static ActionListener action(ActionEvent e, Object block) { throw new LambdaWeavingNotEnabledException(); } @Test public void createSingleMethodInterfaceTakingOneArgumentUsingNewLambda() throws Exception { ActionEvent actual; ActionEvent event = new ActionEvent(this, 1, "command"); action(e, actual = e).actionPerformed(event); assertSame(event, actual); } @Test public void createSingleMethodInterfaceUsingGenereicDelegate() throws Exception { String hello = ""; Runnable runnable = delegate(hello = "hello"); runnable.run(); assertEquals("hello", hello); assertFalse(runnable instanceof Fn0<?>); } @Test public void createSingleMethodInterfaceTakingOneArgumentUsingGenericDelegate() throws Exception { ActionEvent actual; ActionListener a = delegate(e, actual = e); ActionEvent event = new ActionEvent(this, 1, "command"); a.actionPerformed(event); assertSame(event, actual); } @Test public void createSingleMethodInterfaceTakingTwoArgumentsAndReturningPrimitiveUsingGenericDelegate() throws Exception { Comparator<Integer> c = delegate(n, m, m - n); List<Integer> list = list(1, 2, 3); Collections.sort(list, c); assertEquals(list(3, 2, 1), list); } static interface TakesAndReturnsPrimtive { public double toDouble(int i); } @Test public void createSingleMethodInterfaceWithPrimtiveArgumentAndReturnUsingGenericDelagate() throws Exception { TakesAndReturnsPrimtive t = delegate(n, n); assertEquals(2.0, t.toDouble(2), 0); } static abstract class SingleAbstractMethodNoArgumentsClass { public abstract String getMessage(); } @Test public void createSingleMethodWithNoArgumentsClassUsingGenericDelagate() throws Exception { SingleAbstractMethodNoArgumentsClass m = delegate("hello"); assertEquals("hello", m.getMessage()); } static abstract class SingleAbstractMethodOneArgumentClass { public void ignoredAsNotAbstract() { } public abstract String getHelloMessage(String name); } @Test public void createSingleMethodWithOneArugmentClassUsingGenericDelagate() throws Exception { SingleAbstractMethodOneArgumentClass m = delegate(s, "hello: " + s); assertEquals("hello: world", m.getHelloMessage("world")); } @Test public void createSingleAbstractMethodWithLibrarySuperClass() throws Exception { Timer timer = new Timer(); int x = 0; TimerTask t = delegate(x = 1); timer.schedule(t, 50); assertEquals(0, x); sleep(100); assertEquals(1, x); } @LambdaParameter static ActionEvent e; @Test public void oneArgumentLambdaAsInterface() throws Exception { ActionEvent actual; ActionListener a = λ(e, actual = e).as(ActionListener.class); ActionEvent event = new ActionEvent(this, 1, "command"); a.actionPerformed(event); assertSame(event, actual); } @SuppressWarnings("unchecked") @Test public void twoArgumentLambdaAsInterface() throws Exception { Comparator<Integer> c = λ(n, m, m - n).as(Comparator.class); List<Integer> list = list(1, 2, 3); Collections.sort(list, c); assertEquals(list(3, 2, 1), list); } @Test public void oneArgumentLambdaAsInterfaceWithZeroArgumentMethod() throws Exception { String string = ""; Callable<?> callable = λ(string = "hello").as(Callable.class); assertEquals("hello", callable.call()); assertEquals("hello", string); } @Test public void lambdaAsRunnable() throws Exception { String string = ""; Thread thread = new Thread(λ(string = "hello").as(Runnable.class)); assertEquals("", string); thread.start(); thread.join(); assertEquals("hello", string); } @LambdaParameter static KeyEvent ke; @Test public void lambdaAsKeyListener() throws Exception { List<KeyEvent> events = new ArrayList<KeyEvent>(); KeyListener keyListener = λ(ke, events.add(ke)).as(KeyListener.class); KeyEvent event = new KeyEvent(new JButton(), 0, 0, 0, 0, (char) 0); keyListener.keyPressed(event); assertEquals(1, events.size()); } @Test public void lambdaAsKeyListenerWithRegex() throws Exception { List<KeyEvent> events = new ArrayList<KeyEvent>(); KeyListener keyListener = λ(ke, events.add(ke)).as(KeyListener.class, ".*Typed"); KeyEvent event = new KeyEvent(new JButton(), 0, 0, 0, 0, (char) 0); keyListener.keyPressed(event); assertTrue(events.isEmpty()); keyListener.keyReleased(event); assertTrue(events.isEmpty()); keyListener.keyTyped(event); assertEquals(1, events.size()); } @Test public void lambdaAsKeyListenerWithExactMatchAndMatchingParameterType() throws Exception { List<KeyEvent> events = new ArrayList<KeyEvent>(); KeyListener keyListener = λ(ke, events.add(ke)).as(KeyListener.class, "keyPressed", EventObject.class); KeyEvent event = new KeyEvent(new JButton(), 0, 0, 0, 0, (char) 0); keyListener.keyPressed(event); assertEquals(1, events.size()); keyListener.keyReleased(event); assertEquals(1, events.size()); keyListener.keyPressed(null); assertEquals(2, events.size()); } @Test public void lambdaAsKeyListenerWithExactMatchAndNonMatchingParameterType() throws Exception { List<KeyEvent> events = new ArrayList<KeyEvent>(); KeyListener keyListener = λ(ke, events.add(ke)).as(KeyListener.class, "keyPressed", ActionEvent.class); KeyEvent event = new KeyEvent(new JButton(), 0, 0, 0, 0, (char) 0); keyListener.keyPressed(event); assertTrue(events.isEmpty()); } @Test @SuppressWarnings("unchecked") public void lambdaAsInterfaceWithExactMatchAndPrimitiveParameterTypes() throws Exception { List<String> list = new ArrayList<String>(); list.add("world"); List<String> proxy = λ(idx, s, list.set(idx, s)).as(List.class, "set", Integer.class, String.class); proxy.set(0, "hello"); assertEquals("hello", list.get(0)); proxy.clear(); assertFalse(list.isEmpty()); } @Test @SuppressWarnings("unchecked") public void lambdaAsInterfaceWithExactMatchAndNullArgumentPassesFilter() throws Exception { List<String> list = new ArrayList<String>(); list.add("world"); List<String> proxy = λ(idx, s, list.set(idx, s)).as(List.class, "set", Integer.class, String.class); proxy.set(0, null); assertNull(list.get(0)); } @Test public void lambdaInConstructor() throws Exception { class ConstructorClass { public String fromConstructor; { λ(fromConstructor = "hello").call(); } } assertEquals("hello", new ConstructorClass().fromConstructor); } @Test public void reflectionOnLambda() throws Exception { int i = 0; Fn1<Integer, Integer> addToI = λ(n, i += n); Method fn1Call = addToI.getClass().getMethod("call", int.class); assertEquals(1, fn1Call.invoke(addToI, 1)); assertEquals(1, i); LambdaLocal annotation = addToI.getParameters().get(0); assertEquals("n", annotation.name()); assertEquals(Parameters.class.getName(), annotation.parameterClass()); Field lambdaParameterField = addToI.getParameterFields().get(0); assertEquals("n", lambdaParameterField.getName()); assertEquals(int.class, lambdaParameterField.getType()); assertEquals(Parameters.class, lambdaParameterField.getDeclaringClass()); Class<?> addToIClass = addToI.getClass(); assertTrue(addToIClass.isSynthetic()); assertTrue(Modifier.isFinal(addToIClass.getModifiers())); assertEquals(getClass(), addToIClass.getEnclosingClass()); assertTrue(list(getClass().getDeclaredClasses()).contains(addToIClass)); assertEquals(getClass().getMethod("reflectionOnLambda"), addToIClass.getEnclosingMethod()); assertEquals(1, addToIClass.getDeclaredMethods().length); assertEquals(1, addToIClass.getDeclaredFields().length); Field field = addToIClass.getDeclaredField("i$1"); assertEquals(int[].class, field.getType()); assertTrue(field.isSynthetic()); assertTrue(Modifier.isFinal(field.getModifiers())); field.setAccessible(true); int[] capturedI = (int[]) field.get(addToI); assertEquals(1, i); capturedI[0] = 2; assertEquals(2, i); assertEquals(3, (int) addToI.call(1)); } @Test public void partialApplication() throws Exception { Fn3<String, Integer, Integer, String> addWithPrefixString = λ(s, n, m, s + (n + m)); assertEquals("prefix: 5", addWithPrefixString.call("prefix: ", 2, 3)); Fn2<Integer, Integer, String> add = addWithPrefixString.partial("result: "); assertEquals("result: 2", add.call(1, 1)); Fn1<Integer, String> add2 = add.partial(2); assertEquals("result: 4", add2.call(2)); Fn0<String> six = add2.partial(4); assertEquals("result: 6", six.call()); } @Test public void curryingFn2() throws Exception { Fn2<String, Integer, String> withPrefixString = λ(s, n, s + n); assertEquals("prefix: 2", withPrefixString.call("prefix: ", 2)); Fn1<String, Fn1<Integer, String>> add = withPrefixString.curry2(); assertEquals("result: 2", add.call("result: ").call(2)); } @Test public void curryingFn3() throws Exception { Fn3<String, Integer, Integer, String> addWithPrefixString = λ(s, n, m, s + (n + m)); assertEquals("prefix: 5", addWithPrefixString.call("prefix: ", 2, 3)); Fn1<String, Fn1<Integer, Fn1<Integer, String>>> add = addWithPrefixString.curry3(); assertEquals("result: 2", add.call("result: ").call(1).call(1)); } @Test(expected = UnsupportedOperationException.class) public void curryingFn3UsingCurry2ThrowsException() throws Exception { Fn3<String, Integer, Integer, String> addWithPrefixString = λ(s, n, m, s + (n + m)); addWithPrefixString.curry2(); } @Test public void fn1OfFn0FunctionComposition() throws Exception { Fn1<Object, String> toString = λ(obj, obj.toString()); Fn0<Integer> ten = λ(10); assertEquals(10, (int) ten.call()); Fn0<String> tenToString = toString.compose(ten); assertEquals("10", tenToString.call()); } @Test public void fn1OfFn1FunctionComposition() throws Exception { Fn1<Boolean, Boolean> not = λ(b, !b); Fn1<Integer, Boolean> even = λ(n, n % 2 == 0); assertTrue(even.call(2)); Fn1<Integer, Boolean> notEven = not.compose(even); assertTrue(notEven.call(3)); } @Test public void fn1OfFn2FunctionComposition() throws Exception { Fn1<Object, String> toString = λ(obj, obj.toString()); Fn2<Integer, Integer, Integer> times = λ(n, m, n * m); Fn2<Integer, Integer, String> toStringTimes = toString.compose(times); assertEquals("12", toStringTimes.call(3, 4)); } @Test public void complementOfFn() throws Exception { Fn0<Boolean> truth = λ(true); assertTrue(truth.call()); assertFalse(truth.complement().call()); Fn0<Object> nullFn = λ(null); assertNull(nullFn.call()); assertTrue(nullFn.complement().call()); Fn1<Integer, Boolean> isOdd = λ(n, n % 2 == 1); assertTrue(isOdd.call(1)); assertFalse(isOdd.complement().call(1)); Fn2<Integer, Integer, Boolean> isSumOdd = λ(n, m, (Boolean) ((m + n) % 2 == 1)); assertTrue(isSumOdd.call(1, 2)); assertFalse(isSumOdd.complement().call(1, 2)); Fn3<Integer, Integer, Integer, Boolean> isSumEven = λ(n, m, i, (m + n + i) % 2 == 0); assertTrue(isSumEven.call(1, 2, 1)); assertFalse(isSumEven.complement().call(1, 2, 1)); } @Test public void comparsionsInZeroArgumentLambdas() throws Exception { assertTrue(λ(true).call()); assertFalse(λ(false).call()); String s = null; assertTrue(λ(s == null).call()); s = ""; assertTrue(λ(s != null).call()); assertTrue(λ(1 != 2).call()); int i = 5; assertTrue(λ(i > 0).call()); assertFalse(λ(i < 0).call()); assertTrue(λ(i / 5 == 1).call()); assertTrue(λ(i == 5).call()); assertFalse(λ(i == 2).call()); } @Test public void comparsiosWithMutlipleLables() throws Exception { int p = 2; Fn1ItoB notDivisbleBy = λ(n, n <= p || (n % p) != 0); assertTrue(notDivisbleBy.call(1)); assertTrue(notDivisbleBy.call(2)); assertTrue(notDivisbleBy.call(3)); assertFalse(notDivisbleBy.call(4)); } @SuppressWarnings("rawtypes") @LambdaParameter static List l; @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void toyScheme() throws Exception { Fn1<List, Object> car = λ(l, l.get(0)); Fn1<List, List> cdr = λ(l, l.subList(1, l.size())); Fn2<Object, List, List> cons = λ(obj, l, (l = list(l.toArray())).addAll(0, list(obj)) ? l : l); Fn1<Object, Boolean> isPair = λ(obj, obj instanceof List); Fn1<Object, Boolean> isAtom = λ(b, !b).compose(isPair); List ints = list(1, 2, 3); assertEquals(1, car.call(ints)); assertEquals(list(2, 3), cdr.call(ints)); assertEquals(list(0, 1, 2, 3), cons.call(0, ints)); assertTrue(isAtom.call(car.call(ints))); assertTrue(isPair.call(cdr.call(ints))); } }