blob: 4103ea9bceddd18b02481d208c0f04ef21b1a721 [file] [log] [blame]
// 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.keepanno.androidx;
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.KotlinCompileMemoizer;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.keepanno.KeepAnnoParameters;
import com.android.tools.r8.keepanno.KeepAnnoTestBase;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.runners.Parameterized.Parameter;
public abstract class KeepAnnoTestExtractedRulesBase extends KeepAnnoTestBase {
@Parameter(0)
public KeepAnnoParameters parameters;
@Parameter(1)
public KotlinTestParameters kotlinParameters;
protected static KotlinCompileMemoizer compilationResults;
protected static KotlinCompileMemoizer compilationResultsClassName;
protected abstract String getExpectedOutput();
protected static List<String> trimRules(List<String> rules) {
List<String> trimmedRules =
rules.stream()
.flatMap(s -> Arrays.stream(s.split("\n")))
.filter(rule -> !rule.startsWith("#"))
.collect(Collectors.toList());
return trimmedRules;
}
public static class ExpectedRule {
private final String conditionClass;
private final String conditionMembers;
private final String consequentClass;
private final String consequentMembers;
private ExpectedRule(Builder builder) {
this.conditionClass = builder.conditionClass;
this.conditionMembers = builder.conditionMembers;
this.consequentClass = builder.consequentClass;
this.consequentMembers = builder.consequentMembers;
}
public String getRule(boolean r8) {
return "-if class "
+ conditionClass
+ " "
+ conditionMembers
+ " -keepclasseswithmembers"
+ (r8 ? ",allowaccessmodification" : "")
+ " class "
+ consequentClass
+ " "
+ consequentMembers;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private String conditionClass;
private String conditionMembers;
private String consequentClass;
private String consequentMembers;
private Builder() {}
public Builder setConditionClass(Class<?> conditionClass) {
this.conditionClass = conditionClass.getTypeName();
return this;
}
public Builder setConditionClass(String conditionClass) {
this.conditionClass = conditionClass;
return this;
}
public Builder setConditionMembers(String conditionMembers) {
this.conditionMembers = conditionMembers;
return this;
}
public Builder setConsequentClass(Class<?> consequentClass) {
this.consequentClass = consequentClass.getTypeName();
return this;
}
public Builder setConsequentClass(String consequentClass) {
this.consequentClass = consequentClass;
return this;
}
public Builder setConsequentMembers(String consequentMembers) {
this.consequentMembers = consequentMembers;
return this;
}
public ExpectedRule build() {
return new ExpectedRule(this);
}
}
}
protected void runTestExtractedRulesJava(List<Class<?>> testClasses, ExpectedRule expectedRule)
throws Exception {
Class<?> mainClass = testClasses.iterator().next();
testForKeepAnnoAndroidX(parameters)
.applyIfPG(
b -> {
KotlinCompiler kotlinc =
new KotlinCompiler(KotlinCompilerVersion.MAX_SUPPORTED_VERSION);
b.addLibraryFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar());
})
.addProgramClasses(testClasses)
.addKeepMainRule(mainClass)
.setExcludedOuterClass(getClass())
.inspectExtractedRules(
rules -> {
if (parameters.isExtractRules()) {
assertEquals(
ImmutableList.of(expectedRule.getRule(parameters.isR8())), trimRules(rules));
}
})
.run(mainClass)
.assertSuccessWithOutput(getExpectedOutput());
}
protected void runTestExtractedRulesKotlin(
KotlinCompileMemoizer compilation, String mainClass, ExpectedRule expectedRule)
throws Exception {
// TODO(b/392865072): Legacy R8 fails with AssertionError: Synthetic class kinds should agree.
assumeFalse(parameters.isLegacyR8());
// TODO(b/392865072): Reference fails with AssertionError: Built-in class kotlin.Any is not
// found (in kotlin.reflect code).
assumeFalse(parameters.isReference());
// TODO(b/392865072): Proguard 7.4.1 fails with "Encountered corrupt @kotlin/Metadata for class
// <binary name> (version 2.1.0)".
assumeFalse(parameters.isPG());
testForKeepAnnoAndroidX(parameters)
.addProgramFiles(ImmutableList.of(compilation.getForConfiguration(kotlinParameters)))
.addProgramFilesWithoutAnnotations(
ImmutableList.of(
kotlinParameters.getCompiler().getKotlinStdlibJar(),
kotlinParameters.getCompiler().getKotlinReflectJar(),
kotlinParameters.getCompiler().getKotlinAnnotationJar()))
.applyIfR8Current(R8TestBuilder::allowDiagnosticWarningMessages)
.addKeepRules("-keepattributes RuntimeVisibleAnnotations")
.addKeepRules("-keep class kotlin.Metadata { *; }")
.addKeepRules(
"-keep class " + mainClass + " { public static void main(java.lang.String[]); }")
.applyIfR8OrR8Partial(
b ->
b.addOptionsModification(
options -> {
options.testing.allowedUnusedDontWarnPatterns.add(
"kotlin.reflect.jvm.internal.**");
options.testing.allowedUnusedDontWarnPatterns.add("java.lang.ClassValue");
}),
b ->
b.addR8PartialR8OptionsModification(
options -> {
options.testing.allowedUnusedDontWarnPatterns.add(
"kotlin.reflect.jvm.internal.**");
options.testing.allowedUnusedDontWarnPatterns.add("java.lang.ClassValue");
}))
.inspectExtractedRules(
rules -> {
if (parameters.isExtractRules()) {
assertEquals(
ImmutableList.of(expectedRule.getRule(parameters.isR8())), trimRules(rules));
}
})
.run(mainClass)
.applyIf(
parameters.isExtractRules(),
b -> b.assertSuccessWithOutput(getExpectedOutput()),
b -> b.assertSuccessWithOutput(""));
}
}