[KeepAnno] Introduce test builders to share testing set up
This should make it easier to update existing tests and maintain
their configuration.
Bug: b/321674067
Change-Id: I1164aea74bd40c7caf61e332b80dfb278a44254f
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java
new file mode 100644
index 0000000..ec1446b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java
@@ -0,0 +1,62 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.TestParameters;
+
+public class KeepAnnoParameters {
+
+ public enum KeepAnnoConfig {
+ REFERENCE,
+ R8_NATIVE,
+ R8_LEGACY,
+ PG;
+ }
+
+ private final TestParameters parameters;
+ private final KeepAnnoConfig config;
+
+ KeepAnnoParameters(TestParameters parameters, KeepAnnoConfig config) {
+ this.parameters = parameters;
+ this.config = config;
+ }
+
+ @Override
+ public String toString() {
+ return config.name() + ", " + parameters;
+ }
+
+ public TestParameters parameters() {
+ return parameters;
+ }
+
+ public KeepAnnoConfig config() {
+ return config;
+ }
+
+ public boolean isReference() {
+ return config == KeepAnnoConfig.REFERENCE;
+ }
+
+ public boolean isShrinker() {
+ return !isReference();
+ }
+
+ public boolean isR8() {
+ return config == KeepAnnoConfig.R8_NATIVE || config == KeepAnnoConfig.R8_LEGACY;
+ }
+
+ public boolean isPG() {
+ return config == KeepAnnoConfig.PG;
+ }
+
+ public boolean isNative() {
+ return config == KeepAnnoConfig.R8_NATIVE;
+ }
+
+ public boolean isExtract() {
+ return config == KeepAnnoConfig.R8_LEGACY || config == KeepAnnoConfig.PG;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java
new file mode 100644
index 0000000..698e0bc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class KeepAnnoTestBase extends TestBase {
+
+ public static List<KeepAnnoParameters> createParameters(
+ TestParametersCollection parametersCollection) {
+ List<KeepAnnoParameters> keepAnnoParams = new ArrayList<>();
+ for (TestParameters parameters : parametersCollection) {
+ keepAnnoParams.add(
+ new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.REFERENCE));
+ keepAnnoParams.add(
+ new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_NATIVE));
+ keepAnnoParams.add(
+ new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_LEGACY));
+ if (parameters.isCfRuntime()) {
+ keepAnnoParams.add(
+ new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.PG));
+ }
+ }
+ return keepAnnoParams;
+ }
+
+ public KeepAnnoTestBuilder testForKeepAnno(KeepAnnoParameters params) throws IOException {
+ return KeepAnnoTestBuilder.forKeepAnnoTest(params, temp);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
new file mode 100644
index 0000000..cd475f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
@@ -0,0 +1,234 @@
+// Copyright (c) 2024, 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.keepanno;
+
+import com.android.tools.r8.ExternalR8TestBuilder;
+import com.android.tools.r8.ProguardTestBuilder;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.examples.sync.Sync.Consumer;
+import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractorOptions;
+import java.io.IOException;
+import java.util.List;
+import org.junit.rules.TemporaryFolder;
+
+public abstract class KeepAnnoTestBuilder {
+
+ public static KeepAnnoTestBuilder forKeepAnnoTest(KeepAnnoParameters params, TemporaryFolder temp)
+ throws IOException {
+ switch (params.config()) {
+ case REFERENCE:
+ return new ReferenceBuilder(params, temp);
+ case R8_NATIVE:
+ return new R8NativeBuilder(params, temp);
+ case R8_LEGACY:
+ return new R8LegacyBuilder(params, temp);
+ case PG:
+ return new PGBuilder(params, temp);
+ default:
+ throw new IllegalStateException("Unexpected keep anno config: " + params.config());
+ }
+ }
+
+ private final KeepAnnoParameters keepAnnoParams;
+
+ private KeepAnnoTestBuilder(KeepAnnoParameters params, TemporaryFolder temp) {
+ this.keepAnnoParams = params;
+ }
+
+ public final TestParameters parameters() {
+ return keepAnnoParams.parameters();
+ }
+
+ public abstract KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses)
+ throws IOException;
+
+ public abstract KeepAnnoTestBuilder addKeepMainRule(Class<?> mainClass);
+
+ public abstract SingleTestRunResult<?> run(Class<?> mainClass) throws Exception;
+
+ public KeepAnnoTestBuilder applyIfR8(
+ Consumer<TestShrinkerBuilder<?, ?, ?, ?, ?>> builderConsumer) {
+ return this;
+ }
+
+ public KeepAnnoTestBuilder applyIfR8Native(Consumer<R8TestBuilder<?>> builderConsumer) {
+ return this;
+ }
+
+ public KeepAnnoTestBuilder applyIfPG(Consumer<ProguardTestBuilder> builderConsumer) {
+ return this;
+ }
+
+ public final KeepAnnoTestBuilder setExcludedOuterClass(Class<?> clazz) {
+ return applyIfPG(b -> b.addDontWarn(clazz));
+ }
+
+ public final KeepAnnoTestBuilder allowUnusedProguardConfigurationRules() {
+ return applyIfR8Native(R8TestBuilder::allowUnusedProguardConfigurationRules);
+ }
+
+ private static class ReferenceBuilder extends KeepAnnoTestBuilder {
+
+ private final TestBuilder<? extends SingleTestRunResult<?>, ?> builder;
+
+ public ReferenceBuilder(KeepAnnoParameters params, TemporaryFolder temp) {
+ super(params, temp);
+ if (parameters().isCfRuntime()) {
+ builder = TestBase.testForJvm(temp);
+ } else {
+ assert parameters().isDexRuntime();
+ builder = TestBase.testForD8(temp).setMinApi(parameters());
+ }
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) {
+ builder.addProgramClasses(programClasses);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addKeepMainRule(Class<?> mainClass) {
+ // Nothing to keep in JVM/D8.
+ return this;
+ }
+
+ @Override
+ public SingleTestRunResult<?> run(Class<?> mainClass) throws Exception {
+ return builder.run(parameters().getRuntime(), mainClass);
+ }
+ }
+
+ private static class R8NativeBuilder extends KeepAnnoTestBuilder {
+
+ private final R8FullTestBuilder builder;
+
+ public R8NativeBuilder(KeepAnnoParameters params, TemporaryFolder temp) {
+ super(params, temp);
+ builder =
+ TestBase.testForR8(temp, parameters().getBackend())
+ .enableExperimentalKeepAnnotations()
+ .setMinApi(parameters());
+ }
+
+ @Override
+ public KeepAnnoTestBuilder applyIfR8(
+ Consumer<TestShrinkerBuilder<?, ?, ?, ?, ?>> builderConsumer) {
+ builderConsumer.accept(builder);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder applyIfR8Native(Consumer<R8TestBuilder<?>> builderConsumer) {
+ builderConsumer.accept(builder);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) {
+ builder.addProgramClasses(programClasses);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addKeepMainRule(Class<?> mainClass) {
+ builder.addKeepMainRule(mainClass);
+ return this;
+ }
+
+ @Override
+ public SingleTestRunResult<?> run(Class<?> mainClass) throws Exception {
+ return builder.run(parameters().getRuntime(), mainClass);
+ }
+ }
+
+ private static class R8LegacyBuilder extends KeepAnnoTestBuilder {
+
+ private final KeepRuleExtractorOptions extractorOptions =
+ KeepRuleExtractorOptions.getR8Options();
+ private final ExternalR8TestBuilder builder;
+
+ public R8LegacyBuilder(KeepAnnoParameters params, TemporaryFolder temp) throws IOException {
+ super(params, temp);
+ builder =
+ TestBase.testForExternalR8(temp, parameters().getBackend())
+ .useProvidedR8(KeepAnnoTestUtils.R8_LIB)
+ .addProgramFiles(KeepAnnoTestUtils.getKeepAnnoLib(temp))
+ .setMinApi(parameters());
+ }
+
+ @Override
+ public KeepAnnoTestBuilder applyIfR8(
+ Consumer<TestShrinkerBuilder<?, ?, ?, ?, ?>> builderConsumer) {
+ builderConsumer.accept(builder);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) throws IOException {
+ List<String> rules = KeepAnnoTestUtils.extractRules(programClasses, extractorOptions);
+ builder.addProgramClasses(programClasses);
+ builder.addKeepRules(rules);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addKeepMainRule(Class<?> mainClass) {
+ builder.addKeepMainRule(mainClass);
+ return this;
+ }
+
+ @Override
+ public SingleTestRunResult<?> run(Class<?> mainClass) throws Exception {
+ return builder.run(parameters().getRuntime(), mainClass);
+ }
+ }
+
+ private static class PGBuilder extends KeepAnnoTestBuilder {
+
+ private final KeepRuleExtractorOptions extractorOptions =
+ KeepRuleExtractorOptions.getPgOptions();
+ private final ProguardTestBuilder builder;
+
+ public PGBuilder(KeepAnnoParameters params, TemporaryFolder temp) throws IOException {
+ super(params, temp);
+ builder =
+ TestBase.testForProguard(KeepAnnoTestUtils.PG_VERSION, temp)
+ .addProgramFiles(KeepAnnoTestUtils.getKeepAnnoLib(temp))
+ .setMinApi(parameters());
+ }
+
+ @Override
+ public KeepAnnoTestBuilder applyIfPG(Consumer<ProguardTestBuilder> builderConsumer) {
+ builderConsumer.accept(builder);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses) throws IOException {
+ List<String> rules = KeepAnnoTestUtils.extractRules(programClasses, extractorOptions);
+ builder.addProgramClasses(programClasses);
+ builder.addKeepRules(rules);
+ return this;
+ }
+
+ @Override
+ public KeepAnnoTestBuilder addKeepMainRule(Class<?> mainClass) {
+ builder.addKeepMainRule(mainClass);
+ return this;
+ }
+
+ @Override
+ public SingleTestRunResult<?> run(Class<?> mainClass) throws Exception {
+ return builder.run(parameters().getRuntime(), mainClass);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java
index 7f79344..7335975 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestUtils.java
@@ -6,11 +6,7 @@
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
-import com.android.tools.r8.ExternalR8TestBuilder;
-import com.android.tools.r8.ProguardTestBuilder;
import com.android.tools.r8.ProguardVersion;
-import com.android.tools.r8.TestShrinkerBuilder;
-import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
@@ -32,7 +28,7 @@
public static ProguardVersion PG_VERSION = ProguardVersion.V7_3_2;
// TODO(b/321674067): Downgrade this to oldest supported AGP, such as R8 8.0.35.
- private static Path R8_LIB = Paths.get(ToolHelper.THIRD_PARTY_DIR, "r8", "r8lib_8.2.20-dev.jar");
+ public static Path R8_LIB = Paths.get(ToolHelper.THIRD_PARTY_DIR, "r8", "r8lib_8.2.20-dev.jar");
public static Path getKeepAnnoLib(TemporaryFolder temp) throws IOException {
Path archive = temp.newFolder().toPath().resolve("keepanno.jar");
@@ -68,25 +64,4 @@
return rules;
}
- public static ThrowableConsumer<ProguardTestBuilder> addInputClassesAndRulesPG(
- List<Class<?>> inputClasses) {
- return builder -> {
- addInputClassesAndRulesShared(inputClasses, builder);
- };
- }
-
- public static ThrowableConsumer<ExternalR8TestBuilder> addInputClassesAndRulesR8(
- List<Class<?>> inputClasses) {
- return builder -> {
- builder.useProvidedR8(R8_LIB);
- addInputClassesAndRulesShared(inputClasses, builder);
- };
- }
-
- private static void addInputClassesAndRulesShared(
- List<Class<?>> inputClasses, TestShrinkerBuilder<?, ?, ?, ?, ?> builder) throws IOException {
- Path keepAnnoLib = getKeepAnnoLib(builder.getState().getTempFolder());
- List<String> rules = extractRules(inputClasses, KeepRuleExtractorOptions.getPgOptions());
- builder.addProgramClasses(inputClasses).addProgramFiles(keepAnnoLib).addKeepRules(rules);
- }
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsedByReflectionAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsedByReflectionAnnotationTest.java
index e6edebc..0bc2417 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsedByReflectionAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsedByReflectionAnnotationTest.java
@@ -6,12 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.ProguardVersion;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.keepanno.annotations.KeepCondition;
import com.android.tools.r8.keepanno.annotations.KeepConstraint;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
@@ -26,112 +21,50 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class KeepUsedByReflectionAnnotationTest extends TestBase {
+public class KeepUsedByReflectionAnnotationTest extends KeepAnnoTestBase {
static final String EXPECTED = StringUtils.lines("Hello, world");
- private final TestParameters parameters;
+ private final KeepAnnoParameters parameters;
@Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ public static List<KeepAnnoParameters> data() {
+ return createParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
}
- public KeepUsedByReflectionAnnotationTest(TestParameters parameters) {
+ public KeepUsedByReflectionAnnotationTest(KeepAnnoParameters parameters) {
this.parameters = parameters;
}
@Test
- public void testReference() throws Exception {
- testForRuntime(parameters)
+ public void test() throws Exception {
+ Class<?> mainClass = TestClass.class;
+ testForKeepAnno(parameters)
.addProgramClasses(getInputClasses())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .enableExperimentalKeepAnnotations()
- .addProgramClasses(getInputClasses())
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClass.class)
+ .addKeepMainRule(mainClass)
+ .setExcludedOuterClass(getClass())
+ .run(mainClass)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkOutput);
+ .applyIf(parameters.isShrinker(), r -> r.inspect(this::checkOutput));
}
@Test
- public void testExtractR8() throws Exception {
- testForExternalR8(parameters.getBackend())
- .apply(KeepAnnoTestUtils.addInputClassesAndRulesR8(getInputClasses()))
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkOutput);
- }
-
- @Test
- public void testExtractPG() throws Exception {
- assumeTrue(parameters.isCfRuntime());
- testForProguard(KeepAnnoTestUtils.PG_VERSION)
- .addDontWarn(getClass())
- .apply(KeepAnnoTestUtils.addInputClassesAndRulesPG(getInputClasses()))
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkOutput);
- }
-
- @Test
- public void testNoRefReference() throws Exception {
- testForRuntime(parameters)
+ public void testNoRef() throws Exception {
+ Class<?> mainClass = TestClassNoRef.class;
+ testForKeepAnno(parameters)
.addProgramClasses(getInputClasses())
- .run(parameters.getRuntime(), TestClassNoRef.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- @Test
- public void testNoRefR8() throws Exception {
- testForR8(parameters.getBackend())
- .enableExperimentalKeepAnnotations()
- .addProgramClasses(getInputClasses())
- .addKeepMainRule(TestClassNoRef.class)
+ .addKeepMainRule(mainClass)
.allowUnusedProguardConfigurationRules()
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClassNoRef.class)
+ .setExcludedOuterClass(getClass())
+ .run(mainClass)
.assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkOutputNoRef);
- }
-
- @Test
- public void testNoRefExtractR8() throws Exception {
- testForExternalR8(parameters.getBackend())
- .apply(KeepAnnoTestUtils.addInputClassesAndRulesR8(getInputClasses()))
- .addKeepMainRule(TestClassNoRef.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClassNoRef.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkOutputNoRef);
- }
-
- @Test
- public void testNoRefExtractPG() throws Exception {
- assumeTrue(parameters.isCfRuntime());
- testForProguard(ProguardVersion.V7_3_2)
- .addDontWarn(getClass())
- .apply(KeepAnnoTestUtils.addInputClassesAndRulesPG(getInputClasses()))
- .addKeepMainRule(TestClassNoRef.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClassNoRef.class)
- .assertSuccessWithOutput(EXPECTED)
+ .applyIf(parameters.isR8(), r -> r.inspect(this::checkOutputNoRef))
// PG does not eliminate B so the same output remains.
- .inspect(this::checkOutput);
+ .applyIf(parameters.isPG(), r -> r.inspect(this::checkOutput));
}
- public List<Class<?>> getInputClasses() {
+ private List<Class<?>> getInputClasses() {
return ImmutableList.of(TestClass.class, TestClassNoRef.class, A.class, B.class, C.class);
}