| // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| package com.android.tools.r8; | |
| import static org.junit.Assert.assertTrue; | |
| import com.android.tools.r8.utils.AndroidApiLevel; | |
| import com.android.tools.r8.utils.OffOrAuto; | |
| import java.nio.file.Path; | |
| import java.util.function.UnaryOperator; | |
| import org.junit.Assert; | |
| import org.junit.Assume; | |
| import org.junit.Test; | |
| public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> { | |
| class D8TestRunner extends TestRunner<D8TestRunner> { | |
| D8TestRunner(String testName, String packageName, String mainClass) { | |
| super(testName, packageName, mainClass); | |
| } | |
| @Override | |
| D8TestRunner withMinApiLevel(AndroidApiLevel minApiLevel) { | |
| return withBuilderTransformation(builder -> builder.setMinApiLevel(minApiLevel.getLevel())); | |
| } | |
| D8TestRunner withClasspath(Path... classpath) { | |
| return withBuilderTransformation(b -> b.addClasspathFiles(classpath)); | |
| } | |
| @Override | |
| void build(Path inputFile, Path out, OutputMode mode) throws CompilationFailedException { | |
| D8Command.Builder builder = D8Command.builder().setOutput(out, mode); | |
| for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) { | |
| builder = transformation.apply(builder); | |
| } | |
| builder.addLibraryFiles( | |
| ToolHelper.getAndroidJar( | |
| androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel())); | |
| builder.addProgramFiles(inputFile); | |
| ToolHelper.runD8(builder, this::combinedOptionConsumer); | |
| } | |
| D8TestRunner withIntermediate(boolean intermediate) { | |
| return withBuilderTransformation(builder -> builder.setIntermediate(intermediate)); | |
| } | |
| @Override | |
| D8TestRunner self() { | |
| return this; | |
| } | |
| } | |
| @Test | |
| public void testDefaultInInterfaceWithoutDesugaring() throws Throwable { | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("testDefaultInInterfaceWithoutDesugaring", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Off) | |
| .withMinApiLevel(AndroidApiLevel.K); | |
| try { | |
| lib1.build(); | |
| // compilation should have failed on CompilationError since A is declaring a default method. | |
| Assert.fail(); | |
| } catch (CompilationFailedException e) { | |
| // Expected. | |
| } | |
| } | |
| @Test | |
| public void testMissingInterfaceDesugared() throws Throwable { | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(AndroidApiLevel.K); | |
| lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(AndroidApiLevel.K); | |
| lib2.build(); | |
| // test: class ImplementMethodsWithDefault implements A, B {} should get its foo implementation | |
| // from B. | |
| // test is compiled with incomplete classpath: lib2 is missing so ImplementMethodsWithDefault is | |
| // missing one of it interfaces. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest1", "desugaringwithmissingclasstest1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(AndroidApiLevel.K); | |
| test.build(); | |
| // TODO check compilation warnings are correctly reported | |
| // B is missing so compiled code makes no sense, no need to test execution. | |
| } | |
| @Test | |
| public void testMissingInterfaceDesugared2AndroidK() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.K; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib2Dex = lib2.build(); | |
| // lib3: class C implements A {} | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // test: class ImplementMethodsWithDefault extends C implements B should get its foo | |
| // implementation from B. | |
| // test is compiled with incomplete classpath: lib2 and lib3 are missing so | |
| // ImplementMethodsWithDefault is missing all its hierarchy. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| // TODO check compilation warnings are correctly reported | |
| // Missing interface B is causing the wrong code to be executed. | |
| if (!ToolHelper.artSupported() && !ToolHelper.compareAgaintsGoldenFiles()) { | |
| return; | |
| } | |
| if (ToolHelper.artSupported() && !ToolHelper.compareAgaintsGoldenFiles()) { | |
| thrown.expect(AssertionError.class); | |
| } | |
| execute( | |
| "testMissingInterfaceDesugared2AndroidK", | |
| "desugaringwithmissingclasstest2.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); | |
| } | |
| @Test | |
| public void testMissingInterfaceDesugared2AndroidO() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.O; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib2Dex = lib2.build(); | |
| // lib3: class C implements A {} | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // test: class ImplementMethodsWithDefault extends C implements B should get its foo | |
| // implementation from B. | |
| // test is compiled with incomplete classpath: lib2 and lib3 are missing so | |
| // ImplementMethodsWithDefault is missing all its hierarchy. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| execute( | |
| "testMissingInterfaceDesugared2AndroidO", | |
| "desugaringwithmissingclasstest2.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); | |
| } | |
| @Test | |
| public void testCallToMissingSuperInterfaceDesugaredAndroidK() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.K; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib2Dex = lib2.build(); | |
| // lib3: class C implements A {} | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // test: class ImplementMethodsWithDefault extends C implements B | |
| // { String getB() { return B.super.foo(); } | |
| // Should be able to call implementation from B. | |
| // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar(), lib3.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| // TODO check compilation warnings are correctly reported | |
| Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles()); | |
| // Missing interface B is causing the wrong method to be executed. | |
| if (ToolHelper.artSupported() && !ToolHelper.compareAgaintsGoldenFiles()) { | |
| thrown.expect(AssertionError.class); | |
| } | |
| execute( | |
| "testCallToMissingSuperInterfaceDesugaredAndroidK", | |
| "desugaringwithmissingclasstest3.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); | |
| } | |
| @Test | |
| public void testCallToMissingSuperInterfaceDesugaredAndroidO() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.O; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib2Dex = lib2.build(); | |
| // lib3: class C implements A {} | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // test: class ImplementMethodsWithDefault extends C implements B | |
| // { String getB() { return B.super.foo(); } | |
| // Should be able to call implementation from B. | |
| // test is compiled with incomplete classpath: lib2, i.e. B definition, is missing. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest3", "desugaringwithmissingclasstest3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar(), lib3.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| execute( | |
| "testCallToMissingSuperInterfaceDesugaredAndroidO", | |
| "desugaringwithmissingclasstest3.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); | |
| } | |
| @Test | |
| public void testMissingSuperDesugaredAndroidK() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.K; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| lib2.build(); | |
| // lib3: class C implements A {} | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| lib3.build(); | |
| // test: class ImplementMethodsWithDefault extends C implements B should get its foo | |
| // implementation from B. | |
| // test is compiled with incomplete classpath: lib3 is missing so | |
| // ImplementMethodsWithDefault is missing its super class. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withClasspath(lib2.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| try { | |
| test.build(); | |
| Assert.fail("Expected build to fail with CompilationFailedException"); | |
| } catch (CompilationFailedException e) { | |
| String message = e.getCause().getMessage(); | |
| assertTrue(message.contains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault")); | |
| assertTrue(message.contains("desugaringwithmissingclasslib3.C")); | |
| } | |
| } | |
| @Test | |
| public void testMissingSuperDesugaredAndroidO() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.O; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib2: interface B extends A { default String foo() { return "B"; } } | |
| // lib2 is compiled with full classpath | |
| D8TestRunner lib2 = | |
| test("desugaringwithmissingclasslib2", "desugaringwithmissingclasslib2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib2Dex = lib2.build(); | |
| // lib3: class C implements A {} | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // test: class ImplementMethodsWithDefault extends C implements B should get its foo | |
| // implementation from B. | |
| // test is compiled with incomplete classpath: lib3 is missing so | |
| // ImplementMethodsWithDefault is missing its super class. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest2", "desugaringwithmissingclasstest2", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withClasspath(lib2.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| execute( | |
| "testMissingSuperDesugaredAndroidO", | |
| "desugaringwithmissingclasstest2.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib2.getInputJar(), lib3.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib2Dex, lib3Dex, testDex}); | |
| } | |
| @Test | |
| public void testMissingSuperDesugaredWithLibInterfaceAndroidK() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.K; | |
| // Reference case: there's nothing to do, we should not complain. | |
| // Class MissingSuperImplementIterator extends C implements Iterator should not require | |
| // desugaring because Iterator has no default method at this API level. | |
| // The test is compiled with incomplete classpath: lib3 is missing so | |
| // MissingSuperImplementIterator is missing its super class. | |
| test("desugaringwithmissingclasstest6", "desugaringwithmissingclasstest6", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withAndroidJar(AndroidApiLevel.K) | |
| .withMinApiLevel(minApi) | |
| .build(); | |
| // More litigious case, D8 needs to detect that the default method is part of bootclasspath. | |
| // Class MissingSuperImplementIterator extends C implements Iterator should not require | |
| // desugaring of Iterator default method because it is part of the Android API and thus is | |
| // declaring the default method only when default methods are supported by the runtime. | |
| // test is compiled with incomplete classpath: lib3 is missing so | |
| // MissingSuperImplementIterator is missing its super class. | |
| test("desugaringwithmissingclasstest6", "desugaringwithmissingclasstest6", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withAndroidJar(AndroidApiLevel.N) | |
| .withMinApiLevel(minApi) | |
| .build(); | |
| } | |
| @Test | |
| public void testMissingSuperDesugaredWithProgramCrossImplementationAndroidK() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.K; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| // interface A2 { default String foo2() { return "A2"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib3: class C { /* content irrelevant } | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // test: class C2 extends C { public String foo2() { return "C2"; } } | |
| // class ImplementMethodsWithDefault extends C2 implements A, A2 { | |
| // public String foo() { return "ImplementMethodsWithDefault"; } | |
| // } | |
| // test is compiled with incomplete classpath: lib3 is missing so | |
| // C2 is missing its super class. But desugaring should be OK since all | |
| // interface methods are explicitly defined in program classes of the hierarchy. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| execute( | |
| "testMissingSuperDesugaredAndroidKWithCrossImplementation", | |
| "desugaringwithmissingclasstest4.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib3.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib3Dex, testDex}); | |
| } | |
| @Test | |
| public void testMissingSuperDesugaredWithClasspathCrossImplementationAndroidK() throws Throwable { | |
| AndroidApiLevel minApi = AndroidApiLevel.K; | |
| // lib1: interface A { default String foo() { return "A"; } } | |
| // interface A2 { default String foo2() { return "A2"; } } | |
| D8TestRunner lib1 = | |
| test("desugaringwithmissingclasslib1", "desugaringwithmissingclasslib1", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withMinApiLevel(minApi); | |
| Path lib1Dex = lib1.build(); | |
| // lib3: class C { /* content irrelevant } | |
| // lib3 is compiled with full classpath | |
| D8TestRunner lib3 = | |
| test("desugaringwithmissingclasslib3", "desugaringwithmissingclasslib3", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib3Dex = lib3.build(); | |
| // lib4: class C2 extends C { public String foo2() { return "C2"; } } | |
| // lib4 is compiled with full classpath | |
| D8TestRunner lib4 = | |
| test("desugaringwithmissingclasslib4", "desugaringwithmissingclasslib4", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar(), lib3.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path lib4Dex = lib4.build(); | |
| // test: class ImplementMethodsWithDefault extends C2 implements A, A2 { | |
| // public String foo() { return "ImplementMethodsWithDefault"; } | |
| // } | |
| // test is compiled with incomplete classpath: lib3 is missing so | |
| // C2 is missing its super class. But desugaring should be OK since all | |
| // interface methods are explicitly defined in program classes of the hierarchy. | |
| D8TestRunner test = | |
| test("desugaringwithmissingclasstest4", "desugaringwithmissingclasstest4", "N/A") | |
| .withInterfaceMethodDesugaring(OffOrAuto.Auto) | |
| .withClasspath(lib1.getInputJar(), lib4.getInputJar()) | |
| .withMinApiLevel(minApi); | |
| Path testDex = test.build(); | |
| execute( | |
| "testMissingSuperDesugaredAndroidKWithCrossImplementation", | |
| "desugaringwithmissingclasstest4.Main", | |
| new Path[] { | |
| lib1.getInputJar(), lib3.getInputJar(), lib4.getInputJar(), test.getInputJar() | |
| }, | |
| new Path[] {lib1Dex, lib3Dex, lib4Dex, testDex}); | |
| } | |
| @Override | |
| D8TestRunner test(String testName, String packageName, String mainClass) { | |
| return new D8TestRunner(testName, packageName, mainClass); | |
| } | |
| } |