Add support for using R8 partial in existing D8/R8 tests
Change-Id: Iab5c235d4fbf7ae34fc69d95e20f93a0f49bcb2f
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java b/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java
index c1ec90c..22b2096 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java
@@ -18,6 +18,7 @@
import com.google.common.base.Splitter;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Random;
import java.util.function.Consumer;
public class R8PartialCompilationConfiguration {
@@ -25,22 +26,25 @@
private final boolean enabled;
private final R8PartialPredicateCollection includePredicates;
private final R8PartialPredicateCollection excludePredicates;
+ private final Random randomizeForTesting;
public Consumer<InternalOptions> d8DexOptionsConsumer = ConsumerUtils.emptyConsumer();
public Consumer<InternalOptions> r8OptionsConsumer = ConsumerUtils.emptyConsumer();
private static final R8PartialCompilationConfiguration disabledConfiguration =
- new R8PartialCompilationConfiguration(false, null, null);
+ new R8PartialCompilationConfiguration(false, null, null, null);
private R8PartialCompilationConfiguration(
boolean enabled,
R8PartialPredicateCollection includePredicates,
- R8PartialPredicateCollection excludePredicates) {
+ R8PartialPredicateCollection excludePredicates,
+ Random randomizeForTesting) {
assert !enabled || !includePredicates.isEmpty();
assert !enabled || excludePredicates != null;
this.enabled = enabled;
this.includePredicates = includePredicates;
this.excludePredicates = excludePredicates;
+ this.randomizeForTesting = randomizeForTesting;
}
public R8PartialPredicateCollection getIncludePredicates() {
@@ -52,6 +56,9 @@
}
public boolean test(DexProgramClass clazz) {
+ if (randomizeForTesting != null) {
+ return randomizeForTesting.nextBoolean();
+ }
return includePredicates.test(clazz) && !excludePredicates.test(clazz);
}
@@ -83,17 +90,22 @@
return enabled;
}
+ public boolean isRandomizeForTestingEnabled() {
+ return randomizeForTesting != null;
+ }
+
public static class Builder {
private final R8PartialPredicateCollection includePredicates =
new R8PartialPredicateCollection();
private final R8PartialPredicateCollection excludePredicates =
new R8PartialPredicateCollection();
+ private Random randomizeForTesting;
private Builder() {}
public R8PartialCompilationConfiguration build() {
return new R8PartialCompilationConfiguration(
- !includePredicates.isEmpty(), includePredicates, excludePredicates);
+ !includePredicates.isEmpty(), includePredicates, excludePredicates, randomizeForTesting);
}
public Builder includeAll() {
@@ -106,6 +118,14 @@
return this;
}
+ public Builder randomizeForTesting() {
+ randomizeForTesting = new Random();
+ long seed = System.currentTimeMillis();
+ randomizeForTesting.setSeed(seed);
+ System.out.println("Partial compilation seed: " + seed);
+ return this;
+ }
+
public Builder addJavaTypeIncludePattern(String pattern) {
includePredicates.add(
createPredicate("L" + DescriptorUtils.getBinaryNameFromJavaType(pattern)));
diff --git a/src/test/java/com/android/tools/r8/partial/D8R8TestWithPartialCompilationTest.java b/src/test/java/com/android/tools/r8/partial/D8R8TestWithPartialCompilationTest.java
new file mode 100644
index 0000000..f1b1bc2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/D8R8TestWithPartialCompilationTest.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2025, 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.partial;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class D8R8TestWithPartialCompilationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().withPartialCompilation().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters)
+ .addInnerClasses(getClass())
+ .release()
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/MisconfiguredD8R8TestWithPartialCompilationTest.java b/src/test/java/com/android/tools/r8/partial/MisconfiguredD8R8TestWithPartialCompilationTest.java
new file mode 100644
index 0000000..086b8af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/MisconfiguredD8R8TestWithPartialCompilationTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2025, 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.partial;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MisconfiguredD8R8TestWithPartialCompilationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().withPartialCompilation().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ try {
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .release()
+ .setMinApi(parameters)
+ .compile();
+ assertTrue(parameters.getPartialCompilationTestParameters().isNone());
+ } catch (AssertionError e) {
+ assertTrue(parameters.getPartialCompilationTestParameters().isSome());
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ try {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
+ .compile();
+ assertTrue(parameters.getPartialCompilationTestParameters().isNone());
+ } catch (AssertionError e) {
+ assertTrue(parameters.getPartialCompilationTestParameters().isSome());
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
index f6d7950..7687324 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
@@ -67,6 +67,10 @@
return this;
}
+ R8PartialCompilationConfiguration getR8PartialConfiguration() {
+ return r8PartialConfiguration;
+ }
+
public R8PartialTestBuilder setR8PartialConfiguration(
Consumer<R8PartialCompilationConfiguration.Builder> consumer) {
assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
index 7b415ba..bdcbe32 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.metadata.R8BuildMetadata;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.partial.R8PartialCompilationConfiguration;
import com.android.tools.r8.profile.art.ArtProfileConsumer;
import com.android.tools.r8.profile.art.ArtProfileProvider;
import com.android.tools.r8.profile.art.model.ExternalArtProfile;
@@ -190,7 +191,7 @@
compileResult.assertOnlyInfos();
break;
case NONE:
- if (allowUnusedProguardConfigurationRules) {
+ if (allowUnusedProguardConfigurationRules || !checkNoUnusedProguardConfigurationRules()) {
compileResult
.assertAllInfosMatch(Matchers.proguardConfigurationRuleDoesNotMatch())
.assertNoErrorMessages()
@@ -207,12 +208,24 @@
}
if (allowUnusedProguardConfigurationRules) {
compileResult.assertInfoThatMatches(Matchers.proguardConfigurationRuleDoesNotMatch());
- } else {
+ } else if (checkNoUnusedProguardConfigurationRules()) {
compileResult.assertNoInfoThatMatches(Matchers.proguardConfigurationRuleDoesNotMatch());
}
return compileResult;
}
+ private boolean checkNoUnusedProguardConfigurationRules() {
+ // Allow unused Proguard configuration rules in R8 partial when using a random partitioning.
+ if (isR8PartialTestBuilder()) {
+ R8PartialCompilationConfiguration configuration =
+ asR8PartialTestBuilder().getR8PartialConfiguration();
+ if (configuration != null && configuration.isRandomizeForTestingEnabled()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private static StringBuilder wrapProguardMapConsumer(Builder builder) {
StringBuilder pgMapOutput = new StringBuilder();
StringConsumer pgMapConsumer = builder.getProguardMapConsumer();
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java
index 0ca2f67..ca48bf6 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -13,6 +13,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.DataResourceProvider.Visitor;
@@ -107,6 +108,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -207,9 +209,49 @@
}
public R8FullTestBuilder testForR8(Backend backend) {
+ assert verifyNoPartialCompilationTestParameters();
return testForR8(temp, backend);
}
+ private boolean verifyNoPartialCompilationTestParameters() {
+ try {
+ Class<?> testClass = getClass();
+ for (Field field : testClass.getDeclaredFields()) {
+ if (field.getType() == TestParameters.class) {
+ field.setAccessible(true);
+ TestParameters parameters = (TestParameters) field.get(this);
+ assertTrue(parameters.getPartialCompilationTestParameters().isNone());
+ }
+ }
+ } catch (IllegalAccessException t) {
+ throw new RuntimeException(t);
+ }
+ return true;
+ }
+
+ public R8TestBuilder<?, ?, ?> testForR8(TestParameters parameters) {
+ return testForR8(parameters.getBackend(), parameters.getPartialCompilationTestParameters())
+ .setMinApi(parameters);
+ }
+
+ public R8TestBuilder<?, ?, ?> testForR8(
+ Backend backend, PartialCompilationTestParameters parameters) {
+ if (parameters.isNone()) {
+ return testForR8(backend);
+ }
+ assumeTrue(parameters.isIncludeAll() || parameters.isRandom());
+ return testForR8Partial(backend)
+ .setR8PartialConfiguration(
+ builder -> {
+ if (parameters.isIncludeAll()) {
+ builder.includeAll();
+ } else {
+ assert parameters.isRandom();
+ builder.randomizeForTesting();
+ }
+ });
+ }
+
public R8PartialTestBuilder testForR8Partial(Backend backend) {
return testForR8Partial(temp, backend);
}
@@ -238,6 +280,20 @@
return testForD8(temp, backend);
}
+ public TestCompilerBuilder<?, ?, ?, ?, ?> testForD8(TestParameters parameters) {
+ return testForD8(parameters.getBackend(), parameters.getPartialCompilationTestParameters())
+ .setMinApi(parameters);
+ }
+
+ public TestCompilerBuilder<?, ?, ?, ?, ?> testForD8(
+ Backend backend, PartialCompilationTestParameters partialCompilationTestParameters) {
+ if (partialCompilationTestParameters.isNone()) {
+ return testForD8(backend);
+ }
+ assumeTrue(partialCompilationTestParameters.isExcludeAll());
+ return testForR8Partial(backend).setR8PartialConfiguration(builder -> builder.excludeAll());
+ }
+
public AssistantTestBuilder testForAssistant() {
return AssistantTestBuilder.create(new TestState(temp));
}