Tests for inconsistent conditional rules.

These tests also show a case where we seem to keep <init>() in full mode without
an explicit keep rule for it.

Bug: 132318609
Bug: 132318799
Change-Id: Ib0bfd1ed6c592af383ec79c0515c6c37580e9a2c
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java
new file mode 100644
index 0000000..7c9a06c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java
@@ -0,0 +1,195 @@
+// 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 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.Collections;
+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().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): Full mode should not keep <init>() when not specified.
+              assertEquals(1, classSubject.allMethods().size());
+              assertThat(classSubject.init(Collections.emptyList()), 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 {
+    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);
+    }
+  }
+}