| // 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.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertThat; |
| |
| 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.DiagnosticsHandler; |
| import com.android.tools.r8.R8Command; |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.errors.Unreachable; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.codeinspector.CodeInspector; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.io.ByteStreams; |
| import java.io.IOException; |
| import java.nio.charset.Charset; |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| public class AdaptResourceFileContentsTest extends TestBase { |
| |
| private static class CustomDataResourceConsumer implements DataResourceConsumer { |
| |
| private final Map<String, ImmutableList<String>> resources = new HashMap<>(); |
| |
| @Override |
| public void accept(DataDirectoryResource directory, DiagnosticsHandler diagnosticsHandler) { |
| throw new Unreachable(); |
| } |
| |
| @Override |
| public void accept(DataEntryResource file, DiagnosticsHandler diagnosticsHandler) { |
| try { |
| byte[] bytes = ByteStreams.toByteArray(file.getByteStream()); |
| String contents = new String(bytes, Charset.defaultCharset()); |
| resources.put(file.getName(), ImmutableList.copyOf(contents.split(System.lineSeparator()))); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public void finished(DiagnosticsHandler handler) {} |
| |
| public ImmutableList<String> get(String name) { |
| return resources.get(name); |
| } |
| } |
| |
| 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(...);", |
| "}", |
| "-neverinline class com.android.tools.r8.naming.AdaptResourceFileContentsTestClass$B {", |
| " public void method();", |
| "}"); |
| } |
| |
| @Test |
| public void testEnabled() throws Exception { |
| CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer(); |
| AndroidApp out = compileWithR8(getProguardConfig(true, null), dataResourceConsumer); |
| |
| // 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"), new CodeInspector(out)); |
| |
| // 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); |
| } |
| |
| @Ignore("b/36847655") |
| @Test |
| public void testProguardBehavior() throws Exception { |
| // TODO(christofferqa): Run Proguard on the example and check that R8 behaves the same way. |
| } |
| |
| @Test |
| public void testEnabledWithFilter() throws Exception { |
| CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer(); |
| compileWithR8(getProguardConfig(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 { |
| CustomDataResourceConsumer dataResourceConsumer = new CustomDataResourceConsumer(); |
| compileWithR8(getProguardConfig(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( |
| ImmutableList<String> adaptedLines, ImmutableList<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(ImmutableList<String> lines, CodeInspector inspector) { |
| for (String line : lines) { |
| assertThat(inspector.clazz(line), isPresent()); |
| } |
| } |
| |
| private static void checkAllAreUnchanged( |
| ImmutableList<String> adaptedLines, ImmutableList<String> originalLines) { |
| assertEquals(adaptedLines.size(), originalLines.size()); |
| for (int i = 0; i < originalLines.size(); i++) { |
| assertEquals(originalLines.get(i), adaptedLines.get(i)); |
| } |
| } |
| |
| private AndroidApp compileWithR8(String proguardConfig, DataResourceConsumer dataResourceConsumer) |
| throws CompilationFailedException, IOException { |
| AndroidApp app = |
| AndroidApp.builder() |
| .addProgramFiles( |
| ToolHelper.getClassFileForTestClass(AdaptResourceFileContentsTestClass.class), |
| ToolHelper.getClassFileForTestClass(AdaptResourceFileContentsTestClass.A.class), |
| ToolHelper.getClassFileForTestClass(AdaptResourceFileContentsTestClass.B.class)) |
| .addDataResource( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.class", |
| Origin.unknown()) |
| .addDataResource( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.cLaSs", |
| Origin.unknown()) |
| .addDataResource( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.md", |
| Origin.unknown()) |
| .addDataResource( |
| String.join(System.lineSeparator(), originalAllChangedResource).getBytes(), |
| "resource-all-changed.txt", |
| Origin.unknown()) |
| .addDataResource( |
| String.join(System.lineSeparator(), originalAllPresentResource).getBytes(), |
| "resource-all-present.txt", |
| Origin.unknown()) |
| .addDataResource( |
| String.join(System.lineSeparator(), originalAllUnchangedResource).getBytes(), |
| "resource-all-unchanged.txt", |
| Origin.unknown()) |
| .build(); |
| R8Command command = |
| ToolHelper.allowTestProguardOptions( |
| ToolHelper.prepareR8CommandBuilder(app) |
| .addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown())) |
| .build(); |
| return ToolHelper.runR8( |
| command, |
| options -> { |
| // TODO(christofferqa): Class inliner should respect -neverinline. |
| options.enableClassInlining = false; |
| options.enableClassMerging = true; |
| options.dataResourceConsumer = dataResourceConsumer; |
| }); |
| } |
| } |
| |
| 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(); |
| } |
| } |
| } |