blob: 690052b6ca1325b21019f982ee9a72b7d0708450 [file] [log] [blame]
// Copyright (c) 2018, 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.shaking.defaultmethods;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
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.Parameters;
@RunWith(Parameterized.class)
public class DefaultMethodsTest extends TestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public DefaultMethodsTest(TestParameters parameters) {
this.parameters = parameters;
}
private void runTest(List<String> additionalKeepRules, Consumer<CodeInspector> inspection)
throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(
InterfaceWithDefaultMethods.class, ClassImplementingInterface.class, TestClass.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(TestClass.class)
.addKeepRules(additionalKeepRules)
.noMinification()
.compile()
.inspect(inspection);
}
private void interfaceNotKept(CodeInspector inspector) {
assertFalse(inspector.clazz(InterfaceWithDefaultMethods.class).isPresent());
}
private void defaultMethodNotKept(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
assertThat(clazz, isPresent());
assertThat(clazz.method("int", "method", ImmutableList.of()), not(isPresent()));
}
private void defaultMethodKept(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
assertThat(clazz, isPresent());
MethodSubject method = clazz.method("int", "method", ImmutableList.of());
assertThat(method, isPresent());
ClassSubject companionClass = clazz.toCompanionClass();
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
assertThat(method, not(isAbstract()));
assertThat(companionClass, not(isPresent()));
} else {
assertThat(method, isAbstract());
assertThat(companionClass, isPresent());
MethodSubject defaultMethod = method.toMethodOnCompanionClass();
assertThat(defaultMethod, isPresent());
}
}
private void defaultMethodKeptWithoutCompanionClass(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
assertThat(clazz, isPresent());
MethodSubject method = clazz.method("int", "method", ImmutableList.of());
assertThat(method, isPresent());
ClassSubject companionClass = clazz.toCompanionClass();
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
assertThat(method, not(isAbstract()));
} else {
assertThat(method, isAbstract());
}
assertThat(companionClass, not(isPresent()));
}
@Test
public void testInterfaceNotKept() throws Exception {
runTest(ImmutableList.of(), this::interfaceNotKept);
}
@Test
public void testDefaultMethodNotKept() throws Exception {
runTest(
ImmutableList.of(
"-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{", "}"),
this::defaultMethodNotKept);
}
@Test
public void testDefaultMethodKeptWithMethods() throws Exception {
runTest(
ImmutableList.of(
"-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{",
" <methods>;",
"}"),
this::defaultMethodKept);
}
@Test
public void testDefaultMethodsKeptExplicitly() throws Exception {
runTest(
ImmutableList.of(
"-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{",
" public int method();",
"}"),
this::defaultMethodKept);
}
@Test
public void testDefaultMethodNotKeptIndirectly() throws Exception {
runTest(
ImmutableList.of(
"-keep class " + ClassImplementingInterface.class.getTypeName() + "{",
" <methods>;",
"}",
// Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
"-keep class " + InterfaceWithDefaultMethods.class.getTypeName()),
this::defaultMethodNotKept);
}
@Test
public void testDefaultMethodKeptIndirectly() throws Exception {
runTest(
ImmutableList.of(
"-keep class " + ClassImplementingInterface.class.getTypeName() + "{",
" <methods>;",
"}",
"-keep class " + TestClass.class.getCanonicalName() + "{",
" public void useInterfaceMethod();",
"}",
// Prevent InterfaceWithDefaultMethods from being merged into ClassImplementingInterface
"-keep class " + InterfaceWithDefaultMethods.class.getTypeName()),
this::defaultMethodKeptWithoutCompanionClass);
}
}