blob: 8eb3d77a06ea1163e69df451c6d7445f4131e15f [file] [log] [blame]
// 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.repackage;
import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.runners.Parameterized.Parameters;
public abstract class RepackageTestBase extends TestBase {
private final boolean enableExperimentalRepackaging;
private final String flattenPackageHierarchyOrRepackageClasses;
protected final TestParameters parameters;
@Parameters(name = "{1}, kind: {0}")
public static List<Object[]> data() {
return buildParameters(
ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
getTestParameters().withAllRuntimesAndApiLevels().build());
}
public RepackageTestBase(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
this(true, flattenPackageHierarchyOrRepackageClasses, parameters);
}
public RepackageTestBase(
boolean enableExperimentalRepackaging,
String flattenPackageHierarchyOrRepackageClasses,
TestParameters parameters) {
this.enableExperimentalRepackaging = enableExperimentalRepackaging;
this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
this.parameters = parameters;
}
protected String getRepackagePackage() {
return "foo";
}
protected Matcher<Class<?>> isRepackaged(CodeInspector inspector) {
return isRepackagedAsExpected(inspector, null, true);
}
protected Matcher<Class<?>> isRepackagedIf(
CodeInspector inspector, boolean eligibleForRepackaging) {
return isRepackagedAsExpected(inspector, null, eligibleForRepackaging);
}
/**
* Checks that the class of interest is repackaged as expected.
*
* <p>If building with -repackageclasses, it is checked that the given class of interest is
* repackaged into "foo" (unless getRepackagePackage() is overridden). In this case, {@param
* packageName} is unused.
*
* <p>If building with -flattenpackagehierarchy, it is checked that the given class is repackaged
* into "foo.<packageName>".
*/
protected Matcher<Class<?>> isRepackagedAsExpected(CodeInspector inspector, String packageName) {
return isRepackagedAsExpected(inspector, packageName, true);
}
private Matcher<Class<?>> isRepackagedAsExpected(
CodeInspector inspector, String packageName, boolean eligibleForRepackaging) {
return new TypeSafeMatcher<Class<?>>() {
@Override
public boolean matchesSafely(Class<?> clazz) {
ClassSubject classSubject = inspector.clazz(clazz);
if (!classSubject.isPresent()) {
return false;
}
return getActualPackage(classSubject).equals(getExpectedPackage(clazz));
}
@Override
public void describeTo(Description description) {
if (eligibleForRepackaging) {
description.appendText(
"class to be repackaged to '" + getExpectedPackageForEligibleClass() + "'");
} else {
description.appendText("class to be ineligible for repackaging");
}
}
@Override
public void describeMismatchSafely(Class<?> clazz, Description description) {
ClassSubject classSubject = inspector.clazz(clazz);
if (classSubject.isPresent()) {
description
.appendText("class ")
.appendValue(clazz.getTypeName())
.appendText(" was not (actual: '" + getActualPackage(classSubject) + "')");
} else {
description
.appendText("class ")
.appendValue(clazz.getTypeName())
.appendText(" was absent");
}
}
private String getActualPackage(ClassSubject classSubject) {
return classSubject.getDexProgramClass().getType().getPackageName();
}
private String getExpectedPackage(Class<?> clazz) {
return eligibleForRepackaging
? getExpectedPackageForEligibleClass()
: clazz.getPackage().getName();
}
private String getExpectedPackageForEligibleClass() {
List<String> expectedPackageNames = new ArrayList<>();
expectedPackageNames.add(getRepackagePackage());
if (isFlattenPackageHierarchy()) {
expectedPackageNames.add(packageName != null ? packageName : "a");
}
return StringUtils.join(expectedPackageNames, ".");
}
};
}
protected void configureRepackaging(R8FullTestBuilder testBuilder) {
testBuilder
.addKeepRules(
"-" + flattenPackageHierarchyOrRepackageClasses + " \"" + getRepackagePackage() + "\"")
.addOptionsModification(
options -> {
assertFalse(options.testing.enableExperimentalRepackaging);
options.testing.enableExperimentalRepackaging = enableExperimentalRepackaging;
});
}
protected boolean isExperimentalRepackaging() {
return enableExperimentalRepackaging;
}
protected boolean isFlattenPackageHierarchy() {
return flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY);
}
}