// Copyright 2017 The Bazel Authors. All rights reserved. // // 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.devtools.build.android.desugar; import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; import com.google.common.collect.ImmutableList; import com.google.devtools.build.android.desugar.testdata.java8.AnnotationsOfDefaultMethodsShouldBeKept.AnnotatedInterface; import com.google.devtools.build.android.desugar.testdata.java8.AnnotationsOfDefaultMethodsShouldBeKept.SomeAnnotation; import com.google.devtools.build.android.desugar.testdata.java8.ConcreteDefaultInterfaceWithLambda; import com.google.devtools.build.android.desugar.testdata.java8.ConcreteOverridesDefaultWithLambda; import com.google.devtools.build.android.desugar.testdata.java8.DefaultInterfaceMethodWithStaticInitializer; import com.google.devtools.build.android.desugar.testdata.java8.DefaultInterfaceWithBridges; import com.google.devtools.build.android.desugar.testdata.java8.DefaultMethodFromSeparateJava8Target; import com.google.devtools.build.android.desugar.testdata.java8.DefaultMethodFromSeparateJava8TargetOverridden; import com.google.devtools.build.android.desugar.testdata.java8.DefaultMethodTransitivelyFromSeparateJava8Target; import com.google.devtools.build.android.desugar.testdata.java8.FunctionWithDefaultMethod; import com.google.devtools.build.android.desugar.testdata.java8.FunctionalInterfaceWithInitializerAndDefaultMethods; import com.google.devtools.build.android.desugar.testdata.java8.GenericDefaultInterfaceWithLambda; import com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod; import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithDefaultMethod; import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithDuplicateMethods.ClassWithDuplicateMethods; import com.google.devtools.build.android.desugar.testdata.java8.Java7InterfaceWithBridges; import com.google.devtools.build.android.desugar.testdata.java8.Named; import com.google.devtools.build.android.desugar.testdata.java8.TwoInheritedDefaultMethods; import com.google.devtools.build.android.desugar.testdata.java8.VisibilityTestClass; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.function.Function; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Test that exercises classes in the {@code testdata_java8} package. This is meant to be run * against a desugared version of those classes, which in turn exercise various desugaring features. */ @RunWith(JUnit4.class) public class DesugarJava8FunctionalTest extends DesugarFunctionalTest { public DesugarJava8FunctionalTest() { this(true, true); } protected DesugarJava8FunctionalTest( boolean expectBridgesFromSeparateTarget, boolean expectDefaultMethods) { super(expectBridgesFromSeparateTarget, expectDefaultMethods); } @Test public void testLambdaInDefaultMethod() { assertThat(new ConcreteDefaultInterfaceWithLambda().defaultWithLambda()) .containsExactly("0", "1") .inOrder(); } @Test public void testLambdaInDefaultCallsInterfaceMethod() { assertThat(new ConcreteDefaultInterfaceWithLambda().defaultCallsInterfaceMethod()) .containsExactly("1", "2") .inOrder(); } @Test public void testOverrideLambdaInDefault() { assertThat(new ConcreteOverridesDefaultWithLambda().defaultWithLambda()) .containsExactly("2", "3") .inOrder(); } @Test public void testLambdaInDefaultCallsOverrideMethod() { assertThat(new ConcreteOverridesDefaultWithLambda().defaultCallsInterfaceMethod()) .containsExactly("3", "4") .inOrder(); } @Test public void testDefaultInterfaceMethodReference() { InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); List dest = methodrefUse.defaultMethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); assertThat(dest).containsExactly("Sergey"); } @Test public void testStaticInterfaceMethodReference() { InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); List dest = methodrefUse.staticMethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); assertThat(dest).containsExactly("Alex"); } @Test public void testLambdaCallsDefaultMethod() { InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); List dest = methodrefUse.lambdaCallsDefaultMethod(ImmutableList.of("Sergey", "Larry", "Alex")); assertThat(dest).containsExactly("Sergey"); } @Test public void testBootclasspathMethodInvocations() { InterfaceMethod concrete = new InterfaceMethod.Concrete(); assertThat(concrete.defaultInvokingBootclasspathMethods("Larry")).isEqualTo("Larry"); } @Test public void testStaticMethodsInInterface_explicitAndLambdaBody() { List result = FunctionWithDefaultMethod.DoubleInts.add(ImmutableList.of(7, 39, 8), 3); assertThat(result).containsExactly(10L, 42L, 11L).inOrder(); } @Test public void testOverriddenDefaultMethod_inHandwrittenClass() { FunctionWithDefaultMethod doubler = new FunctionWithDefaultMethod.DoubleInts(); assertThat(doubler.apply(7)).isEqualTo(14); assertThat(doubler.twice(7)).isEqualTo(35); } @Test public void testOverriddenDefaultMethod_inHandwrittenSuperclass() { FunctionWithDefaultMethod doubler = new FunctionWithDefaultMethod.DoubleInts2(); assertThat(doubler.apply(7)).isEqualTo(14); assertThat(doubler.twice(7)).isEqualTo(35); } @Test public void testInheritedDefaultMethod_inLambda() { FunctionWithDefaultMethod doubler = FunctionWithDefaultMethod.DoubleInts.doubleLambda(); assertThat(doubler.apply(7)).isEqualTo(14); assertThat(doubler.twice(7)).isEqualTo(28); } @Test public void testDefaultMethodReference_onLambda() { FunctionWithDefaultMethod plus6 = FunctionWithDefaultMethod.DoubleInts.incTwice(3); assertThat(plus6.apply(18)).isEqualTo(24); assertThat(plus6.twice(18)).isEqualTo(30); } @Test public void testDefaultMethodReference_onHandwrittenClass() { FunctionWithDefaultMethod times5 = FunctionWithDefaultMethod.DoubleInts.times5(); assertThat(times5.apply(6)).isEqualTo(30); assertThat(times5.twice(6)).isEqualTo(150); // Irrelevant that DoubleInts overrides twice() } @Test public void testStaticInterfaceMethodReferenceReturned() { Function> factory = FunctionWithDefaultMethod.DoubleInts.incFactory(); assertThat(factory.apply(6).apply(7)).isEqualTo(13); assertThat(factory.apply(6).twice(7)).isEqualTo(19); } @Test public void testSuperDefaultMethodInvocation() { assertThat(new TwoInheritedDefaultMethods().name()).isEqualTo("One:Two"); assertThat(new Named.DefaultName().name()).isEqualTo("DefaultName-once"); assertThat(new Named.DefaultNameSubclass().name()).isEqualTo("DefaultNameSubclass-once-twice"); } @Test public void testInheritedPreferredOverDefault() throws Exception { assertThat(new Named.ExplicitName("hello").name()).isEqualTo("hello"); // Make sure AbstractName remains abstract, despite default method from implemented interface assertThat(Modifier.isAbstract(Named.AbstractName.class.getMethod("name").getModifiers())) .isTrue(); } @Test public void testRedefinedDefaultMethod() throws Exception { assertThat(new InterfaceWithDefaultMethod.Version2().version()).isEqualTo(2); } @Test public void testDefaultMethodRedefinedInSubclass() throws Exception { assertThat(new InterfaceWithDefaultMethod.AlsoVersion2().version()).isEqualTo(2); } @Test public void testDefaultMethodVisibility() { assertThat(new VisibilityTestClass().m()).isEqualTo(42); } /** Test for b/38302860 */ @Test public void testAnnotationsOfDefaultMethodsAreKept() throws Exception { { Annotation[] annotations = AnnotatedInterface.class.getAnnotations(); assertThat(annotations).hasLength(1); assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(1); } { Annotation[] annotations = AnnotatedInterface.class.getMethod("annotatedAbstractMethod").getAnnotations(); assertThat(annotations).hasLength(1); assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(2); } { Annotation[] annotations = AnnotatedInterface.class.getMethod("annotatedDefaultMethod").getAnnotations(); assertThat(annotations).hasLength(1); assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(3); } } /** Test for b/38308515 */ @Test public void testDefaultAndStaticMethodNameClash() { final ClassWithDuplicateMethods instance = new ClassWithDuplicateMethods(); assertThat(instance.getZero()).isEqualTo(0); assertThat(instance.getZeroFromStaticInterfaceMethod()).isEqualTo(1); } /** * Test for b/38257037 * *

