Add a test for -if rules combined with vertical class merging

Bug: 110141157
Change-Id: Ide11362f0096eb6952b2abda6f83ea71ee2a6287
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index c098fb8..d5d0558 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -807,7 +807,9 @@
 
   public static R8Command.Builder prepareR8CommandBuilder(
       AndroidApp app, ProgramConsumer programConsumer) {
-    return R8Command.builder(app).setProgramConsumer(programConsumer);
+    return R8Command.builder(app)
+        .setProgramConsumer(programConsumer)
+        .setProguardMapConsumer(StringConsumer.emptyConsumer());
   }
 
   public static AndroidApp runR8(AndroidApp app) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
index e29ceae..10c64e3 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ProguardCompatabilityTestBase.java
@@ -20,10 +20,12 @@
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.function.Consumer;
 
 public class ProguardCompatabilityTestBase extends TestBase {
 
@@ -87,11 +89,18 @@
     throw new IllegalArgumentException("Unknown shrinker: " + mode);
   }
 
-  protected AndroidApp runR8Raw(List<Class> programClasses, String proguardConfig) throws Exception {
+  protected AndroidApp runR8Raw(List<Class> programClasses, String proguardConfig)
+      throws Exception {
+    return runR8Raw(programClasses, proguardConfig, null);
+  }
+
+  protected AndroidApp runR8Raw(
+      List<Class> programClasses, String proguardConfig, Consumer<InternalOptions> configure)
+      throws Exception {
     AndroidApp app = readClassesAndAndriodJar(programClasses);
     R8Command.Builder builder = ToolHelper.prepareR8CommandBuilder(app);
     builder.addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
-    return ToolHelper.runR8(builder.build());
+    return ToolHelper.runR8(builder.build(), configure);
   }
 
   protected DexInspector runR8(List<Class> programClasses, String proguardConfig) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
new file mode 100644
index 0000000..b484d73
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
@@ -0,0 +1,156 @@
+// 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.ifrule.verticalclassmerging;
+
+import static com.android.tools.r8.utils.DexInspectorMatchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatabilityTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.InternalOptions;
+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;
+
+class A {
+  int x = 1;
+  int a() throws ClassNotFoundException {
+    // Class D is expected to be kept - vertical class merging or not. The -if rule say that if
+    // the method A.a is in the output, then class D is needed.
+    String p =getClass().getPackage().getName();
+    Class.forName(p + ".D");
+    return 4;
+  }
+}
+
+class B extends A {
+  int y = 2;
+  int b() {
+    return 5;
+  }
+}
+
+class C extends B {
+  int z = 3;
+  int c() {
+    return 6;
+  }
+}
+
+class D {
+}
+
+class Main {
+  public static void main(String[] args) throws ClassNotFoundException {
+    C c = new C();
+    System.out.print("" + c.x + "" + c.y + "" + c.z + "" + c.a() + "" + c.b() + "" + c.c());
+  }
+}
+
+@RunWith(Parameterized.class)
+public class IfRuleWithVerticalClassMerging extends ProguardCompatabilityTestBase {
+  private final static List<Class> CLASSES = ImmutableList.of(
+      A.class, B.class, C.class, D.class, Main.class);
+
+  private final Shrinker shrinker;
+  private final boolean enableClassMerging;
+
+  public IfRuleWithVerticalClassMerging(Shrinker shrinker, boolean enableClassMerging) {
+    this.shrinker = shrinker;
+    this.enableClassMerging = enableClassMerging;
+  }
+
+  @Parameters(name = "shrinker: {0} classMerging: {1}")
+  public static Collection<Object[]> data() {
+    // We don't run this on Proguard, as Proguard does not merge A into B.
+    return ImmutableList.of(
+        new Object[]{Shrinker.R8, true},
+        new Object[]{Shrinker.R8, false}
+    );
+  }
+
+  private void configure(InternalOptions options) {
+    options.enableClassMerging = enableClassMerging;
+  }
+
+  @Override
+  protected AndroidApp runR8Raw(
+      List<Class> programClasses, String proguardConfig) throws Exception {
+    return super.runR8Raw(programClasses, proguardConfig, this::configure);
+  }
+
+  private void check(AndroidApp app) throws Exception {
+    DexInspector inspector = new DexInspector(app);
+    ClassSubject clazzA = inspector.clazz(A.class);
+    assertEquals(!enableClassMerging, clazzA.isPresent());
+    ClassSubject clazzB = inspector.clazz(B.class);
+    assertThat(clazzB, isPresent());
+    ClassSubject clazzD = inspector.clazz(D.class);
+    // TODO(110141157): Class D should be kept - vertical class merging or not.
+    assertEquals(!enableClassMerging, clazzD.isPresent());
+
+    ProcessResult result = runOnArtRaw(app, Main.class.getName());
+    // TODO(110141157): The code should run - vertical class merging or not.
+    assertEquals(enableClassMerging ? 1 : 0, result.exitCode);
+    if (!enableClassMerging) {
+      assertEquals("123456", result.stdout);
+    }
+  }
+
+  @Test
+  public void testMergedClassInIfRule() throws Exception {
+    // Class C is kept, meaning that it will not be touched.
+    // Class A will be merged into class B.
+    List<String> config = ImmutableList.of(
+        "-keep class **.Main { public static void main(java.lang.String[]); }",
+        "-keep class **.C",
+        "-if class **.A",
+        "-keep class **.D",
+        "-dontobfuscate"
+    );
+
+    check(runShrinkerRaw(shrinker, CLASSES, config));
+  }
+
+  @Test
+  public void testMergedClassFieldInIfRule() throws Exception {
+    // Class C is kept, meaning that it will not be touched.
+    // Class A will be merged into class B.
+    // Main.main access A.x, so that field exists satisfying the if rule.
+    List<String> config = ImmutableList.of(
+        "-keep class **.Main { public static void main(java.lang.String[]); }",
+        "-keep class **.C",
+        "-if class **.A { int x; }",
+        "-keep class **.D",
+        "-dontobfuscate"
+    );
+
+    check(runShrinkerRaw(shrinker, CLASSES, config));
+  }
+
+  @Test
+  public void testMergedClassMethodInIfRule() throws Exception {
+    // Class C is kept, meaning that it will not be touched.
+    // Class A will be merged into class B.
+    // Main.main access A.a(), that method exists satisfying the if rule.
+    List<String> config = ImmutableList.of(
+        "-keep class **.Main { public static void main(java.lang.String[]); }",
+        "-keep class **.C",
+        "-if class **.A { int a(); }",
+        "-keep class **.D",
+        "-dontobfuscate"
+    );
+
+    check(runShrinkerRaw(shrinker, CLASSES, config));
+  }
+}