/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.j2objc.java8;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
interface UnaryOperator<T> {
public T apply(T t);
}
interface UOToUO<T> {
UnaryOperator<T> apply(UOToUO<T> x);
}
/**
* Command-line tests for Lambda support.
*
* @author Seth Kirby
*/
public class LambdaTest extends TestCase {
public LambdaTest() {}
public void testBasicReflection() {
assertEquals(false, ((Lambdas.One) (x) -> 1).equals((Lambdas.One) (x) -> 1));
Lambdas.One f = x -> 1;
Lambdas.One g = f;
assertEquals(f, g);
assertEquals(f.toString(), "" + g);
String lambdaClassName = f.getClass().getName();
assertEquals(true, lambdaClassName.contains("LambdaTest"));
assertEquals(true, lambdaClassName.contains("ambda$"));
}
String outerCall() {
return "Foo";
}
private String privateOuterCall() {
return "Bar";
}
static class Foo {
String outer() {
return "Foo";
}
class Bar {
String findMe() {
return "Bar";
}
Lambdas.Zero fooS = () -> outer();
Lambdas.Zero barS = () -> findMe();
}
Bar getBar() {
return new Bar();
}
}
public void testOuterMethodCalls() {
assertEquals("Foo", Lambdas.get(() -> outerCall()).apply());
assertEquals("Bar", Lambdas.get(() -> privateOuterCall()).apply());
Foo foo = new Foo();
assertEquals("Foo", Lambdas.get(() -> foo.outer()).apply());
assertEquals("Bar", Lambdas.get(() -> foo.getBar().barS.apply()).apply());
}
Integer outerX = 0;
int outerY = 0;
public void testOuterVarCapture() {
Lambdas.Zero s = () -> outerX;
Lambdas.Zero s2 = () -> outerY;
assertEquals((Integer) 0, s.apply());
assertEquals((Integer) 0, s2.apply());
outerX += 42;
outerY += 42;
assertEquals((Integer) 42, s.apply());
assertEquals((Integer) 42, s2.apply());
outerX++;
outerY++;
assertEquals((Integer) 43, s.apply());
assertEquals((Integer) 43, s2.apply());
outerX++;
outerY++;
}
public void testFunctionArray() throws Exception {
List<Lambdas.One> fs = new ArrayList<>();
int[] nums = { 3, 2, 1, 0 };
for (int i : nums) {
fs.add((x) -> i);
}
assertEquals(0, fs.get(3).apply("5"));
assertEquals(1, fs.get(2).apply(new Object()));
assertEquals(2, fs.get(1).apply(new Object()));
assertEquals(3, fs.get(0).apply("4"));
}
public <T> UnaryOperator<T> yComb(UnaryOperator<UnaryOperator<T>> r) {
return ((UOToUO<T>) f -> f.apply(f)).apply(f -> r.apply(x -> f.apply(f).apply(x)));
}
public void testYCombinator() throws Exception {
UnaryOperator<UnaryOperator<String>> a = (UnaryOperator<String> f) -> {
return (x) -> {
if (x.length() == 5) {
return x;
} else {
return f.apply((char) (x.charAt(0) + 1) + x);
}
};
};
assertEquals("edcba", yComb(a).apply("a"));
UnaryOperator<UnaryOperator<Integer>> fibonacci = (UnaryOperator<Integer> f) -> {
return (Integer x) -> {
if (x < 1) {
return 0;
} else if (x == 1) {
return x;
} else {
return f.apply(x - 1) + f.apply(x - 2);
}
};
};
assertEquals((Integer) 55, yComb(fibonacci).apply(10));
}
Lambdas.One outerF = (x) -> x;
public void testOuterFunctions() throws Exception {
assertEquals(42, outerF.apply(42));
}
static Lambdas.One staticF = (x) -> x;
public void testStaticFunctions() throws Exception {
assertEquals(42, staticF.apply(42));
}
public void testNestedLambdas() throws Exception {
Lambdas.One<String, Lambdas.One<String, String>> f = (x) -> (y) -> x;
assertEquals(f.apply("Foo").apply("Bar"), "Foo");
Lambdas.One<Integer, Lambdas.One<Integer, Integer>> f2 = (x) -> (y) -> x;
assertEquals(f2.apply(42).apply(43), Integer.valueOf(42));
Lambdas.One<Integer, Lambdas.One<Integer, Integer>> f3 = (y) -> (x) -> x;
assertEquals(f3.apply(43).apply(42), Integer.valueOf(42));
Lambdas.One<String, Lambdas.One<String, Integer>> f4 = (x) -> (y) -> Integer.parseInt(
x);
assertEquals(f4.apply("42").apply("43"), Integer.valueOf(42));
Lambdas.One<String, Lambdas.One<Integer, Integer>> f5 = (x) -> (y) -> Integer.parseInt(
x);
assertEquals(f5.apply("42").apply(43), Integer.valueOf(42));
Callable<Callable> c2 = () -> () -> 42;
assertEquals(c2.call().call(), 42);
Lambdas.Zero<Lambdas.Zero> s2 = () -> () -> 42;
assertEquals(s2.apply().apply(), 42);
}
// Tests outer reference resolution, and that inner fields are being correctly resolved for
// lambdas with implicit blocks.
public void testAdditionInLambda() throws Exception {
Lambdas.One f = new Lambdas.One<Integer, Integer>() {
@Override
public Integer apply(Integer x) {
return x + 20;
}
};
assertEquals(42, f.apply(22));
Lambdas.One<Integer, Integer> g = x -> {
return x + 20;
};
assertEquals((Integer) 42, g.apply(22));
Lambdas.One<Integer, Integer> h = x -> x + 20;
assertEquals((Integer) 42, h.apply(22));
}
public void testBasicAnonymousClass() throws Exception {
Lambdas.One h = new Lambdas.One() {
@Override
public Object apply(Object x) {
return x;
}
};
assertEquals(42, h.apply(42));
}
public void testBasicFunction() throws Exception {
Lambdas.One f = x -> x;
assertEquals(42, f.apply(42));
Lambdas.One<Integer, Integer> f2 = x -> x;
assertEquals((Integer) 42, f2.apply(42));
Lambdas.One f3 = x -> {
int y = 42;
return y;
};
assertEquals(42, f3.apply(null));
Lambdas.Four<String, Double, Integer, Boolean, String> appendFour = (a, b, c, d) -> a + b + c
+ d;
assertEquals("Foo4.214true", appendFour.apply("Foo", 4.2, 14, true));
}
public void testBasicSupplier() throws Exception {
Lambdas.Zero s = () -> 42;
assertEquals(42, s.apply());
}
public void testBasicConsumer() throws Exception {
Object[] ls = new Object[1];
Lambdas.VoidOne c = (x) -> ls[0] = x;
c.apply(42);
assertEquals(42, ls[0]);
}
public void testBasicCallable() throws Exception {
Callable c = () -> 42;
assertEquals(42, c.call());
}
// Adapted from translator/src/test/java/com/google/devtools/j2objc/ast/LambdaExpressionTest.java
interface NumberFunction<T extends Number, R> {
R apply(T t);
}
static class CaptureTest1 {
int y;
NumberFunction f = x -> y + x.intValue();
}
static class CaptureTest2 {
int y;
class InnerCaptureTest {
int y;
NumberFunction fOuter = x -> CaptureTest2.this.y + x.intValue();
NumberFunction fInner = x -> y + x.intValue();
}
}
public void testImplicitCapture() throws Exception {
CaptureTest1 t1 = new CaptureTest1();
assertEquals(1, t1.f.apply(1));
t1.y = 1;
assertEquals(3, t1.f.apply(2));
CaptureTest2 t2 = new CaptureTest2();
CaptureTest2.InnerCaptureTest t2i = t2.new InnerCaptureTest();
t2.y = 10;
t2i.y = 20;
assertEquals(9, t2i.fOuter.apply(-1));
assertEquals(19, t2i.fInner.apply(-1));
}
interface IntSupplier {
int get();
static int f(IntSupplier s) {
return s.get();
}
}
// x + y * 2
private int getFromSimpleLambda(int x, int y) {
return IntSupplier.f(() -> x + y) + y;
}
// x + y * 3
private int getFromNestedLambdas(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(() -> x + y) + y) + y;
}
// x + y * 3
private int getFromAnonymousClassesAndLambda(int x, int y) {
return IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y) + y;
}
}) + y;
}
// x + y * 3
private int getFromLambdaAndAnonymousClass(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return x + y;
}
}) + y) + y;
}
// x + y * 5
private int getFromNestedAnonymousClassesAndLambdas(int x, int y) {
return IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y) + y;
}
}) + y) + y;
}
}) + y;
}
// x + y * 5
private int getFromNestedLambdasAndAnonymousClasses(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return x + y;
}
}) + y) + y;
}
}) + y) + y;
}
// Putting everything together
static class ComplexLambda {
static final int P = 1;
final int q = 2;
int r = Integer.MIN_VALUE;
public class Inner {
static final int S = 4;
final int t = 5;
int u = Integer.MIN_VALUE;
// Should be x + y * 6 + P + Q + R + S + T + U;
public int get(int x, int y) {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y + P) + y + q;
}
}) + y + r) + y + S;
}
}) + y + t) + y + u;
}
}
}
public void testNestingLambdasAndAnonymousClasses() throws Exception {
// Each method is called twice to make sure that all lambdas are capturing ones.
assertEquals(102, getFromSimpleLambda(100, 1));
assertEquals(104, getFromSimpleLambda(100, 2));
assertEquals(109, getFromNestedLambdas(100, 3));
assertEquals(112, getFromNestedLambdas(100, 4));
assertEquals(115, getFromAnonymousClassesAndLambda(100, 5));
assertEquals(118, getFromAnonymousClassesAndLambda(100, 6));
assertEquals(121, getFromLambdaAndAnonymousClass(100, 7));
assertEquals(124, getFromLambdaAndAnonymousClass(100, 8));
assertEquals(145, getFromNestedAnonymousClassesAndLambdas(100, 9));
assertEquals(150, getFromNestedLambdasAndAnonymousClasses(100, 10));
int z = 10;
class Local {
int w = 1;
// x + y * 3 + w + z
private int get(int x, int y) {
return IntSupplier.f(new IntSupplier() {
@Override
public int get() {
return IntSupplier.f(() -> x + y + z + w) + y;
}
}) + y;
}
}
Local l = new Local();
assertEquals(171, l.get(100, 20));
assertEquals(186, l.get(100, 25));
l.w = 500;
assertEquals(670, l.get(100, 20));
assertEquals(685, l.get(100, 25));
ComplexLambda cl = new ComplexLambda();
cl.r = 3;
ComplexLambda.Inner inner = cl.new Inner();
inner.u = 6;
// Should be 1000 + 20 * 6 + sum(1...6)
assertEquals(1141, inner.get(1000, 20));
cl.r += 10000;
inner.u += 10000;
// Should be 1000 + 20 * 6 + sum(1...6) + 10000 + 10000;
assertEquals(21141, inner.get(1000, 20));
}
interface IntSupplierSupplier {
IntSupplier get();
}
public void testNestedLambdaCapturesOuterScope() {
IntSupplier s = new IntSupplier() {
int i = 1234;
public int get() {
IntSupplierSupplier ss = () -> () -> i;
return ss.get().get();
}
};
assertEquals(1234, s.get());
}
interface ImplicitCaptures {
interface F {
int f();
default F g() {
return () -> f();
}
}
interface G extends F {
default F g() {
return () -> F.super.g().f();
}
}
static class P {
int x = 0;
void setX(int x) {
this.x = x;
}
int getX() {
return x;
}
}
static class Q extends P {
public F getSuperGetX() {
return () -> super.getX();
}
public F getFieldX() {
return () -> x;
}
public F getThisFieldX() {
return () -> this.x;
}
public F getThisGetX() {
return () -> getX();
}
}
}
public void testCapturingLambdasFromQualifiedSuperInvocations() throws Exception {
ImplicitCaptures.G g1 = new ImplicitCaptures.G() {
@Override
public int f() {
return 10;
}
};
ImplicitCaptures.G g2 = new ImplicitCaptures.G() {
@Override
public int f() {
return 20;
}
};
assertEquals(10, g1.g().f());
// () -> F.super.g().f() must be a non-capturing lambda to return this value correctly.
assertEquals(20, g2.g().f());
}
public void testImplicitLambdaCaptures() throws Exception {
ImplicitCaptures.Q q1 = new ImplicitCaptures.Q() {
@Override
int getX() {
return 20;
}
};
ImplicitCaptures.Q q2 = new ImplicitCaptures.Q() {
@Override
int getX() {
return 40;
}
};
q1.setX(10);
assertEquals(10, q1.getSuperGetX().f());
assertEquals(10, q1.getFieldX().f());
assertEquals(10, q1.getThisFieldX().f());
assertEquals(20, q1.getThisGetX().f()); // From q1's overridden getX()
q2.setX(30);
assertEquals(30, q2.getSuperGetX().f());
assertEquals(30, q2.getFieldX().f());
assertEquals(30, q2.getThisFieldX().f());
assertEquals(40, q2.getThisGetX().f()); // From q2's overridden getX()
}
}