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