| // 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.utils; |
| |
| import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR; |
| import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.android.tools.r8.CompilationFailedException; |
| import com.android.tools.r8.DiagnosticsChecker; |
| import com.android.tools.r8.GenerateMainDexList; |
| import com.android.tools.r8.GenerateMainDexListCommand; |
| import com.android.tools.r8.StringConsumer; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.ProcessResult; |
| import com.android.tools.r8.origin.Origin; |
| import com.google.common.collect.ImmutableList; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.PrintWriter; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.StandardOpenOption; |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.ExpectedException; |
| import org.junit.rules.TemporaryFolder; |
| |
| public class GenerateMainDexListCommandTest { |
| |
| @Rule |
| public ExpectedException thrown = ExpectedException.none(); |
| |
| @Rule |
| public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest(); |
| |
| private Path getOutputPath(GenerateMainDexListCommand command) { |
| StringConsumer consumer = command.getMainDexListConsumer(); |
| if (consumer instanceof StringConsumer.FileConsumer) { |
| return ((StringConsumer.FileConsumer) consumer).getOutputPath(); |
| } |
| return null; |
| } |
| |
| @Test |
| public void emptyCommand() throws Throwable { |
| verifyEmptyCommand(GenerateMainDexListCommand.builder().build()); |
| verifyEmptyCommand(parse()); |
| verifyEmptyCommand(parse("")); |
| verifyEmptyCommand(parse("", "")); |
| verifyEmptyCommand(parse(" ")); |
| verifyEmptyCommand(parse(" ", " ")); |
| verifyEmptyCommand(parse("\t")); |
| verifyEmptyCommand(parse("\t", "\t")); |
| } |
| |
| private void verifyEmptyCommand(GenerateMainDexListCommand command) throws IOException { |
| assertEquals(0, ToolHelper.getApp(command).getDexProgramResourcesForTesting().size()); |
| assertEquals(0, ToolHelper.getApp(command).getClassProgramResourcesForTesting().size()); |
| assertFalse(ToolHelper.getApp(command).hasMainDexListResources()); |
| } |
| |
| private void addAndroidJarsToCommandLine(List<String> args) { |
| args.add("--lib"); |
| args.add(ToolHelper.getAndroidJar(AndroidApiLevel.K).toAbsolutePath().toString()); |
| } |
| |
| // Add the jars used in the com.android.tools.r8.maindexlist.MainDexTracingTest test. |
| private void addInputJarsToCommandLine(List<String> args) { |
| args.add(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "multidex001" + JAR_EXTENSION) |
| .toAbsolutePath().toString()); |
| args.add(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "multidexfakeframeworks" + JAR_EXTENSION) |
| .toAbsolutePath().toString()); |
| } |
| |
| // Add main-dex rules used in the com.android.tools.r8.maindexlist.MainDexTracingTest test. |
| private void addMainDexRuleToCommandLine(List<String> args) { |
| args.add("--main-dex-rules"); |
| args.add(Paths.get(ToolHelper.EXAMPLES_DIR, "multidex", "main-dex-rules.txt") |
| .toAbsolutePath().toString()); |
| } |
| |
| @Test |
| public void defaultOutIsCwd() throws Throwable { |
| Path working = temp.getRoot().toPath(); |
| String mainDexListOutput = "main-dex-list.txt"; |
| Path output = working.resolve(mainDexListOutput); |
| assertFalse(Files.exists(output)); |
| List<String> args = new ArrayList<>(); |
| addAndroidJarsToCommandLine(args); |
| addInputJarsToCommandLine(args); |
| addMainDexRuleToCommandLine(args); |
| ProcessResult result = |
| ToolHelper.forkGenerateMainDexList( |
| working, args, "--main-dex-list-output", mainDexListOutput); |
| assertEquals(result.stderr, 0, result.exitCode); |
| assertTrue(Files.exists(output)); |
| assertTrue(Files.size(output) > 0); |
| } |
| |
| @Test |
| public void validOutputPath() throws Throwable { |
| Path existingFile = temp.getRoot().toPath().resolve("existing_output"); |
| try (OutputStream existingFileOut = Files.newOutputStream(existingFile, |
| StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { |
| PrintWriter writer = new PrintWriter(existingFileOut); |
| writer.println("Hello, world!"); |
| writer.flush(); |
| } |
| Path nonExistingFile = temp.getRoot().toPath().resolve("non_existing_output"); |
| assertEquals( |
| existingFile, |
| getOutputPath( |
| GenerateMainDexListCommand.builder().setMainDexListOutputPath(existingFile).build())); |
| assertEquals( |
| nonExistingFile, |
| getOutputPath( |
| GenerateMainDexListCommand.builder() |
| .setMainDexListOutputPath(nonExistingFile).build())); |
| assertEquals( |
| existingFile, |
| getOutputPath(parse("--main-dex-list-output", existingFile.toString()))); |
| assertEquals( |
| nonExistingFile, |
| getOutputPath(parse("--main-dex-list-output", nonExistingFile.toString()))); |
| } |
| |
| @Test |
| public void nonExistingOutputFileInNonExistingDir() throws Throwable { |
| Path nonExistingFileInNonExistingDir = |
| temp.getRoot().toPath().resolve("a/path/that/does/not/exist"); |
| assertEquals( |
| nonExistingFileInNonExistingDir, |
| getOutputPath( |
| GenerateMainDexListCommand.builder() |
| .setMainDexListOutputPath(nonExistingFileInNonExistingDir).build())); |
| assertEquals( |
| nonExistingFileInNonExistingDir, |
| getOutputPath( |
| parse("--main-dex-list-output", nonExistingFileInNonExistingDir.toString()))); |
| } |
| |
| @Test |
| public void mainDexRules() throws Throwable { |
| Path mainDexRules1 = temp.newFile("main-dex-1.rules").toPath(); |
| Path mainDexRules2 = temp.newFile("main-dex-2.rules").toPath(); |
| parse("--main-dex-rules", mainDexRules1.toString()); |
| parse( |
| "--main-dex-rules", mainDexRules1.toString(), "--main-dex-rules", mainDexRules2.toString()); |
| } |
| |
| @Test |
| public void mainDexList() throws Throwable { |
| Path mainDexList1 = temp.newFile("main-dex-list-1.txt").toPath(); |
| Path mainDexList2 = temp.newFile("main-dex-list-2.txt").toPath(); |
| parse("--main-dex-list", mainDexList1.toString()); |
| parse("--main-dex-list", mainDexList1.toString(), "--main-dex-list", mainDexList2.toString()); |
| } |
| |
| @Test(expected = CompilationFailedException.class) |
| public void missingAndroidJar() throws Throwable { |
| DiagnosticsChecker.checkErrorsContains( |
| "Tracing for legacy multi dex is not possible without all classpath libraries", |
| handler -> |
| GenerateMainDexList.run( |
| GenerateMainDexListCommand.builder(handler).build())); |
| } |
| |
| @Test(expected = CompilationFailedException.class) |
| public void duplicateProgramClasses() throws Throwable { |
| Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar"); |
| DiagnosticsChecker.checkErrorsContains( |
| "defined multiple times", |
| handler -> |
| GenerateMainDexList.run( |
| GenerateMainDexListCommand.builder(handler) |
| .addLibraryFiles(ToolHelper.getDefaultAndroidJar()) |
| .addProgramFiles(input) |
| .addProgramFiles(input) |
| .build())); |
| } |
| |
| @Test |
| public void emptyMainDex() throws Throwable { |
| Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar"); |
| List<String> result = GenerateMainDexList.run( |
| GenerateMainDexListCommand.builder() |
| .addLibraryFiles(ToolHelper.getDefaultAndroidJar()) |
| .addProgramFiles(input) |
| .build()); |
| assertEquals(0, result.size()); |
| } |
| |
| @Test |
| public void nonEmptyMainDex() throws Throwable { |
| Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar"); |
| List<String> result = GenerateMainDexList.run( |
| GenerateMainDexListCommand.builder() |
| .addLibraryFiles(ToolHelper.getDefaultAndroidJar()) |
| .addProgramFiles(input) |
| .addMainDexRules( |
| ImmutableList.of("-keep class arithmetic.Arithmetic"), Origin.unknown()) |
| .build()); |
| assertEquals(1, result.size()); |
| } |
| |
| private GenerateMainDexListCommand parse(String... args) throws Throwable { |
| return GenerateMainDexListCommand.parse(args).build(); |
| } |
| } |