Add tests for bumping version number of kotlin metadata to 1.4

Bug: 161885097
Change-Id: I39cef63ad172e5034370a60aca3c468678898f23
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
new file mode 100644
index 0000000..d86b25f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -0,0 +1,162 @@
+// 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
+import static org.objectweb.asm.Opcodes.ASM7;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.transformers.ClassTransformer;
+import com.android.tools.r8.utils.StreamUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import org.junit.ComparisonFailure;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.AnnotationVisitor;
+
+@RunWith(Parameterized.class)
+public class MetadataVersionNumberBumpTest extends KotlinMetadataTestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public MetadataVersionNumberBumpTest(TestParameters parameters) {
+    super(KotlinTargetVersion.JAVA_8);
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testLessThan1_4() throws Exception {
+    final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend());
+    rewriteMetadataVersion(testBuilder::addProgramClassFileData, new int[] {1, 1, 16});
+    assertThrows(
+        ComparisonFailure.class,
+        () -> {
+          testBuilder
+              .setMinApi(parameters.getApiLevel())
+              .addKeepAllClassesRule()
+              .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+              .compile()
+              .inspect(inspector -> inspectMetadataVersion(inspector, "1.4"));
+        });
+  }
+
+  @Test
+  public void testEqualTo1_4() throws Exception {
+    final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend());
+    rewriteMetadataVersion(testBuilder::addProgramClassFileData, new int[] {1, 4, 0});
+    assertThrows(
+        ComparisonFailure.class,
+        () -> {
+          testBuilder
+              .setMinApi(parameters.getApiLevel())
+              .addKeepAllClassesRule()
+              .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+              .compile()
+              .inspect(inspector -> inspectMetadataVersion(inspector, "1.4.0"));
+        });
+  }
+
+  @Test
+  public void testGreaterThan1_4() throws Exception {
+    final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend());
+    rewriteMetadataVersion(testBuilder::addProgramClassFileData, new int[] {1, 4, 2});
+    assertThrows(
+        ComparisonFailure.class,
+        () -> {
+          testBuilder
+              .setMinApi(parameters.getApiLevel())
+              .addKeepAllClassesRule()
+              .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+              .compile()
+              .inspect(inspector -> inspectMetadataVersion(inspector, "1.4.2"));
+        });
+  }
+
+  private void rewriteMetadataVersion(Consumer<byte[]> rewrittenBytesConsumer, int[] newVersion)
+      throws IOException {
+    ZipUtils.iter(
+        ToolHelper.getKotlinStdlibJar().toString(),
+        ((entry, input) -> {
+          if (!entry.getName().endsWith(".class")) {
+            return;
+          }
+          final byte[] bytes = StreamUtils.StreamToByteArrayClose(input);
+          final byte[] rewrittenBytes =
+              transformMetadataVersion(
+                  entry.getName().substring(0, entry.getName().length() - 6), bytes, newVersion);
+          rewrittenBytesConsumer.accept(rewrittenBytes);
+        }));
+  }
+
+  private byte[] transformMetadataVersion(String descriptor, byte[] bytes, int[] newVersion) {
+    return transformer(bytes, Reference.classFromDescriptor(descriptor))
+        .addClassTransformer(
+            new ClassTransformer() {
+              @Override
+              public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+                if (!descriptor.equals("Lkotlin/Metadata;")) {
+                  return super.visitAnnotation(descriptor, visible);
+                } else {
+                  return new AnnotationVisitor(ASM7, super.visitAnnotation(descriptor, visible)) {
+                    @Override
+                    public void visit(String name, Object value) {
+                      if (name.equals("mv")) {
+                        super.visit(name, newVersion);
+                      } else {
+                        super.visit(name, value);
+                      }
+                    }
+                  };
+                }
+              }
+            })
+        .transform();
+  }
+
+  private void inspectMetadataVersion(CodeInspector inspector, String expectedVersion) {
+    for (FoundClassSubject clazz : inspector.allClasses()) {
+      verifyExpectedVersionForClass(clazz, expectedVersion);
+    }
+  }
+
+  private void verifyExpectedVersionForClass(FoundClassSubject clazz, String expectedVersion) {
+    final DexAnnotationElement[] elements =
+        clazz.annotation("kotlin.Metadata").getAnnotation().elements;
+    for (DexAnnotationElement element : elements) {
+      if (!element.name.toString().equals("mv")) {
+        continue;
+      }
+      final String version =
+          Arrays.stream(element.value.asDexValueArray().getValues())
+              .map(val -> val.asDexValueInt().value + "")
+              .collect(Collectors.joining("."));
+      assertEquals(expectedVersion, version);
+      return;
+    }
+    fail("Could not find the mv (metadataVersion) element");
+  }
+}