| // Copyright (c) 2018, 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.naming; |
| |
| import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.DataDirectoryResource; |
| import com.android.tools.r8.DataEntryResource; |
| import com.android.tools.r8.DataResourceConsumer; |
| import com.android.tools.r8.DataResourceProvider.Visitor; |
| import com.android.tools.r8.TestCompileResult; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase; |
| import com.android.tools.r8.utils.ArchiveResourceProvider; |
| import com.android.tools.r8.utils.DataResourceConsumerForTesting; |
| import com.android.tools.r8.utils.FileUtils; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.io.ByteStreams; |
| import java.io.File; |
| import java.nio.charset.Charset; |
| import java.nio.file.Path; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class AdaptResourceFileContentsTest extends ProguardCompatibilityTestBase { |
| |
| private static final List<Class<?>> CLASSES = |
| ImmutableList.of( |
| AdaptResourceFileContentsTestClass.class, |
| AdaptResourceFileContentsTestClass.A.class, |
| AdaptResourceFileContentsTestClass.B.class); |
| |
| private Backend backend; |
| |
| @Parameterized.Parameters(name = "Backend: {0}") |
| public static Backend[] data() { |
| return ToolHelper.getBackends(); |
| } |
| |
| public AdaptResourceFileContentsTest(Backend backend) { |
| this.backend = backend; |
| } |
| |
| private static final ImmutableList<String> originalAllChangedResource = |
| ImmutableList.of( |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A<java.lang.String>", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A<" |
| + "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A>", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| // Test property values are rewritten. |
| "property=com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A", |
| // Test XML content is rewritten. |
| "<tag>com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A</tag>", |
| "<tag attr=\"com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A\"></tag>", |
| // Test single-quote literals are rewritten. |
| "'com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A'"); |
| |
| private static final ImmutableList<String> originalAllPresentResource = |
| ImmutableList.of( |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B"); |
| |
| private static final ImmutableList<String> originalAllUnchangedResource = |
| ImmutableList.of( |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass", |
| // Test there is no renaming for the method on A. |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A.method", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$A.method()", |
| // Test there is no renaming for the methods on B. |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B.method", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B.method()", |
| // Test various prefixes. |
| "42com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| "WithIdentifierPrefixcom.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| "-com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| "WithDashPrefix-com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| "$com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| "WithDollarPrefix$com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| ".com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| "WithDotPrefix.com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B", |
| // Test various suffixes. |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B42", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$BWithIdentifierSuffix", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B-", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B-WithDashSuffix", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B$", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B$WithDollarSuffix", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B.", |
| "com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B.WithDotSuffix"); |
| |
| private static String getProguardConfig( |
| boolean enableAdaptResourceFileContents, String adaptResourceFileContentsPathFilter) { |
| String adaptResourceFileContentsRule; |
| if (enableAdaptResourceFileContents) { |
| adaptResourceFileContentsRule = "-adaptresourcefilecontents"; |
| if (adaptResourceFileContentsPathFilter != null) { |
| adaptResourceFileContentsRule += " " + adaptResourceFileContentsPathFilter; |
| } |
| } else { |
| adaptResourceFileContentsRule = ""; |
| } |
| return String.join( |
| System.lineSeparator(), |
| adaptResourceFileContentsRule, |
| "-keep class " + AdaptResourceFileContentsTestClass.class.getName() + " {", |
| " public static void main(...);", |
| "}"); |
| } |
| |
| private static String getProguardConfigWithNeverInline( |
| boolean enableAdaptResourceFileContents, String adaptResourceFileContentsPathFilter) { |
| return String.join( |
| System.lineSeparator(), |
| getProguardConfig(enableAdaptResourceFileContents, adaptResourceFileContentsPathFilter), |
| "-neverinline class com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B {", |
| " public void method();", |
| "}", |
| "-neverclassinline class com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B"); |
| } |
| |
| @Test |
| public void testEnabled() throws Exception { |
| String pgConf = getProguardConfigWithNeverInline(true, null); |
| DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting(); |
| CodeInspector inspector = compileWithR8(pgConf, dataResourceConsumer).inspector(); |
| |
| // Check that the data resources have changed as expected. |
| checkAllAreChanged( |
| dataResourceConsumer.get("resource-all-changed.md"), originalAllChangedResource); |
| checkAllAreChanged( |
| dataResourceConsumer.get("resource-all-changed.txt"), originalAllChangedResource); |
| |
| // Check that the new names are consistent with the actual application code. |
| checkAllArePresent(dataResourceConsumer.get("resource-all-present.txt"), inspector); |
| |
| // Check that the data resources have not changed unexpectedly. |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.class"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.cLaSs"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-unchanged.txt"), originalAllUnchangedResource); |
| } |
| |
| @Test |
| public void testProguardBehavior() throws Exception { |
| Path proguardedJar = |
| File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath(); |
| runProguard6Raw( |
| proguardedJar, |
| ImmutableList.of( |
| AdaptResourceFileContentsTestClass.class, |
| AdaptResourceFileContentsTestClass.A.class, |
| AdaptResourceFileContentsTestClass.B.class), |
| getProguardConfig(true, null), |
| null, |
| getDataResources() |
| .stream() |
| .filter(x -> !x.getName().toLowerCase().endsWith(FileUtils.CLASS_EXTENSION)) |
| .collect(Collectors.toList())); |
| |
| // Visit each of the resources in the jar and check that their contents are as expected. |
| Set<String> filenames = new HashSet<>(); |
| ArchiveResourceProvider.fromArchive(proguardedJar, true) |
| .accept( |
| new Visitor() { |
| @Override |
| public void visit(DataDirectoryResource directory) {} |
| |
| @Override |
| public void visit(DataEntryResource file) { |
| try { |
| byte[] bytes = ByteStreams.toByteArray(file.getByteStream()); |
| List<String> lines = |
| Arrays.asList( |
| new String(bytes, Charset.defaultCharset()) |
| .split(System.lineSeparator())); |
| if (file.getName().endsWith("resource-all-changed.md")) { |
| checkAllAreChanged(lines, originalAllChangedResource); |
| } else if (file.getName().endsWith("resource-all-changed.txt")) { |
| checkAllAreChanged(lines, originalAllChangedResource); |
| } else if (file.getName().endsWith("resource-all-present.txt")) { |
| checkAllArePresent(lines, new CodeInspector(readJar(proguardedJar))); |
| } else if (file.getName().endsWith("resource-all-unchanged.txt")) { |
| checkAllAreUnchanged(lines, originalAllUnchangedResource); |
| } |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| |
| // Record that the jar contains a resource with this name. |
| filenames.add(file.getName()); |
| } |
| }); |
| |
| // Check that the jar contains the four expected resources, and nothing else. |
| assertEquals(4, filenames.size()); |
| assertTrue(filenames.stream().anyMatch(x -> x.endsWith("resource-all-changed.md"))); |
| assertTrue(filenames.stream().anyMatch(x -> x.endsWith("resource-all-changed.txt"))); |
| assertTrue(filenames.stream().anyMatch(x -> x.endsWith("resource-all-present.txt"))); |
| assertTrue(filenames.stream().anyMatch(x -> x.endsWith("resource-all-unchanged.txt"))); |
| } |
| |
| @Test |
| public void testEnabledWithFilter() throws Exception { |
| DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting(); |
| compileWithR8(getProguardConfigWithNeverInline(true, "*.md"), dataResourceConsumer); |
| |
| // Check that the file matching the filter has changed as expected. |
| checkAllAreChanged( |
| dataResourceConsumer.get("resource-all-changed.md"), originalAllChangedResource); |
| |
| // Check that all the other data resources are unchanged. |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.class"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.cLaSs"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.txt"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-present.txt"), originalAllPresentResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-unchanged.txt"), originalAllUnchangedResource); |
| } |
| |
| @Test |
| public void testDisabled() throws Exception { |
| DataResourceConsumerForTesting dataResourceConsumer = new DataResourceConsumerForTesting(); |
| compileWithR8(getProguardConfigWithNeverInline(false, null), dataResourceConsumer); |
| |
| // Check that all data resources are unchanged. |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.class"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.cLaSs"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.md"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-changed.txt"), originalAllChangedResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-present.txt"), originalAllPresentResource); |
| checkAllAreUnchanged( |
| dataResourceConsumer.get("resource-all-unchanged.txt"), originalAllUnchangedResource); |
| } |
| |
| private static void checkAllAreChanged(List<String> adaptedLines, List<String> originalLines) { |
| assertEquals(adaptedLines.size(), originalLines.size()); |
| for (int i = 0; i < originalLines.size(); i++) { |
| assertNotEquals(originalLines.get(i), adaptedLines.get(i)); |
| } |
| } |
| |
| private static void checkAllArePresent(List<String> lines, CodeInspector inspector) { |
| for (String line : lines) { |
| assertThat(inspector.clazz(line), isPresent()); |
| } |
| } |
| |
| private static void checkAllAreUnchanged(List<String> adaptedLines, List<String> originalLines) { |
| assertEquals(adaptedLines.size(), originalLines.size()); |
| for (int i = 0; i < originalLines.size(); i++) { |
| assertEquals(originalLines.get(i), adaptedLines.get(i)); |
| } |
| } |
| |
| private TestCompileResult compileWithR8( |
| String proguardConfig, DataResourceConsumer dataResourceConsumer) |
| throws CompilationFailedException { |
| return testForR8(backend) |
| .addProgramClasses(CLASSES) |
| .addDataResources(getDataResources()) |
| .enableProguardTestOptions() |
| .addKeepRules(proguardConfig) |
| .addOptionsModification(o -> o.dataResourceConsumer = dataResourceConsumer) |
| .compile(); |
| } |
| |
| private List<DataEntryResource> getDataResources() { |
| return ImmutableList.of( |
| DataEntryResource.fromBytes( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.class", |
| Origin.unknown()), |
| DataEntryResource.fromBytes( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.cLaSs", |
| Origin.unknown()), |
| DataEntryResource.fromBytes( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.md", |
| Origin.unknown()), |
| DataEntryResource.fromBytes( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.txt", |
| Origin.unknown()), |
| DataEntryResource.fromBytes( |
| String.join(System.lineSeparator(), originalAllPresentResource).getBytes(), |
| "resource-all-present.txt", |
| Origin.unknown()), |
| DataEntryResource.fromBytes( |
| String.join(System.lineSeparator(), originalAllUnchangedResource).getBytes(), |
| "resource-all-unchanged.txt", |
| Origin.unknown())); |
| } |
| } |
| |
| class AdaptResourceFileContentsTestClass { |
| |
| public static void main(String[] args) { |
| B obj = new B(); |
| obj.method(); |
| } |
| |
| static class A { |
| |
| public void method() { |
| System.out.println("In A.method()"); |
| } |
| } |
| |
| static class B extends A { |
| |
| public void method() { |
| System.out.println("In B.method()"); |
| super.method(); |
| } |
| } |
| } |