blob: 790ea8dd670309c05b4c8099236dc77281da7f56 [file] [log] [blame]
// Copyright (c) 2019, 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.ifrule;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import java.util.List;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ConditionalKeepIfKeptTest extends TestBase {
static final String EXPECTED =
StringUtils.lines("class " + StaticallyReferenced.class.getTypeName());
private final TestParameters parameters;
private final boolean useMarker;
@Parameterized.Parameters(name = "{0}, marker:{1}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevels().build(),
BooleanUtils.values());
}
public ConditionalKeepIfKeptTest(TestParameters parameters, boolean useMarker) {
this.parameters = parameters;
this.useMarker = useMarker;
}
private String getConditionalRulePrefix() {
String clazz = StaticallyReferenced.class.getTypeName();
return useMarker
? "-if @" + Marked.class.getTypeName() + " class * -keep class <1>"
: "-if class " + clazz + " -keep class " + clazz;
}
@Test
public void testIfKeepNoMembers() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(ConditionalKeepIfKeptTest.class)
.addKeepRules(getConditionalRulePrefix())
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class);
assertThat(classSubject, isPresent());
assertEquals(0, classSubject.allFields().size());
// TODO(b/132318799): Should not keep <init>() when not specified.
assertEquals(1, classSubject.allMethods().size());
assertTrue(classSubject.init().isPresent());
});
}
@Test
public void testIfKeepAllMembers() throws Exception {
Assume.assumeFalse("b/132318609", useMarker);
testForR8(parameters.getBackend())
.addInnerClasses(ConditionalKeepIfKeptTest.class)
.addKeepRules(getConditionalRulePrefix() + " { *; }")
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class);
assertThat(classSubject, isPresent());
assertEquals(2, classSubject.allFields().size());
assertEquals(3, classSubject.allMethods().size());
});
}
@Test
public void testIfKeepStaticMembers() throws Exception {
Assume.assumeFalse("b/132318609", useMarker);
testForR8(parameters.getBackend())
.addInnerClasses(ConditionalKeepIfKeptTest.class)
.addKeepRules(getConditionalRulePrefix() + " { static *; }")
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class);
assertThat(classSubject, isPresent());
assertEquals(1, classSubject.allFields().size());
assertEquals(1, classSubject.allMethods().size());
});
}
@Test
public void testIfKeepNonStaticMembers() throws Exception {
Assume.assumeFalse("b/132318609", useMarker);
testForR8(parameters.getBackend())
.addInnerClasses(ConditionalKeepIfKeptTest.class)
.addKeepRules(getConditionalRulePrefix() + " { !static *; }")
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class);
assertThat(classSubject, isPresent());
assertEquals(1, classSubject.allFields().size());
assertEquals(2, classSubject.allMethods().size());
});
}
@Test
public void testIfKeepFields() throws Exception {
Assume.assumeFalse("b/132318609", useMarker);
testForR8(parameters.getBackend())
.addInnerClasses(ConditionalKeepIfKeptTest.class)
.addKeepRules(getConditionalRulePrefix() + " { <fields>; }")
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class);
assertThat(classSubject, isPresent());
assertEquals(2, classSubject.allFields().size());
assertEquals(0, classSubject.allMethods().size());
});
}
@Test
public void testIfKeepMethods() throws Exception {
Assume.assumeFalse("b/132318609", useMarker);
testForR8(parameters.getBackend())
.addInnerClasses(ConditionalKeepIfKeptTest.class)
.addKeepRules(getConditionalRulePrefix() + " { <methods>; }")
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
ClassSubject classSubject = inspector.clazz(StaticallyReferenced.class);
assertThat(classSubject, isPresent());
assertEquals(3, classSubject.allMethods().size());
classSubject.allMethods().forEach(m -> assertThat(m, not(isRenamed())));
// Keeping methods will cause the fields to be kept too (but allow renaming them).
assertEquals(2, classSubject.allFields().size());
classSubject.allFields().forEach(f -> assertThat(f, isRenamed()));
});
}
@interface Marked {}
@Marked
static class StaticallyReferenced {
static long staticField;
static long staticMethod() {
return staticField += System.nanoTime();
}
long nonStaticField;
StaticallyReferenced() {
nonStaticField = System.nanoTime();
}
long nonStaticMethod() {
return nonStaticField;
}
}
static class TestClass {
public static void main(String[] args) {
System.out.println(StaticallyReferenced.class);
}
}
}