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)
+}