Add a test of desugaring java.util.BufferedReader.lines
This includes testing support to amend the current default desugar
configuration.
Bug: 147485959
Change-Id: I633790db4e1f9f15985708fa4af6504b44a4ed2f
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
index d687d19..7586210 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
public class DesugaredLibraryConfigurationParser {
@@ -77,6 +78,12 @@
}
public DesugaredLibraryConfiguration parse(StringResource stringResource) {
+ return parse(stringResource, builder -> {});
+ }
+
+ public DesugaredLibraryConfiguration parse(
+ StringResource stringResource,
+ Consumer<DesugaredLibraryConfiguration.Builder> configurationAmender) {
origin = stringResource.getOrigin();
assert origin != null;
configurationBuilder = DesugaredLibraryConfiguration.builder(dexItemFactory, reporter, origin);
@@ -142,6 +149,7 @@
}
configurationBuilder.setExtraKeepRules(extraKeepRules);
}
+ configurationAmender.accept(configurationBuilder);
DesugaredLibraryConfiguration config = configurationBuilder.build();
configurationBuilder = null;
origin = null;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
new file mode 100644
index 0000000..99b1316
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -0,0 +1,250 @@
+// Copyright (c) 2020, 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 org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Assume;
+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 BufferedReaderTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ getTestParameters()
+ .withAllRuntimes()
+ .withAllApiLevelsAlsoForCf()
+ .withApiLevel(AndroidApiLevel.N)
+ .build());
+ }
+
+ public BufferedReaderTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ private String expectedOutput() {
+ return StringUtils.lines(
+ "Hello",
+ "Larry",
+ "Page",
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+ ? "Caught java.io.UncheckedIOException"
+ : "Caught j$.io.UncheckedIOException");
+ }
+
+ DesugaredLibraryConfiguration configurationWithBufferedReader(
+ InternalOptions options, boolean libraryCompilation, TestParameters parameters) {
+ // Parse the current configuration and amend the configuration for BufferedReader.lines. The
+ // configuration is the same for both program and library.
+ return new DesugaredLibraryConfigurationParser(
+ options.dexItemFactory(),
+ options.reporter,
+ libraryCompilation,
+ parameters.getApiLevel().getLevel())
+ .parse(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING),
+ builder -> {
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ builder.putRewritePrefix(
+ "java.io.DesugarBufferedReader", "j$.io.DesugarBufferedReader");
+ builder.putRewritePrefix(
+ "java.io.UncheckedIOException", "j$.io.UncheckedIOException");
+ builder.putRetargetCoreLibMember(
+ "java.io.BufferedReader#lines", "java.io.DesugarBufferedReader");
+ }
+ });
+ }
+
+ private void configurationForProgramCompilation(InternalOptions options) {
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, false, parameters);
+ }
+
+ private void configurationForLibraryCompilation(InternalOptions options) {
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, true, parameters);
+ }
+
+ @Test
+ public void testBufferedReaderD8Cf() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ // Use D8 to desugar with Java classfile output.
+ Path jar =
+ testForD8(Backend.CF)
+ .addOptionsModification(this::configurationForProgramCompilation)
+ .addInnerClasses(BufferedReaderTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ // .inspect(this::checkRewrittenInvokes)
+ .writeToZip();
+
+ if (parameters.getRuntime().isDex()) {
+ // Collection keep rules is only implemented in the DEX writer.
+ String desugaredLibraryKeepRules = keepRuleConsumer.get();
+ if (desugaredLibraryKeepRules != null) {
+ assertEquals(0, desugaredLibraryKeepRules.length());
+ desugaredLibraryKeepRules = "-keep class * { *; }";
+ }
+
+ // Convert to DEX without desugaring and run.
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (apiLevel, keepRules, shrink) ->
+ buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ this::configurationForLibraryCompilation),
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ } else {
+ // Build the desugared library in class file format.
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+ L8Command.Builder l8Builder =
+ L8Command.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .setMode(CompilationMode.DEBUG)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.ClassFile);
+ ToolHelper.runL8(l8Builder.build(), this::configurationForLibraryCompilation);
+
+ // Run on the JVM with desuagred library on classpath.
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(desugaredLib)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ }
+ }
+
+ @Test
+ public void testBufferedReaderD8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addOptionsModification(
+ options ->
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, false, parameters))
+ .addInnerClasses(BufferedReaderTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (apiLevel, keepRules, shrink) ->
+ buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ this::configurationForLibraryCompilation),
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ }
+
+ @Test
+ public void testBufferedReaderR8() throws Exception {
+ Assume.assumeTrue(parameters.getRuntime().isDex());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addOptionsModification(
+ options ->
+ options.desugaredLibraryConfiguration =
+ configurationWithBufferedReader(options, false, parameters))
+ .addInnerClasses(BufferedReaderTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .enableInliningAnnotations()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (apiLevel, keepRules, shrink) ->
+ buildDesugaredLibrary(
+ apiLevel,
+ keepRules,
+ shrink,
+ ImmutableList.of(),
+ this::configurationForLibraryCompilation),
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput());
+ }
+
+ static class TestClass {
+
+ @NeverInline
+ public static void testBufferedReaderLines() throws Exception {
+ try (BufferedReader reader = new BufferedReader(new StringReader("Hello\nLarry\nPage"))) {
+ reader.lines().forEach(System.out::println);
+ }
+ }
+
+ @NeverInline
+ public static void testBufferedReaderLines_uncheckedIoException() throws Exception {
+ BufferedReader reader = new BufferedReader(new StringReader(""));
+ reader.close();
+ try {
+ reader.lines().count();
+ System.out.println("UncheckedIOException expected");
+ } catch (UncheckedIOException expected) {
+ System.out.println("Caught " + expected.getClass().getName());
+ } catch (Throwable t) {
+ System.out.println("Caught unexpected" + t.getClass().getName());
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ testBufferedReaderLines();
+ testBufferedReaderLines_uncheckedIoException();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index afc19d9..e520016 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
@@ -29,6 +30,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Consumer;
public class DesugaredLibraryTestBase extends TestBase {
@@ -69,7 +71,7 @@
}
protected Path buildDesugaredLibrary(AndroidApiLevel apiLevel, String keepRules, boolean shrink) {
- return buildDesugaredLibrary(apiLevel, keepRules, shrink, ImmutableList.of());
+ return buildDesugaredLibrary(apiLevel, keepRules, shrink, ImmutableList.of(), options -> {});
}
protected Path buildDesugaredLibrary(
@@ -77,6 +79,16 @@
String keepRules,
boolean shrink,
List<Path> additionalProgramFiles) {
+ return buildDesugaredLibrary(
+ apiLevel, keepRules, shrink, additionalProgramFiles, options -> {});
+ }
+
+ protected Path buildDesugaredLibrary(
+ AndroidApiLevel apiLevel,
+ String keepRules,
+ boolean shrink,
+ List<Path> additionalProgramFiles,
+ Consumer<InternalOptions> optionsModifier) {
// We wrap exceptions in a RuntimeException to call this from a lambda.
try {
// If we compile extended library here, it means we use TestNG.
@@ -108,6 +120,7 @@
options.testing.disableL8AnnotationRemoval = true;
options.testing.forceLibBackportsInL8CfToCf = true;
}
+ optionsModifier.accept(options);
});
if (!extraFiles) {
assertTrue(