Add test illustrating pinning of unrelated classes for keep patterns
In all cases specified, A should not be pinned (illustrated by the
baseline of not using if - or no rule at all)
Bug: b/316100042
Change-Id: Ie91c0ffdd13e692904ad701e8b4144a0ec6ed3b8
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/PinningStarPatternTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/PinningStarPatternTest.java
new file mode 100644
index 0000000..c9443a2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/PinningStarPatternTest.java
@@ -0,0 +1,145 @@
+// Copyright (c) 2023, 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.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 PinningStarPatternTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultDexRuntime().build();
+ }
+
+ public static final List<Class<?>> EXPECTED_ABSENT = ImmutableList.of(A.class);
+ public static final List<Class<?>> EXPECTED_PRESENT = ImmutableList.of(TestClass.class, B.class);
+
+ private TestParameters parameters;
+
+ public PinningStarPatternTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testNoKeep() throws Exception {
+ testKeepRule("", ImmutableList.of(A.class, B.class), ImmutableList.of(TestClass.class));
+ }
+
+ @Test
+ public void testNoIf() throws Exception {
+ testKeepRule("-keep class **B { *; }", EXPECTED_ABSENT, EXPECTED_PRESENT);
+ }
+
+ @Test
+ public void testR8IfStar() throws Exception {
+ // TODO(b/316100042): We should not keep A.class.
+ testKeepRule(
+ "-if class * -keep class **B { *; }",
+ ImmutableList.of(),
+ ImmutableList.of(B.class, TestClass.class, A.class));
+ }
+
+ @Test
+ public void testR8IfStarWithMethod() throws Exception {
+ // TODO(b/316100042): We should not keep A.class.
+ testKeepRule(
+ "-if class * { z(); } -keep class **B { *; }",
+ ImmutableList.of(),
+ ImmutableList.of(B.class, TestClass.class, A.class));
+ }
+
+ @Test
+ public void testR8IfStarWithEmptyMethod() throws Exception {
+ // TODO(b/316100042): We should not keep A.class.
+ // We should also not keep B, since y() is dead (empty).
+ testKeepRule(
+ "-if class * { y(); } -keep class **B { *; }",
+ ImmutableList.of(),
+ ImmutableList.of(B.class, TestClass.class, A.class));
+ }
+
+ @Test
+ public void testR8IfStarField() throws Exception {
+ // TODO(b/316100042): We should not keep A.class.
+ // We should also not keep B, since the only_read field is dead.
+ testKeepRule(
+ "-if class * { int only_read; } -keep class **B { *; }",
+ ImmutableList.of(),
+ ImmutableList.of(B.class, TestClass.class, A.class));
+ }
+
+ private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+ throws IOException, ExecutionException, CompilationFailedException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, B.class, TestClass.class)
+ .addKeepRules(keepRule)
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42", "0")
+ .inspect(
+ inspector -> {
+ absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+ present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+ });
+ }
+
+ static class A {
+ boolean use = true;
+
+ public A() {
+ if (System.currentTimeMillis() == 0) {
+ use = false;
+ }
+ }
+
+ public void bar() {
+ if (use) {
+ System.out.println(42);
+ }
+ }
+ }
+
+ static class B {
+ public static void foo() {
+ System.out.println(0);
+ }
+ }
+
+ static class TestClass {
+
+ public static int only_read = 88;
+
+ public void z() {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println("foobar");
+ }
+ }
+
+ public void y() {}
+
+ public static void main(String[] args) {
+ new TestClass().z();
+ new TestClass().y();
+ int a = TestClass.only_read;
+ new A().bar();
+ B.foo();
+ }
+ }
+}