blob: 761154519156d239e1797f5e90c9a5ccc2f3cdcf [file]
// Copyright (c) 2022, 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.desugaring.interfacemethods;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class DefaultMethodInvokeSuperOnDefaultLibraryMethodTest extends TestBase {
private static final String EXPECTED_OUTPUT = StringUtils.lines("1", "2");
@Parameter(0)
public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withDexRuntimes().withPartialCompilation().build();
}
private boolean runtimeHasConsumerInterface(TestParameters parameters) {
// java,util.function.Consumer was introduced at API level 24.
return parameters.asDexRuntime().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N);
}
@Test
public void testD8WithDefaultInterfaceMethodDesugaringWithAPIInLibrary() throws Exception {
testForD8(parameters)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.addInnerClasses(getClass())
.setMinApi(AndroidApiLevel.I_MR1)
.compileWithExpectedDiagnostics(this::verifyOnlyNoSuchMethodErrorDiagnostic)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
// If the platform does not have java.util.function.Consumer the lambda instantiation
// will throw NoClassDefFoundError as it implements java.util.function.Consumer.
// Otherwise, the generated code will throw NoSuchMethodError.
runtimeHasConsumerInterface(parameters),
b -> b.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
b -> b.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
}
@Test
public void testD8WithDefaultInterfaceMethodDesugaringWithoutAPIInLibrary() throws Exception {
testForD8(parameters)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.M))
.addInnerClasses(getClass())
.setMinApi(AndroidApiLevel.I_MR1)
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics
.assertOnlyWarnings()
.assertWarningsMatch(
diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class)))
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
// If the platform does not have java.util.function.Consumer the lambda instantiation
// will throw NoClassDefFoundError as it implements java.util.function.Consumer.
// Otherwise, the generated code will throw NoSuchMethodError.
runtimeHasConsumerInterface(parameters),
b -> b.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
b -> b.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
}
@Test
public void testD8WithDefaultInterfaceMethodSupport() throws Exception {
assumeTrue(
parameters.asDexRuntime().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N));
testForD8(parameters)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.addInnerClasses(getClass())
.setMinApi(AndroidApiLevel.N)
.compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void testR8WithDefaultInterfaceMethodDesugaringWithAPIInLibrary() throws Exception {
parameters.assumeNoPartialCompilation("Requires native multi dex");
testForR8(parameters)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.addInnerClasses(getClass())
.setMinApi(AndroidApiLevel.I_MR1)
.addKeepMainRule(TestClass.class)
.allowDiagnosticWarningMessages()
.compileWithExpectedDiagnostics(this::verifyOnlyNoSuchMethodErrorDiagnostic)
.run(parameters.getRuntime(), TestClass.class)
.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
}
@Test
public void testR8WithDefaultInterfaceMethodDesugaringWithoutAPIInLibrary() throws Exception {
parameters.assumeNoPartialCompilation("Requires native multi dex");
testForR8(parameters)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.M))
.addInnerClasses(getClass())
.setMinApi(AndroidApiLevel.I_MR1)
.addKeepMainRule(TestClass.class)
.addDontWarn(Consumer.class)
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
// If the platform does not have java.util.function.Consumer the lambda instantiation
// will throw NoClassDefFoundError as it implements java.util.function.Consumer.
// Otherwise, the generated code will throw NoSuchMethodError.
runtimeHasConsumerInterface(parameters),
b -> b.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
b -> b.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
}
@Test
public void testR8WithDefaultInterfaceMethodSupport() throws Exception {
assumeTrue(
parameters.asDexRuntime().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N));
testForR8(parameters)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.addInnerClasses(getClass())
.setMinApi(AndroidApiLevel.N)
.addKeepMainRule(TestClass.class)
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
parameters.getPartialCompilationTestParameters().isRandom(),
rr ->
rr.applyIf(
rr.getExitCode() == 0,
ignore -> rr.assertSuccessWithOutput(EXPECTED_OUTPUT),
// TODO(b/404432076): Investigate if this behavior is correct.
ignore -> rr.assertFailureWithErrorThatThrows(NoSuchMethodError.class)),
rr -> rr.assertSuccessWithOutput(EXPECTED_OUTPUT));
}
private void verifyOnlyNoSuchMethodErrorDiagnostic(TestDiagnosticMessages diagnostics) {
diagnostics
.assertOnlyWarnings()
.assertWarningsMatch(
allOf(
diagnosticType(StringDiagnostic.class),
diagnosticMessage(
containsString(
"Interface method desugaring has inserted NoSuchMethodError"
+ " replacing a super call in")),
diagnosticMessage(containsString("forEachPrint"))));
}
interface IntegerIterable extends Iterable<Integer> {
default void forEachPrint() {
Iterable.super.forEach(System.out::println);
}
}
static class IntegerIterable1And2 implements IntegerIterable {
@Override
public Iterator<Integer> iterator() {
List<Integer> result = new ArrayList<>();
result.add(1);
result.add(2);
return result.iterator();
}
}
static class TestClass {
public static void main(String[] args) {
new IntegerIterable1And2().forEachPrint();
}
}
}