blob: 962cec08dffb9e77da855f8134774db86f1ddf2d [file] [log] [blame]
// Copyright (c) 2019, 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.d8;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DexFilePerClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.FIELD})
@interface TestKeep {
}
class TestA {
@TestKeep
void foo() {
System.out.println("TestA::foo");
}
}
class TestB extends TestA {
@TestKeep
static TestA instance;
@TestKeep
void foo() {
super.foo();
System.out.println("TestB::foo");
}
@TestKeep
static void bar() {
instance = new TestB();
}
public static void main(String... args) {
bar();
instance.foo();
}
}
@RunWith(Parameterized.class)
public class DuplicateAnnotationTest extends TestBase {
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().build();
}
public DuplicateAnnotationTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testMergingViaD8() throws Exception {
assumeTrue("D8 tests.", parameters.isDexRuntime());
Path dex1 = temp.newFile("classes1.zip").toPath().toAbsolutePath();
CodeInspector inspector =
testForD8()
.addProgramClasses(TestA.class)
.setIntermediate(true)
.setProgramConsumer(new ArchiveConsumer(dex1))
.setMinApi(parameters.getRuntime())
.compile()
.inspector();
ClassSubject testA = inspector.clazz(TestA.class);
assertThat(testA, isPresent());
MethodSubject foo = testA.uniqueMethodWithName("foo");
assertThat(foo, isPresent());
AnnotationSubject annotation = foo.annotation(TestKeep.class.getName());
assertThat(annotation, isPresent());
Path dex2 = temp.newFile("classes2.zip").toPath().toAbsolutePath();
inspector =
testForD8()
.addProgramClasses(TestB.class)
.setIntermediate(true)
.setProgramConsumer(new ArchiveConsumer(dex2))
.setMinApi(parameters.getRuntime())
.compile()
.inspector();
ClassSubject testB = inspector.clazz(TestB.class);
assertThat(testB, isPresent());
FieldSubject instance = testB.uniqueFieldWithName("instance");
assertThat(instance, isPresent());
annotation = instance.annotation(TestKeep.class.getName());
assertThat(annotation, isPresent());
foo = testB.uniqueMethodWithName("foo");
assertThat(foo, isPresent());
annotation = foo.annotation(TestKeep.class.getName());
assertThat(annotation, isPresent());
MethodSubject bar = testB.uniqueMethodWithName("bar");
assertThat(bar, isPresent());
annotation = bar.annotation(TestKeep.class.getName());
assertThat(annotation, isPresent());
Path merged = temp.newFile("merge.zip").toPath().toAbsolutePath();
testForD8()
.addProgramFiles(dex1, dex2)
.setProgramConsumer(new ArchiveConsumer(merged))
.setMinApi(parameters.getRuntime())
.compile();
}
@Test
public void testDuplicationInInput() throws Exception {
assumeTrue("D8 tests.", parameters.isDexRuntime());
Path dex1 = temp.newFile("classes1.zip").toPath().toAbsolutePath();
try {
testForD8()
.addProgramClassFileData(TestADump.dump())
.setIntermediate(true)
.setProgramConsumer(new ArchiveConsumer(dex1))
.setMinApi(parameters.getRuntime())
.compile();
fail("Expected to fail due to multiple annotations");
} catch (CompilationFailedException e) {
assertThat(e.getCause().getMessage(), containsString("Multiple annotations"));
assertThat(e.getCause().getMessage(), containsString(TestKeep.class.getName()));
}
}
@Test
public void testJVMOutput() throws Exception {
assumeTrue("Only run JVM reference on CF runtimes", parameters.isCfRuntime());
testForJvm()
.addProgramClassFileData(
TestADump.dump(),
ToolHelper.getClassAsBytes(TestB.class),
ToolHelper.getClassAsBytes(TestKeep.class))
.run(TestB.class.getTypeName())
.assertSuccessWithOutput(StringUtils.lines("TestA::foo", "TestB::foo"));
}
}