[Metadata] Add tests for kotlin value classes

Bug: 187781614
Change-Id: I893888e2ca2bcbdcc7874ede8ff149aad6d5ed7d
diff --git a/build.gradle b/build.gradle
index 01d41fc..c38b5a0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -273,7 +273,8 @@
     apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
     kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
-    testImplementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31"
+    testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
+    testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
 }
 
 def r8LibPath = "$buildDir/libs/r8lib.jar"
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index e59e318..71b041c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -32,11 +32,7 @@
 
   private static final KotlinCompileMemoizer libJars =
       getCompileMemoizer(
-              getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"))
-          .configure(
-              kotlinCompilerTool -> {
-                kotlinCompilerTool.addClasspathFiles(ToolHelper.getClassPathForTests());
-              });
+          getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
   private final TestParameters parameters;
 
   @Parameterized.Parameters(name = "{0}, {1}")
@@ -74,13 +70,14 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
-            .enableInliningAnnotations()
             .addClasspathFiles(
                 ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinAnnotationJar(kotlinc))
             .addKeepRules(
                 "-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); *** keptProperty; }")
+            .addKeepRules("-neverinline class * { @" + PKG_LIB + ".NeverInline *; }")
             .addKeepClassAndMembersRules(PKG_LIB + ".SubUser")
             .addKeepRuntimeVisibleAnnotations()
+            .enableProguardTestOptions()
             .noMinification()
             .compile()
             .inspect(this::checkPruned)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
new file mode 100644
index 0000000..e5c7a51
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
@@ -0,0 +1,139 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
+import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteValueClassTest extends KotlinMetadataTestBase {
+
+  private static final String EXPECTED = StringUtils.lines("Hello, John Doe", "42", "UInt(x=42)");
+  private static final String PKG_LIB = PKG + ".value_class_lib";
+  private static final String PKG_APP = PKG + ".value_class_app";
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}, {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(),
+        // TODO(b/193765243): This should be run for all later compilers.
+        getKotlinTestParameters()
+            .withCompiler(KotlinCompilerVersion.KOTLINC_1_5_0)
+            .withTargetVersion(KotlinTargetVersion.JAVA_8)
+            .build());
+  }
+
+  public MetadataRewriteValueClassTest(
+      TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
+    this.parameters = parameters;
+  }
+
+  private static final KotlinCompileMemoizer kotlincLibJar =
+      getCompileMemoizer(
+          getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(
+            getKotlinStdlibJar(kotlinc), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testMetadataForLib() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+            .addKeepAllClassesRule()
+            .addKeepAttributes(
+                ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+                ProguardKeepAttributes.SIGNATURE,
+                ProguardKeepAttributes.INNER_CLASSES,
+                ProguardKeepAttributes.ENCLOSING_METHOD)
+            .addOptionsModification(
+                internalOptions -> {
+                  internalOptions.testing.keepMetadataInR8IfNotRewritten = false;
+                })
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+    Path main =
+        kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(
+            getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+        .addClasspath(main)
+        .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private void inspect(CodeInspector inspector) throws IOException {
+    CodeInspector stdLibInspector =
+        new CodeInspector(kotlincLibJar.getForConfiguration(kotlinc, targetVersion));
+    for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+      ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+      assertThat(r8Clazz, isPresent());
+      KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+      KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+      if (originalMetadata == null) {
+        assertNull(rewrittenMetadata);
+        continue;
+      }
+      TestCase.assertNotNull(rewrittenMetadata);
+      KotlinClassHeader originalHeader = originalMetadata.getHeader();
+      KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+      TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+      TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+      // We cannot assert equality of the data since it may be ordered differently. Instead we use
+      // the KotlinMetadataWriter.
+      String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+      String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+      TestCase.assertEquals(expected, actual);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
index a7672fd..7cb8107 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.kotlin.metadata.pruned_lib
 
-import com.android.tools.r8.NeverInline
+annotation class NeverInline
 
 // The Base class will be removed during compilation
 open class Base
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_app/main.kt
new file mode 100644
index 0000000..476c6e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_app/main.kt
@@ -0,0 +1,13 @@
+// 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.kotlin.metadata.value_class_app
+
+import com.android.tools.r8.kotlin.metadata.value_class_lib.*
+
+fun main() {
+  Name("John Doe").prettyPrint()
+  compute(42)
+  compute(UInt(42))
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_lib/lib.kt
new file mode 100644
index 0000000..0c4d392
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_lib/lib.kt
@@ -0,0 +1,39 @@
+// 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.kotlin.metadata.value_class_lib
+
+// These examples are from the kotlinlang.org website.
+
+interface Printable {
+  fun prettyPrint()
+}
+
+@JvmInline
+value class Name(val s: String) : Printable {
+
+  init {
+    require(s.length > 0) { }
+  }
+
+  val length: Int
+    get() = s.length
+
+  override fun prettyPrint() {
+    println("Hello, $s")
+  }
+}
+
+@JvmInline
+value class UInt(val x: Int)
+
+// Represented as 'public final void compute(int x)' on the JVM
+fun compute(x: Int) {
+  println(x);
+}
+
+// Also represented as 'public final void compute(int x)' on the JVM thus name is mangled!
+fun compute(x: UInt) {
+  println(x);
+}