Test for disallowing horizontal merging of classes in main dex list

Bug: 178460068
Change-Id: I7e057546ccd389ce364131453ad0de814c325796
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
index 94392e6..8f88d08 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainDexInliningTest.java
@@ -80,7 +80,6 @@
             .enableInliningAnnotations()
             .enableNoHorizontalClassMergingAnnotations()
             .enableNoStaticClassMergingAnnotations()
-            .noMinification()
             .setMinApi(parameters.getApiLevel())
             .compile();
 
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
new file mode 100644
index 0000000..867b1ec
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListFromGenerateMainHorizontalMergingTest.java
@@ -0,0 +1,155 @@
+// Copyright (c) 2021, 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.maindexlist;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.BeforeClass;
+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 MainDexListFromGenerateMainHorizontalMergingTest extends TestBase {
+
+  private static List<ClassReference> mainDexList;
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withDexRuntimes()
+        .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+        .build();
+  }
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    mainDexList =
+        testForMainDexListGenerator(getStaticTemp())
+            .addInnerClasses(MainDexListFromGenerateMainHorizontalMergingTest.class)
+            .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+            .addMainDexRules(
+                "-keep class " + Main.class.getTypeName() + " { public static void foo(); }")
+            .run()
+            .getMainDexList();
+  }
+
+  public MainDexListFromGenerateMainHorizontalMergingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    assertEquals(3, mainDexList.size());
+    Set<String> mainDexReferences =
+        mainDexList.stream().map(TypeReference::getTypeName).collect(Collectors.toSet());
+    assertTrue(mainDexReferences.contains(A.class.getTypeName()));
+    assertTrue(mainDexReferences.contains(B.class.getTypeName()));
+    assertTrue(mainDexReferences.contains(Main.class.getTypeName()));
+
+    R8TestCompileResult compileResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .addInliningAnnotations()
+            .addKeepClassAndMembersRules(Main.class, Outside.class)
+            .addMainDexListClassReferences(mainDexList)
+            .collectMainDexClasses()
+            .enableInliningAnnotations()
+            .enableNeverClassInliningAnnotations()
+            .setMinApi(parameters.getApiLevel())
+            .addHorizontallyMergedClassesInspector(
+                horizontallyMergedClassesInspector -> {
+                  horizontallyMergedClassesInspector.assertMergedInto(B.class, A.class);
+                })
+            .compile();
+
+    CodeInspector inspector = compileResult.inspector();
+    ClassSubject mainClassSubject = inspector.clazz(Main.class);
+    assertThat(mainClassSubject, isPresent());
+
+    MethodSubject fooMethodSubject = mainClassSubject.uniqueMethodWithName("foo");
+    assertThat(fooMethodSubject, isPresent());
+
+    ClassSubject aClassSubject = inspector.clazz(A.class);
+    assertThat(aClassSubject, isPresent());
+
+    // TODO(b/178460068): Should be present, but was merged with A.
+    ClassSubject bClassSubject = inspector.clazz(B.class);
+    assertThat(bClassSubject, isAbsent());
+
+    MethodSubject fooASubject = aClassSubject.uniqueMethodWithName("foo");
+    assertThat(fooASubject, isPresent());
+
+    assertThat(fooMethodSubject, invokesMethod(fooASubject));
+
+    compileResult.inspectMainDexClasses(
+        mainDexClasses -> {
+          assertEquals(
+              ImmutableSet.of(mainClassSubject.getFinalName(), aClassSubject.getFinalName()),
+              mainDexClasses);
+        });
+
+    compileResult
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputThatMatches(containsString(Outside.class.getTypeName()));
+  }
+
+  static class Main {
+
+    // public static B b;
+
+    public static void main(String[] args) {
+      B.print();
+    }
+
+    public static void foo() {
+      A.foo();
+    }
+  }
+
+  public static class Outside {}
+
+  public static class A {
+
+    @NeverInline
+    public static void foo() {
+      System.out.println("A::foo");
+    }
+  }
+
+  @NeverClassInline
+  public static class B {
+
+    @NeverInline
+    public static void print() {
+      System.out.println(Outside.class);
+    }
+  }
+}