blob: f4786f746055f3be7b3b679c1f602dfdd92f02ea [file] [log] [blame]
// Copyright (c) 2019, 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.desugar.desugaredlibrary;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import com.android.tools.r8.L8Command;
import com.android.tools.r8.L8TestBuilder;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class DesugaredLibraryContentTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
public DesugaredLibraryContentTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testInvalidLibrary() throws Exception {
Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
L8TestBuilder l8TestBuilder =
testForL8(parameters.getApiLevel())
.addProgramFiles(Collections.singleton(ToolHelper.getDesugarJDKLibs()))
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.L))
.setDesugarJDKLibsConfiguration(ToolHelper.DESUGAR_LIB_CONVERSIONS);
try {
l8TestBuilder.compile();
fail();
} catch (AssertionError ae) {
// Expected since the library is invalid.
}
TestDiagnosticMessages diagnosticMessages = l8TestBuilder.getDiagnosticMessages();
diagnosticMessages.assertOnlyWarnings();
assertEquals(diagnosticMessages.getWarnings().size(), 1);
assertTrue(
diagnosticMessages
.getWarnings()
.get(0)
.getDiagnosticMessage()
.contains(
"Desugared library requires to be compiled with a library file of API greater or"
+ " equal to"));
}
@Test
public void testDesugaredLibraryContentD8() throws Exception {
Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
CodeInspector inspector = new CodeInspector(buildDesugaredLibrary(parameters.getApiLevel()));
assertCorrect(inspector);
}
@Test
public void testDesugaredLibraryContentR8() throws Exception {
Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
CodeInspector inspector =
new CodeInspector(
buildDesugaredLibrary(parameters.getApiLevel(), "-keep class * { *; }", true));
assertCorrect(inspector);
}
@Test
public void testDesugaredLibraryContentWithCoreLambdaStubsAsProgram() throws Exception {
Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
ArrayList<Path> coreLambdaStubs = new ArrayList<>();
coreLambdaStubs.add(ToolHelper.getCoreLambdaStubs());
CodeInspector inspector =
new CodeInspector(
buildDesugaredLibrary(parameters.getApiLevel(), "", false, coreLambdaStubs));
assertCorrect(inspector);
}
@Test
public void testDesugaredLibraryContentWithCoreLambdaStubsAsLibrary() throws Exception {
Assume.assumeTrue(requiresAnyCoreLibDesugaring(parameters));
TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs_dex.zip");
L8Command.Builder l8Builder =
L8Command.builder(diagnosticsHandler)
.addLibraryFiles(getLibraryFile())
.addProgramFiles(ToolHelper.getDesugarJDKLibs())
.addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
.addLibraryFiles(ToolHelper.getCoreLambdaStubs())
.addDesugaredLibraryConfiguration(
StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting()))
.setMinApiLevel(parameters.getApiLevel().getLevel())
.setOutput(desugaredLib, OutputMode.DexIndexed);
ToolHelper.runL8(l8Builder.build(), options -> {});
CodeInspector codeInspector = new CodeInspector(desugaredLib);
assertCorrect(codeInspector);
assertOneWarning(diagnosticsHandler);
}
private void assertOneWarning(TestDiagnosticMessagesImpl diagnosticsHandler) {
assertEquals(
(isJDK11DesugaredLibrary() && parameters.getApiLevel().isLessThan(AndroidApiLevel.O))
? 2
: 1,
diagnosticsHandler.getWarnings().size());
String msg = diagnosticsHandler.getWarnings().get(0).getDiagnosticMessage();
assertTrue(
msg.contains(
"The following library types, prefixed by java., are present both as library and non"
+ " library classes"));
assertTrue(diagnosticsHandler.getErrors().isEmpty());
}
private void assertCorrect(CodeInspector inspector) {
inspector.allClasses().forEach(clazz -> assertThat(clazz.getOriginalName(), startsWith("j$.")));
assertThat(inspector.clazz("j$.time.Clock"), isPresent());
// Above N the following classes are removed instead of being desugared.
if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
assertFalse(inspector.clazz("j$.util.Optional").isPresent());
assertFalse(inspector.clazz("j$.util.function.Function").isPresent());
return;
}
assertThat(inspector.clazz("j$.util.Optional"), isPresent());
assertThat(inspector.clazz("j$.util.function.Function"), isPresent());
if (parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) {
inspector.forAllClasses(clazz -> clazz.forAllMethods(this::assertNoSupressedInvocations));
}
}
private void assertNoSupressedInvocations(FoundMethodSubject method) {
if (method.isAbstract()) {
return;
}
for (InstructionSubject instruction : method.instructions()) {
if (instruction.isInvoke() && instruction.getMethod() != null) {
assertNotEquals(
instruction.getMethod(), new DexItemFactory().throwableMethods.addSuppressed);
}
}
}
}