Add horizontal merging test of Kotlin sealed interface with sub classes

Bug: b/296852026
Change-Id: I8984597ed14957af1d9cf9a70262d892d433d039
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
index 1e5175b..29a072f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
@@ -46,7 +46,7 @@
 
   private static Collection<Path> getKotlinSources() {
     try {
-      return getFilesInTestFolderRelativeToClass(SealedClassTest.class, "kt", ".kt");
+      return getFilesInTestFolderRelativeToClass(SealedClassTest.class, "kt", "Format.kt");
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java
new file mode 100644
index 0000000..7acfdfe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java
@@ -0,0 +1,110 @@
+// 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.kotlin.sealed;
+
+import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+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 SealedInterfaceSubClassesHorizontalMergeTest extends KotlinTestBase {
+
+  private static final String MAIN =
+      "com.android.tools.r8.kotlin.sealed.kt.SealedInterfaceSubClassesKt";
+  private static final String A =
+      "com.android.tools.r8.kotlin.sealed.kt.SealedInterfaceSubClasses$A";
+  private static final String B =
+      "com.android.tools.r8.kotlin.sealed.kt.SealedInterfaceSubClasses$B";
+
+  private static final String[] EXPECTED = new String[] {"an A: I am A", "a B: I am B"};
+
+  private final TestParameters parameters;
+  private final boolean clinitNoSideEffects;
+
+  @Parameters(name = "{0}, {1}, clinitNoSideEffects: {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimesAndApiLevels().build(),
+        getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+        BooleanUtils.values());
+  }
+
+  public SealedInterfaceSubClassesHorizontalMergeTest(
+      TestParameters parameters,
+      KotlinTestParameters kotlinParameters,
+      boolean clinitNoSideEffects) {
+    super(kotlinParameters);
+    this.parameters = parameters;
+    this.clinitNoSideEffects = clinitNoSideEffects;
+  }
+
+  private static final KotlinCompileMemoizer compilationResults =
+      getCompileMemoizer(getKotlinSources());
+
+  private static Collection<Path> getKotlinSources() {
+    try {
+      return getFilesInTestFolderRelativeToClass(
+          SealedInterfaceSubClassesHorizontalMergeTest.class, "kt", "SealedInterfaceSubClasses.kt");
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Test
+  public void testRuntime() throws ExecutionException, CompilationFailedException, IOException {
+    assumeTrue(clinitNoSideEffects);
+    testForRuntime(parameters)
+        .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
+        .addRunClasspathFiles(buildOnDexRuntime(parameters, kotlinc.getKotlinStdlibJar()))
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+
+  @Test
+  public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
+        .addProgramFiles(kotlinc.getKotlinStdlibJar())
+        .addProgramFiles(kotlinc.getKotlinAnnotationJar())
+        .setMinApi(parameters)
+        .allowDiagnosticWarningMessages()
+        .addKeepMainRule(MAIN)
+        .applyIf(
+            clinitNoSideEffects,
+            b ->
+                b.addKeepRules("-assumenosideeffects class " + A + " { <clinit>(); }")
+                    .addKeepRules("-assumenosideeffects class " + B + " { <clinit>(); }")
+                    .addHorizontallyMergedClassesInspector(
+                        inspector ->
+                            inspector
+                                .assertIsCompleteMergeGroup(A, B)
+                                .assertNoOtherClassesMerged()),
+            // TODO(b/296852026): Updates to horizintal class merging can cause this to start
+            // failing as the classes can actually bne merged without -assumenosideeffects rules.
+            b ->
+                b.addHorizontallyMergedClassesInspector(
+                    HorizontallyMergedClassesInspector::assertNoClassesMerged))
+        .compile()
+        .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutputLines(EXPECTED);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/kt/SealedInterfaceSubClasses.kt b/src/test/java/com/android/tools/r8/kotlin/sealed/kt/SealedInterfaceSubClasses.kt
new file mode 100644
index 0000000..469e08c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/kt/SealedInterfaceSubClasses.kt
@@ -0,0 +1,27 @@
+// 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.kotlin.sealed.kt
+
+sealed interface SealedInterfaceSubClasses {
+  object A : SealedInterfaceSubClasses {
+    override fun toString() : String { return "I am A" }
+  }
+  object B : SealedInterfaceSubClasses {
+    override fun toString() : String { return "I am B" }
+  }
+}
+
+fun f(o : SealedInterfaceSubClasses) {
+  when (o) {
+    SealedInterfaceSubClasses.A -> print("an A: ")
+    SealedInterfaceSubClasses.B -> print("a B: ")
+  }
+  println(o);
+}
+
+fun main() {
+  f(SealedInterfaceSubClasses.A)
+  f(SealedInterfaceSubClasses.B)
+}