blob: 784d320918e1dc79f2a7cffe8336e3d16194c1da [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.desugaredlibraryjdktests;
import static com.android.tools.r8.ToolHelper.JDK_TESTS_BUILD_DIR;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.BeforeClass;
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 Jdk11StreamTests extends Jdk11CoreLibTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
@Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
public static List<Object[]> data() {
// TODO(134732760): Support Dalvik VMs, currently fails because libjavacrypto is required and
// present only in ART runtimes.
return buildParameters(
BooleanUtils.values(),
getTestParameters()
.withDexRuntimesStartingFromIncluding(Version.V5_1_1)
.withAllApiLevels()
.build());
}
public Jdk11StreamTests(boolean shrinkDesugaredLibrary, TestParameters parameters) {
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
private static final Path JDK_11_STREAM_TEST_CLASSES_DIR =
Paths.get(ToolHelper.JDK_11_TESTS_CLASSES_DIR + "Stream");
private static final Path JDK_11_STREAM_TEST_FILES_DIR =
Paths.get("third_party/openjdk/jdk-11-test/java/util/stream/test");
private static Path[] JDK_11_STREAM_TEST_COMPILED_FILES;
private static Path[] getJdk11StreamTestFiles() throws Exception {
Path[] files = getAllFilesWithSuffixInDirectory(JDK_11_STREAM_TEST_FILES_DIR, JAVA_EXTENSION);
assert files.length > 0;
return files;
}
private static String[] FAILING_RUNNABLE_TESTS =
new String[] {
// J9 failure.
"org/openjdk/tests/java/util/stream/SpliteratorTest.java",
"org/openjdk/tests/java/util/stream/WhileOpStatefulTest.java",
"org/openjdk/tests/java/util/stream/IterateTest.java",
"org/openjdk/tests/java/util/stream/WhileOpTest.java",
// forEach failure
"org/openjdk/tests/java/util/stream/FindFirstOpTest.java",
"org/openjdk/tests/java/util/stream/MapOpTest.java",
// Disabled because explicit cast done on a wrapped value.
// "org/openjdk/tests/java/util/SplittableRandomTest.java",
// Assertion error
"org/openjdk/tests/java/util/stream/StreamCloseTest.java",
"org/openjdk/tests/java/util/stream/CollectAndSummaryStatisticsTest.java",
"org/openjdk/tests/java/util/stream/CountTest.java",
// J9 Random problem
"org/openjdk/tests/java/util/stream/LongPrimitiveOpsTests.java",
"org/openjdk/tests/java/util/stream/IntPrimitiveOpsTests.java",
"org/openjdk/tests/java/util/stream/DistinctOpTest.java",
"org/openjdk/tests/java/util/stream/DoublePrimitiveOpsTests.java"
};
// Disabled because time to run > 1 min for each test.
// Can be used for experimentation/testing purposes.
private static String[] LONG_RUNNING_TESTS =
new String[] {
"org/openjdk/tests/java/util/stream/InfiniteStreamWithLimitOpTest.java",
"org/openjdk/tests/java/util/stream/CountLargeTest.java",
"org/openjdk/tests/java/util/stream/RangeTest.java",
"org/openjdk/tests/java/util/stream/CollectorsTest.java",
"org/openjdk/tests/java/util/stream/FlatMapOpTest.java",
"org/openjdk/tests/java/util/stream/StreamSpliteratorTest.java",
"org/openjdk/tests/java/util/stream/StreamLinkTest.java",
"org/openjdk/tests/java/util/stream/StreamBuilderTest.java",
"org/openjdk/tests/java/util/stream/SliceOpTest.java",
"org/openjdk/tests/java/util/stream/ToArrayOpTest.java"
};
private static String[] SUCCESSFUL_RUNNABLE_TESTS =
new String[] {
"org/openjdk/tests/java/util/MapTest.java",
"org/openjdk/tests/java/util/FillableStringTest.java",
"org/openjdk/tests/java/util/stream/ForEachOpTest.java",
"org/openjdk/tests/java/util/stream/CollectionAndMapModifyStreamTest.java",
"org/openjdk/tests/java/util/stream/GroupByOpTest.java",
"org/openjdk/tests/java/util/stream/PrimitiveAverageOpTest.java",
"org/openjdk/tests/java/util/stream/TeeOpTest.java",
"org/openjdk/tests/java/util/stream/MinMaxTest.java",
"org/openjdk/tests/java/util/stream/ConcatTest.java",
"org/openjdk/tests/java/util/stream/StreamParSeqTest.java",
"org/openjdk/tests/java/util/stream/ReduceByOpTest.java",
"org/openjdk/tests/java/util/stream/ConcatOpTest.java",
"org/openjdk/tests/java/util/stream/IntReduceTest.java",
"org/openjdk/tests/java/util/stream/SortedOpTest.java",
"org/openjdk/tests/java/util/stream/MatchOpTest.java",
"org/openjdk/tests/java/util/stream/IntSliceOpTest.java",
"org/openjdk/tests/java/util/stream/SequentialOpTest.java",
"org/openjdk/tests/java/util/stream/PrimitiveSumTest.java",
"org/openjdk/tests/java/util/stream/ReduceTest.java",
"org/openjdk/tests/java/util/stream/IntUniqOpTest.java",
"org/openjdk/tests/java/util/stream/FindAnyOpTest.java"
};
private static Map<String, String> getRunnableTests(String[] tests) {
IdentityHashMap<String, String> pathToName = new IdentityHashMap<>();
int javaExtSize = JAVA_EXTENSION.length();
for (String runnableTest : tests) {
String nameWithoutJavaExt = runnableTest.substring(0, runnableTest.length() - javaExtSize);
pathToName.put(
JDK_11_STREAM_TEST_CLASSES_DIR + "/" + nameWithoutJavaExt + CLASS_EXTENSION,
nameWithoutJavaExt.replace("/", "."));
}
return pathToName;
}
private static String[] missingDesugaredMethods() {
// These methods are from Java 9 and not supported in the current desugared libraries.
return new String[]{
// Stream
"takeWhile(",
"dropWhile(",
"iterate(",
"ofNullable(",
"range(",
"doubles(",
// Collectors
"filtering(",
"flatMapping(",
};
}
@BeforeClass
public static void compileJdk11StreamTests() throws Exception {
File streamClassesDir = new File(JDK_11_STREAM_TEST_CLASSES_DIR.toString());
assert streamClassesDir.exists() || streamClassesDir.mkdirs();
List<String> options =
Arrays.asList(
"--add-reads",
"java.base=ALL-UNNAMED",
"--patch-module",
"java.base=" + JDK_11_JAVA_BASE_EXTENSION_CLASSES_DIR);
javac(CfVm.JDK11, getStaticTemp())
.addOptions(options)
.addClasspathFiles(
Collections.singletonList(Paths.get(JDK_TESTS_BUILD_DIR + "testng-6.10.jar")))
.addSourceFiles(getJdk11StreamTestFiles())
.setOutputPath(JDK_11_STREAM_TEST_CLASSES_DIR)
.compile();
JDK_11_STREAM_TEST_COMPILED_FILES =
getAllFilesWithSuffixInDirectory(JDK_11_STREAM_TEST_CLASSES_DIR, CLASS_EXTENSION);
assert JDK_11_STREAM_TEST_COMPILED_FILES.length > 0;
}
@Test
public void testStream() throws Exception {
Assume.assumeFalse(
"getAllFilesWithSuffixInDirectory() seems to find different files on Windows",
ToolHelper.isWindows());
Assume.assumeTrue(
"Requires Java base extensions, should add it when not desugaring",
parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel());
D8TestCompileResult compileResult = compileStreamTestsToDex();
runSuccessfulTests(compileResult);
runFailingTests(compileResult);
}
private D8TestCompileResult compileStreamTestsToDex() throws Exception {
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
List<Path> filesToCompile =
Arrays.stream(JDK_11_STREAM_TEST_COMPILED_FILES)
.filter(file -> !file.toString().contains("lang/invoke"))
.collect(Collectors.toList());
return testForD8()
.addProgramFiles(filesToCompile)
.addProgramFiles(getPathsFiles())
.addProgramFiles(getSafeVarArgsFile())
.addProgramFiles(testNGSupportProgramFiles())
.addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
.addDesugaredCoreLibraryRunClassPath(
this::buildDesugaredLibraryWithJavaBaseExtension,
parameters.getApiLevel(),
keepRuleConsumer.get(),
shrinkDesugaredLibrary)
.withArtFrameworks()
.withArt6Plus64BitsLib();
}
private void runSuccessfulTests(D8TestCompileResult compileResult) throws Exception {
String verbosity = "2"; // Increase verbosity for debugging.
Map<String, String> runnableTests = getRunnableTests(SUCCESSFUL_RUNNABLE_TESTS);
for (String path : runnableTests.keySet()) {
assert runnableTests.get(path) != null;
D8TestRunResult result =
compileResult.run(
parameters.getRuntime(), "TestNGMainRunner", verbosity, runnableTests.get(path));
assertTrue(
result
.getStdOut()
.endsWith(
StringUtils.lines("Tests result in " + runnableTests.get(path) + ": SUCCESS")));
}
}
private void runFailingTests(D8TestCompileResult compileResult) throws Exception {
// For failing runnable tests, we just ensure that they do not fail due to desugaring, but
// due to an expected failure (missing API, etc.).
String verbosity = "2"; // Increase verbosity for debugging.
Map<String, String> runnableTests = getRunnableTests(FAILING_RUNNABLE_TESTS);
for (String path : runnableTests.keySet()) {
assert runnableTests.get(path) != null;
D8TestRunResult result =
compileResult.run(
parameters.getRuntime(), "TestNGMainRunner", verbosity, runnableTests.get(path));
if (result
.getStdOut()
.endsWith(
StringUtils.lines("Tests result in " + runnableTests.get(path) + ": SUCCESS"))) {
// The test succeeds, this can happen on tests succeeding only on high API levels.
assertTrue(
parameters.getRuntime().asDex().getMinApiLevel().getLevel()
>= AndroidApiLevel.N.getLevel());
} else if (result.getStdOut().contains("java.lang.NoSuchMethodError")
&& Arrays.stream(missingDesugaredMethods())
.anyMatch(method -> result.getStdOut().contains(method))) {
// TODO(b/134732760): support Java 9 APIs.
} else if (result
.getStdOut()
.contains("java.lang.NoSuchMethodError: No interface method forEach")) {
// TODO(b/134732760): fix tests no to use Iterable#forEach
} else if (result.getStdOut().contains("in class Ljava/util/Random")
&& result.getStdOut().contains("java.lang.NoSuchMethodError")) {
// TODO(b/134732760): Random Java 9 Apis, support or do not use them.
} else if (result.getStdOut().contains("java.lang.AssertionError")) {
// TODO(b/134732760): Investigate and fix these issues.
} else if (result.getStdErr().contains("$r8$wrapper$")) {
// Use of high library API on low API device, cannot do anything about this.
assertTrue(
parameters.getRuntime().asDex().getMinApiLevel().getLevel()
< AndroidApiLevel.N.getLevel());
} else {
String errorMessage = "STDOUT:\n" + result.getStdOut() + "STDERR:\n" + result.getStdErr();
fail(errorMessage);
}
}
}
}