| // 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 com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.TestShrinkerBuilder; |
| 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 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.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); |
| } |
| |
| protected Matcher<Class<?>> isNotRepackaged(CodeInspector inspector) { |
| return isRepackagedAsExpected(inspector, null, false); |
| } |
| |
| /** |
| * 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(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder) { |
| testBuilder.addKeepRules( |
| "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + getRepackagePackage() + "\""); |
| } |
| |
| protected boolean isFlattenPackageHierarchy() { |
| return flattenPackageHierarchyOrRepackageClasses.equals(FLATTEN_PACKAGE_HIERARCHY); |
| } |
| } |