1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package com.google.devtools.build.android.desugar; 15 16 import static com.google.common.truth.Truth.assertThat; 17 import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; 18 19 import com.google.common.collect.ImmutableList; 20 import com.google.devtools.build.android.desugar.testdata.java8.AnnotationsOfDefaultMethodsShouldBeKept.AnnotatedInterface; 21 import com.google.devtools.build.android.desugar.testdata.java8.AnnotationsOfDefaultMethodsShouldBeKept.SomeAnnotation; 22 import com.google.devtools.build.android.desugar.testdata.java8.ConcreteDefaultInterfaceWithLambda; 23 import com.google.devtools.build.android.desugar.testdata.java8.ConcreteOverridesDefaultWithLambda; 24 import com.google.devtools.build.android.desugar.testdata.java8.DefaultInterfaceMethodWithStaticInitializer; 25 import com.google.devtools.build.android.desugar.testdata.java8.DefaultInterfaceWithBridges; 26 import com.google.devtools.build.android.desugar.testdata.java8.DefaultMethodFromSeparateJava8Target; 27 import com.google.devtools.build.android.desugar.testdata.java8.DefaultMethodFromSeparateJava8TargetOverridden; 28 import com.google.devtools.build.android.desugar.testdata.java8.DefaultMethodTransitivelyFromSeparateJava8Target; 29 import com.google.devtools.build.android.desugar.testdata.java8.FunctionWithDefaultMethod; 30 import com.google.devtools.build.android.desugar.testdata.java8.FunctionalInterfaceWithInitializerAndDefaultMethods; 31 import com.google.devtools.build.android.desugar.testdata.java8.GenericDefaultInterfaceWithLambda; 32 import com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod; 33 import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithDefaultMethod; 34 import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithDuplicateMethods.ClassWithDuplicateMethods; 35 import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithInheritedMethods; 36 import com.google.devtools.build.android.desugar.testdata.java8.Java7InterfaceWithBridges; 37 import com.google.devtools.build.android.desugar.testdata.java8.Named; 38 import com.google.devtools.build.android.desugar.testdata.java8.TwoInheritedDefaultMethods; 39 import com.google.devtools.build.android.desugar.testdata.java8.VisibilityTestClass; 40 import java.lang.annotation.Annotation; 41 import java.lang.reflect.Modifier; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.function.Function; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 import org.junit.runners.JUnit4; 48 49 /** 50 * Test that exercises classes in the {@code testdata_java8} package. This is meant to be run 51 * against a desugared version of those classes, which in turn exercise various desugaring features. 52 */ 53 @RunWith(JUnit4.class) 54 public class DesugarJava8FunctionalTest extends DesugarFunctionalTest { 55 DesugarJava8FunctionalTest()56 public DesugarJava8FunctionalTest() { 57 this(true, true); 58 } 59 DesugarJava8FunctionalTest( boolean expectBridgesFromSeparateTarget, boolean expectDefaultMethods)60 protected DesugarJava8FunctionalTest( 61 boolean expectBridgesFromSeparateTarget, boolean expectDefaultMethods) { 62 super(expectBridgesFromSeparateTarget, expectDefaultMethods); 63 } 64 65 @Test testLambdaInDefaultMethod()66 public void testLambdaInDefaultMethod() { 67 assertThat(new ConcreteDefaultInterfaceWithLambda().defaultWithLambda()) 68 .containsExactly("0", "1") 69 .inOrder(); 70 } 71 72 @Test testLambdaInDefaultCallsInterfaceMethod()73 public void testLambdaInDefaultCallsInterfaceMethod() { 74 assertThat(new ConcreteDefaultInterfaceWithLambda().defaultCallsInterfaceMethod()) 75 .containsExactly("1", "2") 76 .inOrder(); 77 } 78 79 @Test testOverrideLambdaInDefault()80 public void testOverrideLambdaInDefault() { 81 assertThat(new ConcreteOverridesDefaultWithLambda().defaultWithLambda()) 82 .containsExactly("2", "3") 83 .inOrder(); 84 } 85 86 @Test testLambdaInDefaultCallsOverrideMethod()87 public void testLambdaInDefaultCallsOverrideMethod() { 88 assertThat(new ConcreteOverridesDefaultWithLambda().defaultCallsInterfaceMethod()) 89 .containsExactly("3", "4") 90 .inOrder(); 91 } 92 93 @Test testDefaultInterfaceMethodReference()94 public void testDefaultInterfaceMethodReference() { 95 InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); 96 List<String> dest = 97 methodrefUse.defaultMethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); 98 assertThat(dest).containsExactly("Sergey"); 99 } 100 101 @Test testStaticInterfaceMethodReference()102 public void testStaticInterfaceMethodReference() { 103 InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); 104 List<String> dest = 105 methodrefUse.staticMethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); 106 assertThat(dest).containsExactly("Alex"); 107 } 108 109 @Test testLambdaCallsDefaultMethod()110 public void testLambdaCallsDefaultMethod() { 111 InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); 112 List<String> dest = 113 methodrefUse.lambdaCallsDefaultMethod(ImmutableList.of("Sergey", "Larry", "Alex")); 114 assertThat(dest).containsExactly("Sergey"); 115 } 116 117 @Test testBootclasspathMethodInvocations()118 public void testBootclasspathMethodInvocations() { 119 InterfaceMethod concrete = new InterfaceMethod.Concrete(); 120 assertThat(concrete.defaultInvokingBootclasspathMethods("Larry")).isEqualTo("Larry"); 121 } 122 123 @Test testStaticMethodsInInterface_explicitAndLambdaBody()124 public void testStaticMethodsInInterface_explicitAndLambdaBody() { 125 List<Long> result = FunctionWithDefaultMethod.DoubleInts.add(ImmutableList.of(7, 39, 8), 3); 126 assertThat(result).containsExactly(10L, 42L, 11L).inOrder(); 127 } 128 129 @Test testOverriddenDefaultMethod_inHandwrittenClass()130 public void testOverriddenDefaultMethod_inHandwrittenClass() { 131 FunctionWithDefaultMethod<Integer> doubler = new FunctionWithDefaultMethod.DoubleInts(); 132 assertThat(doubler.apply(7)).isEqualTo(14); 133 assertThat(doubler.twice(7)).isEqualTo(35); 134 } 135 136 @Test testOverriddenDefaultMethod_inHandwrittenSuperclass()137 public void testOverriddenDefaultMethod_inHandwrittenSuperclass() { 138 FunctionWithDefaultMethod<Integer> doubler = new FunctionWithDefaultMethod.DoubleInts2(); 139 assertThat(doubler.apply(7)).isEqualTo(14); 140 assertThat(doubler.twice(7)).isEqualTo(35); 141 } 142 143 @Test testInheritedDefaultMethod_inLambda()144 public void testInheritedDefaultMethod_inLambda() { 145 FunctionWithDefaultMethod<Integer> doubler = 146 FunctionWithDefaultMethod.DoubleInts.doubleLambda(); 147 assertThat(doubler.apply(7)).isEqualTo(14); 148 assertThat(doubler.twice(7)).isEqualTo(28); 149 } 150 151 @Test testDefaultMethodReference_onLambda()152 public void testDefaultMethodReference_onLambda() { 153 FunctionWithDefaultMethod<Integer> plus6 = FunctionWithDefaultMethod.DoubleInts.incTwice(3); 154 assertThat(plus6.apply(18)).isEqualTo(24); 155 assertThat(plus6.twice(18)).isEqualTo(30); 156 } 157 158 @Test testDefaultMethodReference_onHandwrittenClass()159 public void testDefaultMethodReference_onHandwrittenClass() { 160 FunctionWithDefaultMethod<Integer> times5 = FunctionWithDefaultMethod.DoubleInts.times5(); 161 assertThat(times5.apply(6)).isEqualTo(30); 162 assertThat(times5.twice(6)).isEqualTo(150); // Irrelevant that DoubleInts overrides twice() 163 } 164 165 @Test testStaticInterfaceMethodReferenceReturned()166 public void testStaticInterfaceMethodReferenceReturned() { 167 Function<Integer, FunctionWithDefaultMethod<Integer>> factory = 168 FunctionWithDefaultMethod.DoubleInts.incFactory(); 169 assertThat(factory.apply(6).apply(7)).isEqualTo(13); 170 assertThat(factory.apply(6).twice(7)).isEqualTo(19); 171 } 172 173 @Test testSuperDefaultMethodInvocation()174 public void testSuperDefaultMethodInvocation() { 175 assertThat(new TwoInheritedDefaultMethods().name()).isEqualTo("One:Two"); 176 assertThat(new Named.DefaultName().name()).isEqualTo("DefaultName-once"); 177 assertThat(new Named.DefaultNameSubclass().name()).isEqualTo("DefaultNameSubclass-once-twice"); 178 } 179 180 @Test testInheritedPreferredOverDefault()181 public void testInheritedPreferredOverDefault() throws Exception { 182 assertThat(new Named.ExplicitName("hello").name()).isEqualTo("hello"); 183 // Make sure AbstractName remains abstract, despite default method from implemented interface 184 assertThat(Modifier.isAbstract(Named.AbstractName.class.getMethod("name").getModifiers())) 185 .isTrue(); 186 } 187 188 @Test testRedefinedDefaultMethod()189 public void testRedefinedDefaultMethod() throws Exception { 190 assertThat(new InterfaceWithDefaultMethod.Version2().version()).isEqualTo(2); 191 } 192 193 @Test testDefaultMethodRedefinedInSubclass()194 public void testDefaultMethodRedefinedInSubclass() throws Exception { 195 assertThat(new InterfaceWithDefaultMethod.AlsoVersion2().version()).isEqualTo(2); 196 } 197 198 @Test testDefaultMethodVisibility()199 public void testDefaultMethodVisibility() { 200 assertThat(new VisibilityTestClass().m()).isEqualTo(42); 201 } 202 203 /** Test for b/38302860 */ 204 @Test testAnnotationsOfDefaultMethodsAreKept()205 public void testAnnotationsOfDefaultMethodsAreKept() throws Exception { 206 { 207 Annotation[] annotations = AnnotatedInterface.class.getAnnotations(); 208 assertThat(annotations).hasLength(1); 209 assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); 210 assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(1); 211 } 212 { 213 Annotation[] annotations = 214 AnnotatedInterface.class.getMethod("annotatedAbstractMethod").getAnnotations(); 215 assertThat(annotations).hasLength(1); 216 assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); 217 assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(2); 218 } 219 { 220 Annotation[] annotations = 221 AnnotatedInterface.class.getMethod("annotatedDefaultMethod").getAnnotations(); 222 assertThat(annotations).hasLength(1); 223 assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); 224 assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(3); 225 } 226 } 227 /** Test for b/38308515 */ 228 @Test testDefaultAndStaticMethodNameClash()229 public void testDefaultAndStaticMethodNameClash() { 230 final ClassWithDuplicateMethods instance = new ClassWithDuplicateMethods(); 231 assertThat(instance.getZero()).isEqualTo(0); 232 assertThat(instance.getZeroFromStaticInterfaceMethod()).isEqualTo(1); 233 } 234 235 /** 236 * Test for b/38257037 237 * 238 * <p>Note that, we intentionally suppress unchecked warnings, because we expect some 239 * ClassCastException to test bridge methods. 240 */ 241 @SuppressWarnings("unchecked") 242 @Test testBridgeAndDefaultMethods()243 public void testBridgeAndDefaultMethods() { 244 { 245 DefaultInterfaceWithBridges object = new DefaultInterfaceWithBridges(); 246 Integer one = 1; 247 assertThat(object.copy(one)).isEqualTo(one); 248 assertThat(object.copy((Number) one)).isEqualTo(one); 249 assertThrows(ClassCastException.class, () -> object.copy(Double.valueOf(1))); 250 251 assertThat(object.getNumber()).isInstanceOf(Double.class); 252 assertThat(object.getNumber()).isEqualTo(Double.valueOf(2.3d)); 253 assertThat(object.getDouble()).isEqualTo(Double.valueOf(2.3d)); 254 } 255 { 256 Java7InterfaceWithBridges.ClassAddTwo testObject = 257 new Java7InterfaceWithBridges.ClassAddTwo(); 258 assertThat(testObject.add(Integer.valueOf(2))).isEqualTo(4); 259 260 @SuppressWarnings("rawtypes") 261 Java7InterfaceWithBridges top = testObject; 262 assertThat(top.add(Integer.valueOf(2))).isEqualTo(4); 263 assertThrows(ClassCastException.class, () -> top.add(new Object())); 264 assertThrows(ClassCastException.class, () -> top.add(Double.valueOf(1))); 265 assertThrows(ClassCastException.class, () -> top.add(Long.valueOf(1))); 266 267 @SuppressWarnings("rawtypes") 268 Java7InterfaceWithBridges.LevelOne levelOne = testObject; 269 assertThat(levelOne.add(Integer.valueOf(2))).isEqualTo(4); 270 assertThrows(ClassCastException.class, () -> top.add(new Object())); 271 assertThrows(ClassCastException.class, () -> top.add(Double.valueOf(1))); 272 assertThrows(ClassCastException.class, () -> top.add(Long.valueOf(1))); 273 274 @SuppressWarnings("rawtypes") 275 Java7InterfaceWithBridges.LevelOne levelTwo = testObject; 276 assertThat(levelTwo.add(Integer.valueOf(2))).isEqualTo(4); 277 assertThrows(ClassCastException.class, () -> levelTwo.add(Double.valueOf(1))); 278 assertThrows(ClassCastException.class, () -> levelTwo.add(Long.valueOf(1))); 279 } 280 { 281 GenericDefaultInterfaceWithLambda.ClassTwo testObject = 282 new GenericDefaultInterfaceWithLambda.ClassTwo(); 283 284 assertThat(testObject.increment(Integer.valueOf(0))).isEqualTo(1); 285 assertThat(testObject.toString(Integer.valueOf(0))).isEqualTo("0"); 286 assertThat(testObject.getBaseValue()).isEqualTo(Integer.valueOf(0)); 287 288 assertThat(testObject.toList(0)).isEmpty(); 289 assertThat(testObject.toList(1)).containsExactly(0).inOrder(); 290 assertThat(testObject.toList(2)).containsExactly(0, 1).inOrder(); 291 292 assertThat(((Function<Integer, ArrayList<Integer>>) testObject.toListSupplier()).apply(0)) 293 .isEmpty(); 294 assertThat(((Function<Integer, ArrayList<Integer>>) testObject.toListSupplier()).apply(1)) 295 .containsExactly(0) 296 .inOrder(); 297 assertThat(((Function<Integer, ArrayList<Integer>>) testObject.toListSupplier()).apply(2)) 298 .containsExactly(0, 1) 299 .inOrder(); 300 301 assertThat(testObject.convertToStringList(ImmutableList.of(0))) 302 .containsExactly("0") 303 .inOrder(); 304 assertThat(testObject.convertToStringList(ImmutableList.of(0, 1))) 305 .containsExactly("0", "1") 306 .inOrder(); 307 308 @SuppressWarnings("rawtypes") 309 GenericDefaultInterfaceWithLambda top = testObject; 310 assertThrows(ClassCastException.class, () -> top.increment(Long.valueOf(1))); 311 assertThrows(ClassCastException.class, () -> top.toString(Long.valueOf(1))); 312 assertThat(top.increment(Integer.valueOf(0))).isEqualTo(1); 313 assertThat(top.toString(Integer.valueOf(0))).isEqualTo("0"); 314 assertThat(top.getBaseValue()).isEqualTo(Integer.valueOf(0)); 315 316 assertThat(top.toList(0)).isEmpty(); 317 assertThat(top.toList(1)).containsExactly(0).inOrder(); 318 assertThat(top.toList(2)).containsExactly(0, 1).inOrder(); 319 320 assertThat(((Function<Integer, ArrayList<Integer>>) top.toListSupplier()).apply(0)).isEmpty(); 321 assertThat(((Function<Integer, ArrayList<Integer>>) top.toListSupplier()).apply(1)) 322 .containsExactly(0) 323 .inOrder(); 324 assertThat(((Function<Integer, ArrayList<Integer>>) top.toListSupplier()).apply(2)) 325 .containsExactly(0, 1) 326 .inOrder(); 327 328 assertThat(top.convertToStringList(ImmutableList.of(0))).containsExactly("0").inOrder(); 329 assertThat(top.convertToStringList(ImmutableList.of(0, 1))) 330 .containsExactly("0", "1") 331 .inOrder(); 332 } 333 { 334 @SuppressWarnings("rawtypes") 335 GenericDefaultInterfaceWithLambda testObject = 336 new GenericDefaultInterfaceWithLambda.ClassThree(); 337 assertThat(testObject.getBaseValue()).isEqualTo(Long.valueOf(0)); 338 assertThat(testObject.increment(Long.valueOf(0))).isEqualTo(Long.valueOf(0 + 1)); 339 assertThat(testObject.toString(Long.valueOf(0))).isEqualTo(Long.valueOf(0).toString()); 340 assertThrows(ClassCastException.class, () -> testObject.increment(Integer.valueOf(0))); 341 assertThrows(ClassCastException.class, () -> testObject.toString(Integer.valueOf(0))); 342 assertThat(testObject.toList(2)).containsExactly(Long.valueOf(0), Long.valueOf(1)).inOrder(); 343 assertThat(testObject.convertToStringList(testObject.toList(1))).containsExactly("0"); 344 assertThat(((Function<Integer, ArrayList<Long>>) testObject.toListSupplier()).apply(2)) 345 .containsExactly(Long.valueOf(0), Long.valueOf(1)); 346 } 347 } 348 349 /** 350 * Test for b/62047432. 351 * 352 * <p>When desugaring a functional interface with an executable clinit and default methods, we 353 * erase the body of clinit to avoid executing it during desugaring. This test makes sure that all 354 * the constants defined in the interface are still there after desugaring. 355 */ 356 @Test testFunctionalInterfaceWithExecutableClinitStillWorkAfterDesugar()357 public void testFunctionalInterfaceWithExecutableClinitStillWorkAfterDesugar() { 358 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CONSTANT.length("").convert()) 359 .isEqualTo(0); 360 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CONSTANT.length("1").convert()) 361 .isEqualTo(1); 362 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.BOOLEAN).isFalse(); 363 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CHAR).isEqualTo('h'); 364 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.BYTE).isEqualTo(0); 365 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.SHORT).isEqualTo(0); 366 367 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.INT).isEqualTo(0); 368 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.FLOAT).isEqualTo(0f); 369 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.LONG).isEqualTo(0); 370 assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.DOUBLE).isEqualTo(0d); 371 } 372 373 /** Test for b/38255926. */ 374 @Test testDefaultMethodInitializationOrder()375 public void testDefaultMethodInitializationOrder() { 376 { 377 assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne.C().sum()) 378 .isEqualTo(11); // To trigger loading the class C and its super interfaces. 379 assertThat( 380 DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne 381 .getRealInitializationOrder()) 382 .isEqualTo( 383 DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne 384 .getExpectedInitializationOrder()); 385 } 386 { 387 assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo.C().sum()) 388 .isEqualTo(3); 389 assertThat( 390 DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo 391 .getRealInitializationOrder()) 392 .isEqualTo( 393 DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo 394 .getExpectedInitializationOrder()); 395 } 396 { 397 assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree.C().sum()) 398 .isEqualTo(11); 399 assertThat( 400 DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree 401 .getRealInitializationOrder()) 402 .isEqualTo( 403 DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree 404 .getExpectedInitializationOrder()); 405 } 406 } 407 408 /** 409 * Tests that default methods on the classpath are correctly handled. We'll also verify the 410 * metadata that's emitted for this case to make sure the binary-wide double-check for correct 411 * desugaring of default and static interface methods keeps working (b/65645388). 412 */ 413 @Test testDefaultMethodsInSeparateTarget()414 public void testDefaultMethodsInSeparateTarget() { 415 assertThat(new DefaultMethodFromSeparateJava8Target().dflt()).isEqualTo("dflt"); 416 assertThat(new DefaultMethodTransitivelyFromSeparateJava8Target().dflt()).isEqualTo("dflt"); 417 assertThat(new DefaultMethodFromSeparateJava8TargetOverridden().dflt()).isEqualTo("override"); 418 } 419 420 /** Regression test for b/73355452 */ 421 @Test testSuperCallToInheritedDefaultMethod()422 public void testSuperCallToInheritedDefaultMethod() { 423 assertThat(new InterfaceWithInheritedMethods.Impl().name()).isEqualTo("Base"); 424 assertThat(new InterfaceWithInheritedMethods.Impl().suffix()).isEqualTo("!"); 425 } 426 } 427