Note that, we intentionally suppress unchecked warnings, because we expect some * ClassCastException to test bridge methods. */ @SuppressWarnings("unchecked") @Test public void testBridgeAndDefaultMethods() { { DefaultInterfaceWithBridges object = new DefaultInterfaceWithBridges(); Integer one = 1; assertThat(object.copy(one)).isEqualTo(one); assertThat(object.copy((Number) one)).isEqualTo(one); assertThrows(ClassCastException.class, () -> object.copy(Double.valueOf(1))); assertThat(object.getNumber()).isInstanceOf(Double.class); assertThat(object.getNumber()).isEqualTo(Double.valueOf(2.3d)); assertThat(object.getDouble()).isEqualTo(Double.valueOf(2.3d)); } { Java7InterfaceWithBridges.ClassAddTwo testObject = new Java7InterfaceWithBridges.ClassAddTwo(); assertThat(testObject.add(Integer.valueOf(2))).isEqualTo(4); @SuppressWarnings("rawtypes") Java7InterfaceWithBridges top = testObject; assertThat(top.add(Integer.valueOf(2))).isEqualTo(4); assertThrows(ClassCastException.class, () -> top.add(new Object())); assertThrows(ClassCastException.class, () -> top.add(Double.valueOf(1))); assertThrows(ClassCastException.class, () -> top.add(Long.valueOf(1))); @SuppressWarnings("rawtypes") Java7InterfaceWithBridges.LevelOne levelOne = testObject; assertThat(levelOne.add(Integer.valueOf(2))).isEqualTo(4); assertThrows(ClassCastException.class, () -> top.add(new Object())); assertThrows(ClassCastException.class, () -> top.add(Double.valueOf(1))); assertThrows(ClassCastException.class, () -> top.add(Long.valueOf(1))); @SuppressWarnings("rawtypes") Java7InterfaceWithBridges.LevelOne levelTwo = testObject; assertThat(levelTwo.add(Integer.valueOf(2))).isEqualTo(4); assertThrows(ClassCastException.class, () -> levelTwo.add(Double.valueOf(1))); assertThrows(ClassCastException.class, () -> levelTwo.add(Long.valueOf(1))); } { GenericDefaultInterfaceWithLambda.ClassTwo testObject = new GenericDefaultInterfaceWithLambda.ClassTwo(); assertThat(testObject.increment(Integer.valueOf(0))).isEqualTo(1); assertThat(testObject.toString(Integer.valueOf(0))).isEqualTo("0"); assertThat(testObject.getBaseValue()).isEqualTo(Integer.valueOf(0)); assertThat(testObject.toList(0)).isEmpty(); assertThat(testObject.toList(1)).containsExactly(0).inOrder(); assertThat(testObject.toList(2)).containsExactly(0, 1).inOrder(); assertThat(((Function>) testObject.toListSupplier()).apply(0)) .isEmpty(); assertThat(((Function>) testObject.toListSupplier()).apply(1)) .containsExactly(0) .inOrder(); assertThat(((Function>) testObject.toListSupplier()).apply(2)) .containsExactly(0, 1) .inOrder(); assertThat(testObject.convertToStringList(ImmutableList.of(0))) .containsExactly("0") .inOrder(); assertThat(testObject.convertToStringList(ImmutableList.of(0, 1))) .containsExactly("0", "1") .inOrder(); @SuppressWarnings("rawtypes") GenericDefaultInterfaceWithLambda top = testObject; assertThrows(ClassCastException.class, () -> top.increment(Long.valueOf(1))); assertThrows(ClassCastException.class, () -> top.toString(Long.valueOf(1))); assertThat(top.increment(Integer.valueOf(0))).isEqualTo(1); assertThat(top.toString(Integer.valueOf(0))).isEqualTo("0"); assertThat(top.getBaseValue()).isEqualTo(Integer.valueOf(0)); assertThat(top.toList(0)).isEmpty(); assertThat(top.toList(1)).containsExactly(0).inOrder(); assertThat(top.toList(2)).containsExactly(0, 1).inOrder(); assertThat(((Function>) top.toListSupplier()).apply(0)).isEmpty(); assertThat(((Function>) top.toListSupplier()).apply(1)) .containsExactly(0) .inOrder(); assertThat(((Function>) top.toListSupplier()).apply(2)) .containsExactly(0, 1) .inOrder(); assertThat(top.convertToStringList(ImmutableList.of(0))).containsExactly("0").inOrder(); assertThat(top.convertToStringList(ImmutableList.of(0, 1))) .containsExactly("0", "1") .inOrder(); } { @SuppressWarnings("rawtypes") GenericDefaultInterfaceWithLambda testObject = new GenericDefaultInterfaceWithLambda.ClassThree(); assertThat(testObject.getBaseValue()).isEqualTo(Long.valueOf(0)); assertThat(testObject.increment(Long.valueOf(0))).isEqualTo(Long.valueOf(0 + 1)); assertThat(testObject.toString(Long.valueOf(0))).isEqualTo(Long.valueOf(0).toString()); assertThrows(ClassCastException.class, () -> testObject.increment(Integer.valueOf(0))); assertThrows(ClassCastException.class, () -> testObject.toString(Integer.valueOf(0))); assertThat(testObject.toList(2)).containsExactly(Long.valueOf(0), Long.valueOf(1)).inOrder(); assertThat(testObject.convertToStringList(testObject.toList(1))).containsExactly("0"); assertThat(((Function>) testObject.toListSupplier()).apply(2)) .containsExactly(Long.valueOf(0), Long.valueOf(1)); } } /** * Test for b/62047432. * *

When desugaring a functional interface with an executable clinit and default methods, we * erase the body of clinit to avoid executing it during desugaring. This test makes sure that all * the constants defined in the interface are still there after desugaring. */ @Test public void testFunctionalInterfaceWithExecutableClinitStillWorkAfterDesugar() { assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CONSTANT.length("").convert()) .isEqualTo(0); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CONSTANT.length("1").convert()) .isEqualTo(1); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.BOOLEAN).isFalse(); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CHAR).isEqualTo('h'); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.BYTE).isEqualTo(0); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.SHORT).isEqualTo(0); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.INT).isEqualTo(0); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.FLOAT).isEqualTo(0f); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.LONG).isEqualTo(0); assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.DOUBLE).isEqualTo(0d); } /** Test for b/38255926. */ @Test public void testDefaultMethodInitializationOrder() { { assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne.C().sum()) .isEqualTo(11); // To trigger loading the class C and its super interfaces. assertThat( DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne .getRealInitializationOrder()) .isEqualTo( DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne .getExpectedInitializationOrder()); } { assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo.C().sum()) .isEqualTo(3); assertThat( DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo .getRealInitializationOrder()) .isEqualTo( DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo .getExpectedInitializationOrder()); } { assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree.C().sum()) .isEqualTo(11); assertThat( DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree .getRealInitializationOrder()) .isEqualTo( DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree .getExpectedInitializationOrder()); } } /** * Tests that default methods on the classpath are correctly handled. We'll also verify the * metadata that's emitted for this case to make sure the binary-wide double-check for correct * desugaring of default and static interface methods keeps working (b/65645388). */ @Test public void testDefaultMethodsInSeparateTarget() { assertThat(new DefaultMethodFromSeparateJava8Target().dflt()).isEqualTo("dflt"); assertThat(new DefaultMethodTransitivelyFromSeparateJava8Target().dflt()).isEqualTo("dflt"); assertThat(new DefaultMethodFromSeparateJava8TargetOverridden().dflt()).isEqualTo("override"); } }