Regression test for conditional keep rule.

Bug: 160136641
Change-Id: Ia2fe9e5621feb59d189ffe77a8b3f46235e99955
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ifrules/ConditionalOnInlinedFieldTest.java b/src/test/java/com/android/tools/r8/compatproguard/ifrules/ConditionalOnInlinedFieldTest.java
new file mode 100644
index 0000000..efc344e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatproguard/ifrules/ConditionalOnInlinedFieldTest.java
@@ -0,0 +1,122 @@
+// Copyright (c) 2020, 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.compatproguard.ifrules;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.shaking.methods.MethodsTestBase.Shrinker;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+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 ConditionalOnInlinedFieldTest extends TestBase {
+
+  static class A {
+
+    int field = 42;
+
+    void method(String name, int field) throws Exception {
+      if (field == 42) {
+        Class<?> clazz = Class.forName(name);
+        Object object = clazz.getDeclaredConstructor().newInstance();
+        clazz.getDeclaredMethod("method").invoke(object);
+      }
+    }
+  }
+
+  static class B {
+    void method() {
+      System.out.println("B::method");
+    }
+  }
+
+  static class MainWithFieldReference {
+
+    public static void main(String[] args) throws Exception {
+      A a = new A();
+      a.method(args[0], a.field);
+    }
+  }
+
+  static class MainWithoutFieldReference {
+
+    public static void main(String[] args) throws Exception {
+      A a = new A();
+      a.method(args[0], 42);
+    }
+  }
+
+  private static String EXPECTED = StringUtils.lines("B::method");
+
+  @Parameters(name = "{0}, {1}, ref:{2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        Shrinker.values(), getTestParameters().withCfRuntimes().build(), BooleanUtils.values());
+  }
+
+  private final Shrinker shrinker;
+  private final TestParameters parameters;
+  private final boolean withFieldReference;
+
+  public ConditionalOnInlinedFieldTest(
+      Shrinker shrinker, TestParameters parameters, boolean withFieldReference) {
+    this.shrinker = shrinker;
+    this.parameters = parameters;
+    this.withFieldReference = withFieldReference;
+  }
+
+  private Class<?> getMain() {
+    return withFieldReference ? MainWithFieldReference.class : MainWithoutFieldReference.class;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForJvm()
+        .addProgramClasses(getMain(), A.class, B.class)
+        .run(parameters.getRuntime(), getMain(), B.class.getTypeName())
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private TestShrinkerBuilder<?, ?, ?, ?, ?> buildShrinker() throws Exception {
+    TestShrinkerBuilder<?, ?, ?, ?, ?> builder;
+    if (shrinker == Shrinker.Proguard) {
+      builder =
+          testForProguard()
+              .addKeepRules("-dontwarn " + ConditionalOnInlinedFieldTest.class.getTypeName());
+    } else if (shrinker == Shrinker.R8Compat) {
+      builder = testForR8Compat(parameters.getBackend());
+    } else {
+      builder = testForR8(parameters.getBackend());
+    }
+    return builder
+        .addProgramClasses(getMain(), A.class, B.class)
+        .addKeepMainRule(getMain())
+        .addKeepRules(
+            "-if class "
+                + A.class.getTypeName()
+                + " { int field; }"
+                + " -keep class "
+                + B.class.getTypeName()
+                + " { void <init>(); void method(); }");
+  }
+
+  @Test
+  public void testConditionalOnField() throws Exception {
+    TestRunResult<?> result =
+        buildShrinker().compile().run(parameters.getRuntime(), getMain(), B.class.getTypeName());
+    if (!withFieldReference && shrinker != Shrinker.Proguard) {
+      // Without the reference we expect an error. For some reason PG keeps in any case.
+      result.assertFailureWithErrorThatThrows(ClassNotFoundException.class);
+    } else {
+      result.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ifrules/ConditionalOnInlinedTest.java b/src/test/java/com/android/tools/r8/compatproguard/ifrules/ConditionalOnInlinedTest.java
new file mode 100644
index 0000000..faa9eda
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compatproguard/ifrules/ConditionalOnInlinedTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2020, 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.compatproguard.ifrules;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.shaking.methods.MethodsTestBase.Shrinker;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+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 ConditionalOnInlinedTest extends TestBase {
+
+  static class A {
+    void method(String name) throws Exception {
+      Class<?> clazz = Class.forName(name);
+      Object object = clazz.getDeclaredConstructor().newInstance();
+      clazz.getDeclaredMethod("method").invoke(object);
+    }
+  }
+
+  static class B {
+    void method() {
+      System.out.println("B::method");
+    }
+  }
+
+  static class Main {
+    public static void main(String[] args) throws Exception {
+      new A().method(args[0]);
+    }
+  }
+
+  private static Class<?> MAIN_CLASS = Main.class;
+  private static Collection<Class<?>> CLASSES = ImmutableList.of(MAIN_CLASS, A.class, B.class);
+
+  private static String EXPECTED = StringUtils.lines("B::method");
+
+  @Parameters(name = "{0}, {1}")
+  public static List<Object[]> data() {
+    return buildParameters(Shrinker.values(), getTestParameters().withCfRuntimes().build());
+  }
+
+  private final Shrinker shrinker;
+  private final TestParameters parameters;
+
+  public ConditionalOnInlinedTest(Shrinker shrinker, TestParameters parameters) {
+    this.shrinker = shrinker;
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForJvm()
+        .addProgramClasses(CLASSES)
+        .run(parameters.getRuntime(), MAIN_CLASS, B.class.getTypeName())
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private TestShrinkerBuilder<?, ?, ?, ?, ?> buildShrinker() throws Exception {
+    TestShrinkerBuilder<?, ?, ?, ?, ?> builder;
+    if (shrinker == Shrinker.Proguard) {
+      builder =
+          testForProguard()
+              .addKeepRules("-dontwarn " + ConditionalOnInlinedTest.class.getTypeName());
+    } else if (shrinker == Shrinker.R8Compat) {
+      builder = testForR8Compat(parameters.getBackend());
+    } else {
+      builder = testForR8(parameters.getBackend());
+    }
+    return builder.addProgramClasses(CLASSES).addKeepMainRule(MAIN_CLASS);
+  }
+
+  @Test
+  public void testConditionalOnClass() throws Exception {
+    TestRunResult<?> result =
+        buildShrinker()
+            .addKeepRules(
+                "-if class "
+                    + A.class.getTypeName()
+                    + " -keep class "
+                    + B.class.getTypeName()
+                    + " { void <init>(); void method(); }")
+            .compile()
+            .run(parameters.getRuntime(), MAIN_CLASS, B.class.getTypeName());
+    if (shrinker != Shrinker.Proguard) {
+      // TODO(b/160136641): The conditional rule fails to apply after class A is inlined/removed.
+      result.assertFailureWithErrorThatThrows(ClassNotFoundException.class);
+    } else {
+      result.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+
+  @Test
+  public void testConditionalOnClassAndMethod() throws Exception {
+    TestRunResult<?> result =
+        buildShrinker()
+            .addKeepRules(
+                "-if class "
+                    + A.class.getTypeName()
+                    + " { void method(java.lang.String); }"
+                    + " -keep class "
+                    + B.class.getTypeName()
+                    + " { void <init>(); void method(); }")
+            .compile()
+            .run(parameters.getRuntime(), MAIN_CLASS, B.class.getTypeName());
+    if (shrinker == Shrinker.Proguard) {
+      // In this case PG appears to not actually keep the consequent, but it does remain renamed
+      // in the output.
+      result.assertFailureWithErrorThatThrows(ClassNotFoundException.class);
+    } else {
+      result.assertSuccessWithOutput(EXPECTED);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index ac30cb7..43ba1fb 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -48,6 +48,7 @@
 import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -308,6 +309,10 @@
     return builder.build();
   }
 
+  public FieldSubject field(Field field) {
+    return field(Reference.fieldFromField(field));
+  }
+
   public FieldSubject field(FieldReference field) {
     ClassSubject clazz = clazz(field.getHolderClass());
     if (!clazz.isPresent()) {