blob: c78212b30c54e96010c2a11c194da3d60f428c34 [file] [log] [blame]
// Copyright (c) 2022, 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.internal;
import static com.android.tools.r8.ToolHelper.isLocalDevelopment;
import static com.android.tools.r8.ToolHelper.shouldRunSlowTests;
import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.assertRewrittenProtoSchemasMatch;
import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepAllProtosRule;
import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepDynamicMethodSignatureRule;
import static com.android.tools.r8.internal.proto.ProtoShrinkingTestBase.keepNewMessageInfoSignatureRule;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.L8TestCompileResult;
import com.android.tools.r8.LibraryDesugaringTestConfiguration;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringConsumer.FileConsumer;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.tracereferences.TraceReferencesCommand;
import com.android.tools.r8.tracereferences.TraceReferencesKeepRules;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.ExecutionException;
import org.junit.After;
import org.junit.Ignore;
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 YouTubeV1719Test extends YouTubeCompilationTestBase {
private static final int MAX_APPLICATION_SIZE = 29750000;
private static final int MAX_DESUGARED_LIBRARY_SIZE = 425000;
private final TestParameters parameters;
// Location where test artifacts will be dumped when `local_development` is set.
private final Path dumpDirectory = Paths.get("YouTubeV1719-" + System.currentTimeMillis());
// By setting this to an actual startup list, YouTube will be build with layout optimizations
// enabled.
private final StartupProfileProvider startupProfileProvider = null;
private final boolean enableMinimalStartupDex = true;
private final boolean enableStartupBoundaryOptimizations = false;
private final Reporter reporter = new Reporter();
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.M).build();
}
public YouTubeV1719Test(TestParameters parameters) {
super(17, 19, parameters.getApiLevel());
this.parameters = parameters;
}
/**
* Running this test will dump an instrumented version of YouTube in the {@link #dumpDirectory}.
*/
@Test
public void testStartupInstrumentation() throws Exception {
assumeTrue(isLocalDevelopment());
assumeTrue(shouldRunSlowTests());
// Compile the app with instrumentation and core library desugaring enabled.
D8TestCompileResult d8CompileResult =
testForD8()
.addProgramFiles(getProgramFiles())
.addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
.addOptionsModification(
options ->
options
.getStartupInstrumentationOptions()
.setEnableStartupInstrumentation()
.setStartupInstrumentationTag("r8"))
.enableCoreLibraryDesugaring(
LibraryDesugaringTestConfiguration.builder()
.addDesugaredLibraryConfiguration(
StringResource.fromFile(getDesugaredLibraryConfiguration()))
.build())
.release()
.setMinApi(parameters.getApiLevel())
.compile();
// Compile desugared library using cf backend (without keep rules).
L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8();
// Generate keep rules for desugared library using trace references.
Path generatedKeepRules = temp.newFile().toPath();
TraceReferences.run(
TraceReferencesCommand.builder()
.addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
.addSourceFiles(d8CompileResult.writeToZip())
.addTargetFiles(l8CompileResult.writeToZip())
.setConsumer(
TraceReferencesKeepRules.builder()
.setAllowObfuscation(false)
.setOutputConsumer(new FileConsumer(generatedKeepRules))
.build())
.build());
// Compile desugared library using the generated keep rules.
R8TestCompileResult l8R8CompileResult =
compileDesugaredLibraryWithR8(l8CompileResult, generatedKeepRules);
if (isLocalDevelopment()) {
dump(d8CompileResult, l8R8CompileResult);
}
}
/**
* Running this test will dump an R8 build of YouTube in the {@link #dumpDirectory}, where the
* desugared library keep rules are generated using trace references.
*
* <p>If {@link #startupProfileProvider} is set to a concrete startup list, YouTube will be build
* with layout optimizations enabled.
*/
@Test
public void testR8() throws Exception {
assumeTrue(isLocalDevelopment());
assumeTrue(shouldRunSlowTests());
// Compile app using R8, passing the startup list.
R8TestCompileResult r8CompileResult =
compileApplicationWithR8(
testBuilder ->
testBuilder
.addOptionsModification(
options -> {
if (startupProfileProvider != null) {
options
.getStartupOptions()
.setEnableMinimalStartupDex(enableMinimalStartupDex)
.setEnableStartupBoundaryOptimizations(
enableStartupBoundaryOptimizations);
}
})
.applyIf(
startupProfileProvider != null,
b -> b.addStartupProfileProviders(startupProfileProvider)));
// Compile desugared library using cf backend (without keep rules).
L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8();
// Generate keep rules for desugared library using trace references.
Path generatedKeepRules = temp.newFile().toPath();
TraceReferences.run(
TraceReferencesCommand.builder()
.addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
.addSourceFiles(r8CompileResult.writeToZip())
.addTargetFiles(l8CompileResult.writeToZip())
.setConsumer(
TraceReferencesKeepRules.builder()
.setAllowObfuscation(false)
.setOutputConsumer(new FileConsumer(generatedKeepRules))
.build())
.build());
// Compile desugared library using the generated keep rules.
R8TestCompileResult l8R8CompileResult =
compileDesugaredLibraryWithR8(l8CompileResult, generatedKeepRules);
if (isLocalDevelopment()) {
dump(r8CompileResult, l8R8CompileResult);
}
inspect(r8CompileResult, l8CompileResult);
}
/**
* Validates that when all protos are kept and the proto optmization is enabled, the generated
* proto schemas are identical to the proto schemas in the input.
*/
@Ignore
@Test
public void testProtoRewriting() throws Exception {
assumeTrue(shouldRunSlowTests());
R8TestCompileResult r8CompileResult =
compileApplicationWithR8(
builder ->
builder
.addKeepRules(
keepAllProtosRule(),
keepDynamicMethodSignatureRule(),
keepNewMessageInfoSignatureRule())
.allowCheckDiscardedErrors(true));
assertRewrittenProtoSchemasMatch(
new CodeInspector(getProgramFiles()), r8CompileResult.inspector());
}
@After
public void teardown() {
reporter.failIfPendingErrors();
}
private R8TestCompileResult compileApplicationWithR8(
ThrowableConsumer<R8FullTestBuilder> configuration)
throws IOException, CompilationFailedException {
return testForR8(parameters.getBackend())
.addProgramFiles(getProgramFiles())
.addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
.addKeepRuleFiles(getKeepRuleFiles())
.addDontWarn("android.app.Activity$TranslucentConversionListener")
.apply(configuration)
.apply(this::disableR8StrictMode)
.apply(this::disableR8TestingDefaults)
.setMinApi(getApiLevel())
.enableCoreLibraryDesugaring(
LibraryDesugaringTestConfiguration.builder()
.addDesugaredLibraryConfiguration(
StringResource.fromFile(getDesugaredLibraryConfiguration()))
.build())
.compile()
.assertAllInfoMessagesMatch(
anyOf(
containsString("Ignoring option: -optimizations"),
containsString("Proguard configuration rule does not match anything"),
containsString("Invalid signature")))
.apply(this::printProtoStats);
}
private L8TestCompileResult compileDesugaredLibraryWithL8()
throws CompilationFailedException, IOException, ExecutionException {
return testForL8(getApiLevel(), Backend.CF)
.setDesugaredLibrarySpecification(getDesugaredLibraryConfiguration())
.addProgramFiles(getDesugaredLibraryJDKLibs())
.addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
.compile();
}
private R8TestCompileResult compileDesugaredLibraryWithR8(
L8TestCompileResult l8CompileResult, Path generatedKeepRules)
throws IOException, CompilationFailedException {
return testForR8(parameters.getBackend())
.addProgramFiles(l8CompileResult.writeToZip())
.addLibraryFiles(getLibraryFileWithoutDesugaredLibrary())
.addKeepRuleFiles(getDesugaredLibraryKeepRuleFiles(generatedKeepRules))
.apply(this::disableR8StrictMode)
.apply(this::disableR8TestingDefaults)
.setMinApi(parameters.getApiLevel())
.compile();
}
private void inspect(R8TestCompileResult r8CompileResult, L8TestCompileResult l8CompileResult)
throws IOException, ResourceException {
r8CompileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
l8CompileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors();
int applicationSize = r8CompileResult.getApp().applicationSize();
if (applicationSize > MAX_APPLICATION_SIZE) {
reporter.error(
"Expected application size to be <="
+ MAX_APPLICATION_SIZE
+ ", but was "
+ applicationSize);
}
int desugaredLibrarySize = l8CompileResult.getApp().applicationSize();
if (desugaredLibrarySize > MAX_DESUGARED_LIBRARY_SIZE) {
reporter.error(
"Expected desugared library size to be <="
+ MAX_DESUGARED_LIBRARY_SIZE
+ ", but was "
+ desugaredLibrarySize);
}
if (isLocalDevelopment()) {
System.out.println("Dex size (application, excluding desugared library): " + applicationSize);
System.out.println("Dex size (desugared library): " + desugaredLibrarySize);
System.out.println("Dex size (total): " + (applicationSize + desugaredLibrarySize));
}
}
private void dump(TestCompileResult<?, ?> compileResult, R8TestCompileResult l8R8CompileResult)
throws IOException {
assertTrue(isLocalDevelopment());
Files.createDirectories(dumpDirectory);
// Dump dex.
compileResult.writeToDirectory(dumpDirectory);
// Dump mapping.
if (compileResult instanceof R8TestCompileResult) {
R8TestCompileResult r8TestCompileResult = (R8TestCompileResult) compileResult;
r8TestCompileResult.writeProguardMap(dumpDirectory.resolve("mapping.txt"));
}
// Dump desugared library dex.
int i = 2;
while (true) {
Path desugaredLibraryDexFile = dumpDirectory.resolve("classes" + i + ".dex");
if (!desugaredLibraryDexFile.toFile().exists()) {
l8R8CompileResult.writeSingleDexOutputToFile(desugaredLibraryDexFile);
break;
}
i++;
}
// Dump desugared library mapping.
l8R8CompileResult.writeProguardMap(dumpDirectory.resolve("mapping-desugared-library.txt"));
}
private void disableR8StrictMode(R8FullTestBuilder testBuilder) {
testBuilder
.allowDiagnosticMessages()
.allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules();
}
private void disableR8TestingDefaults(R8FullTestBuilder testBuilder) {
testBuilder.addOptionsModification(
options -> options.horizontalClassMergerOptions().setEnableInterfaceMerging(false));
}
}