| // Copyright (c) 2017, 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.shaking; |
| |
| import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; |
| import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR; |
| import static com.android.tools.r8.ToolHelper.EXAMPLES_DIR; |
| import static org.hamcrest.CoreMatchers.allOf; |
| import static org.hamcrest.CoreMatchers.containsString; |
| import static org.hamcrest.MatcherAssert.assertThat; |
| import static org.junit.Assert.assertEquals; |
| |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.TestBase; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic; |
| import com.android.tools.r8.mappingcompose.ComposeTestHelpers; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.StringUtils; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.List; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| @RunWith(Parameterized.class) |
| public class TreeShakingSpecificTest extends TestBase { |
| |
| private Backend backend; |
| private AndroidApiLevel minApi = AndroidApiLevel.LATEST; |
| |
| @Parameters(name = "Backend: {1}") |
| public static List<Object[]> data() { |
| return buildParameters(TestParameters.builder().withNoneRuntime().build(), Backend.values()); |
| } |
| |
| public TreeShakingSpecificTest(TestParameters parameters, Backend backend) { |
| this.backend = backend; |
| parameters.assertNoneRuntime(); |
| } |
| |
| private Path getProgramFiles(String test) { |
| return Paths.get(EXAMPLES_BUILD_DIR, test + ".jar"); |
| } |
| |
| @Test |
| public void testIgnoreWarnings() throws Exception { |
| // Generate R8 processed version without library option. |
| String test = "shaking2"; |
| testForR8(backend) |
| .addProgramFiles(getProgramFiles(test)) |
| .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt")) |
| .addIgnoreWarnings() |
| .setMinApi(minApi) |
| .compile(); |
| } |
| |
| @Test(expected = CompilationFailedException.class) |
| public void testMissingLibrary() throws Exception { |
| // Generate R8 processed version without library option. |
| String test = "shaking2"; |
| testForR8(backend) |
| .addProgramFiles(getProgramFiles(test)) |
| .addLibraryFiles() |
| .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt")) |
| .allowDiagnosticErrorMessages() |
| .setMinApi(minApi) |
| .compileWithExpectedDiagnostics( |
| diagnostics -> { |
| diagnostics |
| .assertOnlyErrors() |
| .assertErrorsMatch(diagnosticType(MissingDefinitionsDiagnostic.class)); |
| |
| MissingDefinitionsDiagnostic diagnostic = |
| (MissingDefinitionsDiagnostic) diagnostics.getErrors().get(0); |
| assertThat( |
| diagnostic.getDiagnosticMessage(), |
| allOf( |
| containsString("Missing class java.io.PrintStream"), |
| containsString("Missing class java.lang.Object"), |
| containsString("Missing class java.lang.String"), |
| containsString("Missing class java.lang.StringBuilder"), |
| containsString("Missing class java.lang.System"))); |
| }); |
| } |
| |
| private String getExpectedCf() { |
| return StringUtils.lines( |
| "shaking1.Shaking -> shaking1.Shaking:", |
| "# {'id':'sourceFile','fileName':'Shaking.java'}", |
| " 1:2:void main(java.lang.String[]):8:9 -> main", |
| "shaking1.Used -> a.a:", |
| "# {'id':'sourceFile','fileName':'Used.java'}", |
| " java.lang.String name -> a", |
| " 1:2:void <init>(java.lang.String):12:13 -> <init>", |
| " 1:1:java.lang.String method():17:17 -> a", |
| " 1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 " |
| + "-> aMethodThatIsNotUsedButKept"); |
| } |
| |
| private String getExpectedDex() { |
| return StringUtils.lines( |
| "shaking1.Shaking -> shaking1.Shaking:", |
| "# {'id':'sourceFile','fileName':'Shaking.java'}", |
| " 0:6:void main(java.lang.String[]):8:8 -> main", |
| " 7:21:void main(java.lang.String[]):9:9 -> main", |
| "shaking1.Used -> a.a:", |
| "# {'id':'sourceFile','fileName':'Used.java'}", |
| " java.lang.String name -> a", |
| " 0:2:void <init>(java.lang.String):12:12 -> <init>", |
| " 3:5:void <init>(java.lang.String):13:13 -> <init>", |
| " 0:16:java.lang.String method():17:17 -> a", |
| " 0:2:java.lang.String aMethodThatIsNotUsedButKept():21:21 " |
| + "-> aMethodThatIsNotUsedButKept"); |
| } |
| |
| @Test |
| public void testPrintMapping() throws Throwable { |
| // Generate R8 processed version without library option. |
| String test = "shaking1"; |
| testForR8(backend) |
| .addProgramFiles(getProgramFiles(test)) |
| .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt")) |
| .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) |
| .setMinApi(minApi) |
| .compile() |
| .inspectProguardMap( |
| proguardMap -> { |
| // Remove header. |
| List<String> lines = StringUtils.splitLines(proguardMap); |
| int firstNonHeaderLine = 0; |
| for (String line : lines) { |
| if (line.startsWith("#")) { |
| firstNonHeaderLine++; |
| } else { |
| break; |
| } |
| } |
| assertEquals( |
| backend.isCf() ? getExpectedCf() : getExpectedDex(), |
| ComposeTestHelpers.doubleToSingleQuote( |
| StringUtils.lines(lines.subList(firstNonHeaderLine, lines.size())))); |
| }); |
| } |
| } |