diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/android')
80 files changed, 7610 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/android/BUILD b/src/test/java/com/google/devtools/build/android/BUILD index 77bb8dfa07..2984c27894 100644 --- a/src/test/java/com/google/devtools/build/android/BUILD +++ b/src/test/java/com/google/devtools/build/android/BUILD @@ -2,6 +2,7 @@ filegroup( name = "srcs", srcs = glob(["**"]) + [ "//src/test/java/com/google/devtools/build/android/idlclass:srcs", + "//src/test/java/com/google/devtools/build/android/desugar:srcs", "//src/test/java/com/google/devtools/build/android/dexer:srcs", "//src/test/java/com/google/devtools/build/android/junctions:srcs", "//src/test/java/com/google/devtools/build/android/resources:srcs", diff --git a/src/test/java/com/google/devtools/build/android/desugar/BUILD b/src/test/java/com/google/devtools/build/android/desugar/BUILD new file mode 100644 index 0000000000..6d0187ef1c --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD @@ -0,0 +1,1776 @@ +# Description: +# Tests for the Java 8 desugaring tool for Android. +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["**"]) + [ + "//src/test/java/com/google/devtools/build/android/desugar/runtime:srcs", + ], + visibility = ["//src/test/java/com/google/devtools/build/android:__pkg__"], +) + +filegroup( + name = "android_jar_for_testing", + srcs = select({ + # TODO(ajmichael): Use //tools/defaults:android_jar here once it supplies runfiles. + "//external:has_androidsdk": ["@androidsdk//:platforms/android-25/android.jar"], + "//conditions:default": ["@bazel_tools//tools/android:error_message.jar"], + }), +) + +java_test( + name = "DesugarFunctionalTest", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + deps = [ + ":testdata_desugared", # Make tests run against desugared library + "//third_party:asm", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarIdempotencyFunctionalTest", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":testdata_desugared_twice", # Make tests run against twice-desugared library + "//third_party:asm", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +# Test for b/62456849. If a synthetic method is named "lambda$XXX", but not used in invokedynamic, +# then Desugar should keep it in the class, rather than renaming it. +java_test( + name = "DesugarFunctionalTestForSyntheticMethodsWithLambdaNames", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":desugar_testdata_with_synthetic_methods_with_lambda_names", + ":separate", + "//third_party:asm", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarLongCompareTest", + size = "small", + srcs = [ + "DesugarLongCompareTest.java", + ], + tags = ["no_windows"], + deps = [ + ":testdata_desugared", # Make tests run against desugared library + "//third_party:asm", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarObjectsRequireNonNullTest", + size = "small", + srcs = [ + "DesugarObjectsRequireNonNullTest.java", + ], + tags = ["no_windows"], + deps = [ + ":testdata_desugared", # Make tests run against desugared library + "//third_party:asm", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarObjectsRequireNonNullTestForAndroidLintMode", + size = "small", + srcs = ["DesugarObjectsRequireNonNullTest.java"], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarObjectsRequireNonNullTest", + deps = [ + ":desugar_testdata_by_disabling_lambda_desugaring", # Make tests run against desugared library + "//third_party:asm", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarJava8FunctionalTest", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + "DesugarJava8FunctionalTest.java", + ], + tags = ["no_windows"], + deps = [ + ":testdata_desugared_java8", # Make tests run against desugared library + "//src/test/java/com/google/devtools/build/lib:testutil", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarJava8LikeAndroidStudioFunctionalTest", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + "DesugarJava8FunctionalTest.java", + "DesugarJava8LikeAndroidStudioFunctionalTest.java", + ], + tags = ["no_windows"], + deps = [ + ":libseparate.jar", + ":testdata_desugared_java8_like_in_android_studio.jar", # Make tests run against desugared library + "//src/test/java/com/google/devtools/build/lib:testutil", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarDefaultMethodsFunctionalTest", + size = "small", + srcs = [ + "DesugarDefaultMethodsFunctionalTest.java", + "DesugarFunctionalTest.java", + "DesugarJava8FunctionalTest.java", + ], + tags = ["no_windows"], + deps = [ + ":testdata_desugared_default_methods", # Make tests run against desugared library + "//src/test/java/com/google/devtools/build/lib:testutil", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarDefaultMethodsIdempotencyFunctionalTest", + size = "small", + srcs = [ + "DesugarDefaultMethodsFunctionalTest.java", + "DesugarFunctionalTest.java", + "DesugarJava8FunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarDefaultMethodsFunctionalTest", + deps = [ + ":testdata_desugared_default_methods_twice", # Make tests run against 2x desugared library + "//src/test/java/com/google/devtools/build/lib:testutil", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarLikeAndroidStudioFunctionalTest", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":testdata_desugared_like_in_android_studio", # Make tests run against desugared library + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarFunctionalTestWithMultipleInputs", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":testdata_desugared_with_multiple_inputs", # Make tests run against desugared library + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarFunctionalTestFromDirectoryToJar", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":testdata_desugared_from_directory_to_jar", # Make tests run against desugared library + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarFunctionalTestFromDirectoryToDirectory", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":testdata_desugared_from_directory_to_directory", # Make tests run against desugared library + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarFunctionalTestWithClasspathDirectory", + size = "small", + srcs = [ + "DesugarFunctionalTest.java", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarFunctionalTest", + deps = [ + ":testdata_desugared_with_classpath_directory", # Make tests run against desugared library + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarCoreLibraryFunctionalTest", + size = "small", + srcs = [ + "DesugarCoreLibraryFunctionalTest.java", + ], + jvm_flags = ["-Xbootclasspath/p:$(location :testdata_desugared_core_library):$(location //third_party/java/jacoco:blaze-agent)"], + tags = ["no_windows"], + deps = [ + ":testdata_desugared_core_library", # Make tests run against desugared library + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + ], +) + +java_test( + name = "StackMapBugTest", + size = "small", + srcs = [ + "StackMapBugTest.java", + ], + jvm_flags = ["-Xbootclasspath/p:$(location //third_party/java/jacoco:blaze-agent)"], + tags = ["no_windows"], + deps = [ + ":testdata_desugared_core_library", # Make tests run against desugared library + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + ], +) + +java_test( + name = "DefaultMethodClassFixerTest", + size = "small", + srcs = ["DefaultMethodClassFixerTest.java"], + data = [ + ":android_jar_for_testing", + ":guava_base_classpath.jar", + ":separate", + ":testdata_java8", + ], + jvm_flags = [ + "-DDefaultMethodClassFixerTest.bootclasspath=$(location :android_jar_for_testing)", + "-DDefaultMethodClassFixerTest.classpath=$(location :separate):$(location :guava_base_classpath.jar):$(location //third_party/java/jacoco:blaze-agent)", + "-DDefaultMethodClassFixerTest.input=$(location :testdata_java8)", + ], + tags = ["no_windows"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:asm", + "//third_party:asm-tree", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + "//third_party/java/jacoco:blaze-agent", + ], +) + +java_test( + name = "MethodInfoTest", + size = "small", + srcs = ["MethodInfoTest.java"], + tags = ["no_windows"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "FieldInfoTest", + size = "small", + srcs = ["FieldInfoTest.java"], + tags = ["no_windows"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "Java7CompatibilityTest", + size = "small", + srcs = ["Java7CompatibilityTest.java"], + tags = ["no_windows"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:asm", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "IndexedInputsTest", + size = "small", + srcs = ["IndexedInputsTest.java"], + tags = ["no_windows"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "TryWithResourcesRewriterUnitTestWithReuseStrategy", + size = "small", + srcs = [ + "TryWithResourcesRewriterTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=19", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$ReuseDesugaringStrategy'", + ], + test_class = "com.google.devtools.build.android.desugar.TryWithResourcesRewriterTest", + deps = [ + ":mocked_android_os_sdk_for_testing", + ":testdata", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "TryWithResourcesRewriterUnitTestWithMimicStrategy", + size = "small", + srcs = [ + "TryWithResourcesRewriterTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$MimicDesugaringStrategy'", + ], + test_class = "com.google.devtools.build.android.desugar.TryWithResourcesRewriterTest", + deps = [ + ":mocked_android_os_sdk_for_testing", + ":testdata", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "TryWithResourcesRewriterUnitTestWithNullStrategy", + size = "small", + srcs = [ + "TryWithResourcesRewriterTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "-Dcom.google.devtools.build.android.desugar.runtime.twr_disable_mimic=true", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$NullDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.TryWithResourcesRewriterTest", + deps = [ + ":mocked_android_os_sdk_for_testing", + ":testdata", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +# This test sets the lambda dump directory via the system property in the command line +# (i.e., jvm_flags). So the dump directory is expected to succeed. +java_test( + name = "DesugarMainCLassTestLambdaDirectoryCorrectlySet", + size = "small", + srcs = ["DesugarMainClassTest.java"], + jvm_flags = ["-Djdk.internal.lambda.dumpProxyClasses=$$(mktemp -d)"], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarMainClassTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +# Different from the test target above, this test does not set the lambda dump directory in the +# command line. Instead, it sets the system property in the test code, which is expected to fail. +java_test( + name = "DesugarMainCLassTestLambdaDirectoryIncorrectlySet", + size = "small", + srcs = ["DesugarMainClassTest.java"], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarMainClassTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +sh_test( + name = "testdata_desugared_jar_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared.jar)", + "$(location testdata_desugared_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared.jar", + "testdata_desugared_jar_toc_golden.txt", # Golden file + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +# Make sure desugaring twice doesn't add any files +sh_test( + name = "testdata_desugared_twice_jar_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_twice.jar)", + "$(location testdata_desugared_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_jar_toc_golden.txt", + "testdata_desugared_twice.jar", + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +sh_test( + name = "testdata_desugared_java8_jar_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_java8.jar)", + "$(location testdata_desugared_java8_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_java8.jar", + "testdata_desugared_java8_jar_toc_golden.txt", # Golden file + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +# TODO(b/37110951): Add jar content test for desugared default methods that works with coverage on + +sh_test( + name = "testdata_desugared_core_library_jar_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_core_library.jar)", + "$(location testdata_desugared_core_library_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_core_library.jar", + "testdata_desugared_core_library_jar_toc_golden.txt", # Golden file + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +sh_test( + name = "testdata_desugared_for_try_with_resources_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_for_try_with_resources.jar)", + "$(location testdata_desugared_for_try_with_resources_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_for_try_with_resources.jar", + "testdata_desugared_for_try_with_resources_jar_toc_golden.txt", # Golden file + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +sh_test( + name = "testdata_desugared_for_try_with_resources_test_twice", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_for_try_with_resources_twice.jar)", + "$(location testdata_desugared_for_try_with_resources_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_for_try_with_resources_jar_toc_golden.txt", # Golden file + "testdata_desugared_for_try_with_resources_twice.jar", + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +# Test for b/63900665 +sh_test( + name = "testdata_desugared_for_disabling_try_with_resources_with_large_minsdkversion_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_for_NO_desugaring_try_with_resources.jar)", + "$(location testdata_desugared_for_disabling_twr_with_large_minsdkversion_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_for_NO_desugaring_try_with_resources.jar", + "testdata_desugared_for_disabling_twr_with_large_minsdkversion_jar_toc_golden.txt", # Golden file + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +# Test for b/62047432 +sh_test( + name = "initializer_of_functional_interface_should_not_execute", + size = "small", + srcs = ["static_initializer_of_functional_interface_should_not_execute.sh"], + args = [ + "$(location :desugar_testdata_with_default_methods_for_static_initializer_test) ", + ], + data = [ + ":desugar_testdata_with_default_methods_for_static_initializer_test", + ], + tags = ["no_windows"], +) + +java_library( + name = "testdata", + srcs = glob(["testdata/*.java"]), + resources = ["testdata/testresource.txt"], + deps = [ + ":separate", + "//third_party:guava", + ], +) + +java_library( + name = "testdata_java8", + srcs = glob([ + "testdata/*.java", + "testdata/java8/**/*.java", + ]), + resources = ["testdata/testresource.txt"], + deps = [ + ":separate", + "//third_party:guava", + ], +) + +java_library( + name = "testdata_core_library", + srcs = glob([ + "testdata/core_library/**/*.java", + ]), + resources = ["testdata/testresource.txt"], + deps = [ + "//third_party:error_prone", + "//third_party:guava", + ], +) + +java_library( + name = "separate", + srcs = glob(["testdata/separate/*.java"]), +) + +java_library( + name = "testdata_like_in_android_studio", + srcs = glob(["testdata/*.java"]), + resources = ["testdata/testresource.txt"], + deps = [ + ":libseparate.jar", # puts full Jar instead of header Jar on the classpath + "//third_party:guava", + ], +) + +java_library( + name = "testdata_java8_like_in_android_studio", + srcs = glob([ + "testdata/*.java", + "testdata/java8/**/*.java", + ]), + resources = ["testdata/testresource.txt"], + deps = [ + ":libseparate.jar", # puts full Jar instead of header Jar on the classpath + "//third_party:guava", + ], +) + +# The golden file for this target should NEVER contain generated lambda classes. +sh_test( + name = "desugar_testdata_by_disabling_lambda_desugaring_test", + size = "small", + srcs = ["testdata_desugared_jar_test.sh"], + args = [ + "$(location testdata_desugared_without_lambda_desugared.jar)", + "$(location testdata_desugared_without_lambda_desugared_jar_toc_golden.txt)", + "$(JAVABASE)", + ], + data = [ + "testdata_desugared_without_lambda_desugared.jar", + "testdata_desugared_without_lambda_desugared_jar_toc_golden.txt", # Golden file + "@bazel_tools//tools/jdk", + ], + tags = ["no_windows"], +) + +# The following genrules run the code tested here as a build tool. While that's +# very similar to how Blaze will invoke it natively, the downside is that +# running the tested tool in a genrule doesn't contribute code coverage +# information for the tested tool. Note that the code in :testdata doesn't +# appear in coverage reports when depending on this target regardless because +# the needed metadata doesn't survive these genrules. +genrule( + name = "desugar_testdata", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--nodesugar_interface_method_bodies_if_needed -i $(location :testdata) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +# Convert human-written methods whose names start with "lambda$XXX" to synthetic methods, so we can +# test for b/62456849. +genrule( + name = "generate_synthetic_methods_with_lambda_names_in_test_data", + srcs = [":testdata"], + outs = ["testdata_with_generated_synthetic_methods_with_lambda_names.jar"], + cmd = "$(location :generate_synthetic_method_with_lambda_name_convention) " + + " $(location :testdata) " + + " $@ ", + tags = ["no_windows"], + tools = [":generate_synthetic_method_with_lambda_name_convention"], +) + +# Desugar the test data for b/62456849 +genrule( + name = "desugar_testdata_with_synthetic_methods_with_lambda_names", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":generate_synthetic_methods_with_lambda_names_in_test_data", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_with_synthetic_methods_with_lambda_names.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--nodesugar_interface_method_bodies_if_needed -i $(location :generate_synthetic_methods_with_lambda_names_in_test_data) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_again", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_desugared.jar", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_twice.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--nodesugar_interface_method_bodies_if_needed -i $(location :testdata_desugared.jar) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +# This target tests the flag --allow_desugaring_lambda_for_lint=true, which is introduced to solve +# the incompatibility issue with Android Lint. When the issue is addressed, this target and the +# flag will be removed. +genrule( + name = "desugar_testdata_by_disabling_lambda_desugaring", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_without_lambda_desugared.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--min_sdk_version 17 -i $(location :testdata) -o $@ " + + "--nodesugar_try_with_resources_if_needed " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar) " + + "--only_desugar_javac9_for_lint", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +java_test( + name = "DesugarTryWithResourcesFunctionalTestWithNullStrategy", + size = "small", + srcs = [ + "DesugarTryWithResourcesFunctionalTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "-Dcom.google.devtools.build.android.desugar.runtime.twr_disable_mimic=true", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$NullDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarTryWithResourcesFunctionalTest", + deps = [ + "mocked_android_os_sdk_for_testing", + ":desugar_testdata_by_desugaring_try_with_resources", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarTryWithResourcesFunctionalTestWithNullStrategyTwice", + size = "small", + srcs = [ + "DesugarTryWithResourcesFunctionalTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "-Dcom.google.devtools.build.android.desugar.runtime.twr_disable_mimic=true", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$NullDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarTryWithResourcesFunctionalTest", + deps = [ + "mocked_android_os_sdk_for_testing", + ":desugar_testdata_by_desugaring_try_with_resources_twice", # the lib desugared twice + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarTryWithResourcesFunctionalTestWithMimicStrategy", + size = "small", + srcs = [ + "DesugarTryWithResourcesFunctionalTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$MimicDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarTryWithResourcesFunctionalTest", + deps = [ + ":desugar_testdata_by_desugaring_try_with_resources", + ":mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarTryWithResourcesFunctionalTestWithMimicStrategyTwice", + size = "small", + srcs = [ + "DesugarTryWithResourcesFunctionalTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$MimicDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarTryWithResourcesFunctionalTest", + deps = [ + ":desugar_testdata_by_desugaring_try_with_resources_twice", # the lib desugared twice. + ":mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarTryWithResourcesFunctionalTestWithReuseStrategy", + size = "small", + srcs = [ + "DesugarTryWithResourcesFunctionalTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=19", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$ReuseDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarTryWithResourcesFunctionalTest", + deps = [ + ":desugar_testdata_by_desugaring_try_with_resources", + ":mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "DesugarTryWithResourcesFunctionalTestWithReuseStrategyTwice", + size = "small", + srcs = [ + "DesugarTryWithResourcesFunctionalTest.java", + ], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=19", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$ReuseDesugaringStrategy'", + ], + tags = ["no_windows"], + test_class = "com.google.devtools.build.android.desugar.DesugarTryWithResourcesFunctionalTest", + deps = [ + ":desugar_testdata_by_desugaring_try_with_resources_twice", # the lib desugared twice. + ":mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", + "//third_party:asm", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_library( + name = "mocked_android_os_sdk_for_testing", + srcs = ["mocked_android_framework/android/os/Build.java"], +) + +genrule( + name = "desugar_testdata_java8", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_java8", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_java8.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--min_sdk_version 24 -i $(location :testdata_java8) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_java8_like_in_android_studio", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_java8_like_in_android_studio", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_java8_like_in_android_studio.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--copy_bridges_from_classpath " + + "--legacy_jacoco_fix " + + "--min_sdk_version 24 " + + "-i $(location :testdata_java8_like_in_android_studio) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_with_default_methods", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_java8", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_default_methods.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--desugar_interface_method_bodies_if_needed -i $(location :testdata_java8) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_with_default_methods_for_static_initializer_test", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_java8", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_default_methods.output.txt"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--desugar_interface_method_bodies_if_needed -i $(location :testdata_java8) " + + "-o desugar_testdata_with_default_methods_for_static_initializer_test.jar " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar) " + + " &> $@", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_with_default_methods_again", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_desugared_default_methods.jar", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_default_methods_twice.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--desugar_interface_method_bodies_if_needed -i $(location :testdata_desugared_default_methods.jar) -o $@ " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_core_library", + srcs = [ + ":testdata_core_library", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_core_library.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--core_library -i $(location :testdata_core_library) -o $@ " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_like_in_android_studio", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_like_in_android_studio", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_like_in_android_studio.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--min_sdk_version 23 " + + "--legacy_jacoco_fix " + + "--nodesugar_interface_method_bodies_if_needed " + + "-i $(location :testdata_like_in_android_studio) -o $@ " + + "--copy_bridges_from_classpath " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_and_separate_with_multiple_inputs", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_like_in_android_studio", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = [ + "testdata_desugared_with_multiple_inputs.jar", + "separate_desugared_with_multiple_inputs.jar", + ], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--min_sdk_version 23 --nodesugar_interface_method_bodies_if_needed " + + "-i $(location :testdata_like_in_android_studio) -o $(location testdata_desugared_with_multiple_inputs.jar) " + + "-i $(location :separate) -o $(location separate_desugared_with_multiple_inputs.jar) " + + "--copy_bridges_from_classpath " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_from_directory_to_jar", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_like_in_android_studio", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = [ + "testdata_desugared_from_directory_to_jar.jar", + ], + cmd = """ + tmpdir=$$(mktemp -d) + # unzip input jar in order to pass a folder to desugar + pwddir=$$PWD + (cd $$tmpdir; $$pwddir/$(location //tools/zip:zipper) x $$pwddir/$(location :testdata_like_in_android_studio)) + $(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) \ + --min_sdk_version 23 --nodesugar_interface_method_bodies_if_needed \ + -i $$tmpdir -o $(location testdata_desugared_from_directory_to_jar.jar) \ + --copy_bridges_from_classpath \ + --classpath_entry $(location :separate) \ + --classpath_entry $(location :guava_base_classpath.jar) \ + --classpath_entry $(location //third_party/java/jacoco:blaze-agent) \ + --bootclasspath_entry $(location //tools/defaults:android_jar) + rm -rf $$tmpdir + """, + tags = ["no_windows"], + tools = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar", + "//tools/zip:zipper", + ], +) + +genrule( + name = "desugar_testdata_from_directory_to_directory", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_like_in_android_studio", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = [ + "testdata_desugared_from_directory_to_directory.jar", + ], + cmd = """ + tmpdirIn=$$(mktemp -d) + tmpdirOut=$$(mktemp -d) + # unzip input jar in order to pass a folder to desugar + pwddir=$$PWD + (cd $$tmpdirIn; $$pwddir/$(location //tools/zip:zipper) x $$pwddir/$(location :testdata_like_in_android_studio)) + $(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) \ + --min_sdk_version 23 --nodesugar_interface_method_bodies_if_needed \ + -i $$tmpdirIn -o $$tmpdirOut \ + --copy_bridges_from_classpath \ + --classpath_entry $(location :separate) \ + --classpath_entry $(location :guava_base_classpath.jar) \ + --classpath_entry $(location //third_party/java/jacoco:blaze-agent) \ + --bootclasspath_entry $(location //tools/defaults:android_jar) + pushd $$tmpdirOut + $$pwddir/$(location //tools/zip:zipper) c $$pwddir/$(location testdata_desugared_from_directory_to_directory.jar) $$(find *) + popd + rm -rf $$tmpdirIn + rm -rf $$tmpdirOut + """, + tags = ["no_windows"], + tools = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar", + "//tools/zip:zipper", + ], +) + +genrule( + name = "desugar_testdata_with_classpath_directory", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata_like_in_android_studio", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = [ + "testdata_desugared_with_classpath_directory.jar", + ], + cmd = """ + tmpdir=$$(mktemp -d) + # unzip a classpath entry jar in order to pass a folder to desugar + pwddir=$$PWD + (cd $$tmpdir; $$pwddir/$(location //tools/zip:zipper) x $$pwddir/$(location :separate)) + $(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) \ + --min_sdk_version 23 --nodesugar_interface_method_bodies_if_needed \ + -i $(location :testdata_like_in_android_studio) -o $(location testdata_desugared_with_classpath_directory.jar) \ + --copy_bridges_from_classpath \ + --classpath_entry $$tmpdir \ + --classpath_entry $(location :guava_base_classpath.jar) \ + --classpath_entry $(location //third_party/java/jacoco:blaze-agent) \ + --bootclasspath_entry $(location //tools/defaults:android_jar) + rm -rf $$tmpdir + """, + tags = ["no_windows"], + tools = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar", + "//tools/zip:zipper", + ], +) + +# This target tests the flag -desugar_try_with_resources_if_needed, for desugaring +# try-with-resources statements. +genrule( + name = "desugar_testdata_by_desugaring_try_with_resources", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_for_try_with_resources.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--nodesugar_interface_method_bodies_if_needed -i $(location :testdata) -o $@ " + + "--min_sdk_version 17 --desugar_try_with_resources_if_needed " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +# Desugars an interface with a default and a static interface method that was +# processed by a legacy version of JaCoCo that doesn't emit interface markers +# when calling methods it generated in interfaces (b/62623509). +genrule( + name = "desugar_default_method_with_legacy_coverage", + srcs = [ + "jacoco_0_7_5_default_method.jar", + # Depend on Jacoco runtime in case :testdata_java8 was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["jacoco_0_7_5_default_method_desugared.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--copy_bridges_from_classpath " + + "--legacy_jacoco_fix " + + "--min_sdk_version 19 " + + "-i $(location :jacoco_0_7_5_default_method.jar) -o $@ " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +# Disable desugaring try-with-resources by specifying a min_sdk_version above 18. +genrule( + name = "desugar_testdata_by_disabling_desugaring_try_with_resources_with_minsdkversion_above_18", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":testdata", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_for_NO_desugaring_try_with_resources.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--nodesugar_interface_method_bodies_if_needed -i $(location :testdata) -o $@ " + + "--min_sdk_version 19 --desugar_try_with_resources_if_needed " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_testdata_by_desugaring_try_with_resources_twice", + srcs = [ + ":guava_base_classpath.jar", + ":separate", + ":desugar_testdata_by_desugaring_try_with_resources", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["testdata_desugared_for_try_with_resources_twice.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "--nodesugar_interface_method_bodies_if_needed -i $(location :desugar_testdata_by_desugaring_try_with_resources) -o $@ " + + "--min_sdk_version 17 --desugar_try_with_resources_if_needed " + + "--classpath_entry $(location :separate) " + + "--classpath_entry $(location :guava_base_classpath.jar) " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +# This target allows using the desugared jar as a regular dependency of the test +java_import( + name = "testdata_desugared", + jars = ["testdata_desugared.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_twice", + jars = ["testdata_desugared_twice.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_java8", + jars = ["testdata_desugared_java8.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_java8_like_in_android_studio", + jars = ["testdata_desugared_java8_like_in_android_studio.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_default_methods", + jars = ["testdata_desugared_default_methods.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_default_methods_twice", + jars = ["testdata_desugared_default_methods_twice.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_core_library", + jars = ["testdata_desugared_core_library.jar"], +) + +java_import( + name = "separate_desugared_with_multiple_inputs", + jars = ["separate_desugared_with_multiple_inputs.jar"], +) + +java_import( + name = "testdata_desugared_with_multiple_inputs", + jars = ["testdata_desugared_with_multiple_inputs.jar"], + deps = [":separate_desugared_with_multiple_inputs"], +) + +java_import( + name = "testdata_desugared_from_directory_to_jar", + jars = ["testdata_desugared_from_directory_to_jar.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_from_directory_to_directory", + jars = ["testdata_desugared_from_directory_to_directory.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_with_classpath_directory", + jars = ["testdata_desugared_with_classpath_directory.jar"], + deps = [":separate"], +) + +java_import( + name = "testdata_desugared_like_in_android_studio", + jars = ["testdata_desugared_like_in_android_studio.jar"], + deps = [":separate"], +) + +sh_test( + name = "stateless_lambda_has_no_factory_method_test", + srcs = ["diff.sh"], + args = [ + "$(location stateless_lambda_disassembled_golden.txt)", + "$(location stateless_lambda_disassembled.txt)", + ], + data = [ + # Golden file should show a $instance field and *no* get$Lambda() method + "stateless_lambda_disassembled_golden.txt", + ":stateless_lambda_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_stateless_lambda", + srcs = [":testdata_desugared.jar"], + outs = ["stateless_lambda_disassembled.txt"], + cmd = """ +$(JAVABASE)/bin/javap -c -p -cp $< 'com.google.devtools.build.android.desugar.testdata.Lambda$$$$Lambda$$0' > $@ +""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +sh_test( + name = "capture_lambda_has_no_factory_method_test", + srcs = ["diff.sh"], + args = [ + "$(location capture_lambda_disassembled_golden.txt)", + "$(location capture_lambda_disassembled.txt)", + ], + data = [ + # Golden file should show a package-private constructor and *no* get$Lambda() method + "capture_lambda_disassembled_golden.txt", + ":capture_lambda_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_capture_lambda", + srcs = [":testdata_desugared.jar"], + outs = ["capture_lambda_disassembled.txt"], + cmd = """ +$(JAVABASE)/bin/javap -c -p -cp $< 'com.google.devtools.build.android.desugar.testdata.CaptureLambda$$$$Lambda$$0' > $@ +""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +sh_test( + name = "simple_method_reference_has_no_factory_method_test", + srcs = ["diff.sh"], + args = [ + "$(location simple_instance_method_reference_disassembled_golden.txt)", + "$(location simple_instance_method_reference_disassembled.txt)", + ], + data = [ + # Golden file should show a package-private constructor and *no* get$Lambda() method + "simple_instance_method_reference_disassembled_golden.txt", + ":simple_instance_method_reference_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_simple_instance_method_reference", + srcs = [":testdata_desugared.jar"], + outs = ["simple_instance_method_reference_disassembled.txt"], + cmd = """ +$(JAVABASE)/bin/javap -c -p -cp $< 'com.google.devtools.build.android.desugar.testdata.MethodReferenceSuperclass$$$$Lambda$$0' > $@ +""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +sh_test( + name = "interface_has_method_bodies_removed", + srcs = ["diff.sh"], + args = [ + "$(location interface_with_desugared_method_bodies_disassembled_golden.txt)", + "$(location interface_with_desugared_method_bodies_disassembled.txt)", + ], + data = [ + # Golden file should show abstract methods only and *no* static methods + "interface_with_desugared_method_bodies_disassembled_golden.txt", + ":interface_with_desugared_method_bodies_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_interface_with_method_bodies", + srcs = [":testdata_desugared_default_methods.jar"], + outs = ["interface_with_desugared_method_bodies_disassembled.txt"], + # drop jacoco fields and static initializers, which jacoco may add, so the output is stable + # with or without coverage instrumentation + cmd = """ +$(JAVABASE)/bin/javap -p -cp $< 'com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod' | grep -v 'jacoco' | grep -v 'static {}' > $@ +""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +sh_test( + name = "inherited_abstract_method_gets_no_default_method_stub", + srcs = ["diff.sh"], + args = [ + "$(location class_with_inherited_abstract_method_disassembled_golden.txt)", + "$(location class_with_inherited_abstract_method_disassembled.txt)", + ], + data = [ + # Golden file should show *no* methods + "class_with_inherited_abstract_method_disassembled_golden.txt", + ":class_with_inherited_abstract_method_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_class_with_inherited_abstract_method", + srcs = [":testdata_desugared_default_methods.jar"], + outs = ["class_with_inherited_abstract_method_disassembled.txt"], + # drop jacoco fields and static initializers, which jacoco may add, so the output is stable + # with or without coverage instrumentation + cmd = """ +$(JAVABASE)/bin/javap -p -cp $< 'com.google.devtools.build.android.desugar.testdata.java8.Named$$AbstractName' | grep -v 'jacoco' | grep -v 'static {}' > $@ +""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +sh_test( + name = "no_stubs_for_lambdas_in_implemented_interface", + srcs = ["diff.sh"], + args = [ + "$(location class_with_lambdas_in_implemented_interface_disassembled_golden.txt)", + "$(location class_with_lambdas_in_implemented_interface_disassembled.txt)", + ], + data = [ + # Golden file should show *no* lambda$xxx methods (and 4 stubs for default methods) + "class_with_lambdas_in_implemented_interface_disassembled_golden.txt", + ":class_with_lambdas_in_implemented_interface_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_class_with_lambdas_in_implemented_interface", + srcs = [":testdata_desugared_default_methods.jar"], + outs = ["class_with_lambdas_in_implemented_interface_disassembled.txt"], + # drop jacoco fields and static initializers, which jacoco may add, so the output is stable + # with or without coverage instrumentation + cmd = """ +$(JAVABASE)/bin/javap -p -cp $< 'com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod$$Concrete' | grep -v 'jacoco' | grep -v 'static {}' > $@ +""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +sh_test( + name = "desugar_idempotency_test", + srcs = ["diff.sh"], + args = [ + "$(location :capture_lambda_desugared.jar)", + "$(location :capture_lambda_desugared_twice.jar)", + ], + data = [ + ":capture_lambda_desugared.jar", + ":capture_lambda_desugared_twice.jar", + ], + tags = ["no_windows"], +) + +# Smaller Jar used for idempotency test. CaptureLambda can be desugared without +# any need to fix up the resulting lambda class. Running the desugaring tool +# over lambda classes that required fixes somehow perturbs the constant pool +# (but nothing else) so for simplicity we use this Jar for the idempotency test +# we don't run into the perturbance issue. +java_library( + name = "capture_lambda", + srcs = ["testdata/CaptureLambda.java"], +) + +genrule( + name = "desugar_capture_lambda", + srcs = [ + ":capture_lambda", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["capture_lambda_desugared.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "-i $(location :capture_lambda) -o $@ " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +genrule( + name = "desugar_capture_lambda_again", + srcs = [ + ":capture_lambda_desugared.jar", + # Depend on Jacoco runtime in case :testdata was instrumented with + # --collect_code_coverage or by running "blaze coverage" + "//third_party/java/jacoco:blaze-agent", + "//tools/defaults:android_jar", + ], + outs = ["capture_lambda_desugared_twice.jar"], + cmd = "$(location //src/tools/android/java/com/google/devtools/build/android/desugar:Desugar) " + + "-i $(location :capture_lambda_desugared.jar) -o $@ " + + "--classpath_entry $(location //third_party/java/jacoco:blaze-agent) " + + "--bootclasspath_entry $(location //tools/defaults:android_jar)", + tags = ["no_windows"], + tools = ["//src/tools/android/java/com/google/devtools/build/android/desugar:Desugar"], +) + +sh_test( + name = "testdata_sanity_test", + srcs = ["diff.sh"], + args = [ + "$(location :baseclass_lambda_signature.txt)", + "$(location :subclass_lambda_signature.txt)", + ], + data = [ + ":baseclass_lambda_signature.txt", + ":subclass_lambda_signature.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "baseclass_lambda_signature", + srcs = [":testdata"], + outs = [ + "baseclass_disassembled.txt", + "baseclass_lambda_signature.txt", + ], + cmd = """ +$(JAVABASE)/bin/javap -p -cp $< com.google.devtools.build.android.desugar.testdata.OuterReferenceLambda > $(location baseclass_disassembled.txt) +grep lambda $(location baseclass_disassembled.txt) > $(location baseclass_lambda_signature.txt)""", + tags = ["no_windows"], + tools = ["@bazel_tools//tools/jdk"], +) + +genrule( + name = "subclass_lambda_signature", + srcs = [":testdata"], + outs = [ + "subclass_disassembled.txt", + "subclass_lambda_signature.txt", + ], + cmd = """ +$(JAVABASE)/bin/javap -p -cp $< com.google.devtools.build.android.desugar.testdata.LambdaInOverride > $(location subclass_disassembled.txt) +grep lambda $(location subclass_disassembled.txt) > $(location subclass_lambda_signature.txt)""", + tags = ["no_windows"], + tools = [ + "@bazel_tools//tools/jdk", + ], +) + +# b/62623509 regression test. Calls to jacoco-generated method in interface +# need to be rewritten to call the companion class even though the original +# bytecode falsely doesn't indicate that the method is declared in an +# interface. Jacoco-generated field is *not* moved. +sh_test( + name = "jacoco_legacy_default_method_regression_test", + srcs = ["diff.sh"], + args = [ + "$(location :jacoco_legacy_default_method_companion_disassembled_golden.txt)", + "$(location :jacoco_legacy_default_method_companion_disassembled.txt)", + ], + data = [ + "jacoco_legacy_default_method_companion_disassembled_golden.txt", + ":jacoco_legacy_default_method_companion_disassembled.txt", + ], + tags = ["no_windows"], +) + +genrule( + name = "dump_jacoco_legacy_default_method_companion", + srcs = [":jacoco_0_7_5_default_method_desugared.jar"], + outs = ["jacoco_legacy_default_method_companion_disassembled.txt"], + cmd = """$(JAVABASE)/bin/javap -c -p -cp $< 'com/example/gavra/java8coverage/Defaults$$$$CC' > $@""", + tags = ["no_windows"], + tools = [ + "@bazel_tools//tools/jdk", + ], +) + +alias( + name = "guava_base_classpath.jar", + actual = "//third_party:guava/guava-21.0.jar", +) + +java_binary( + name = "generate_synthetic_method_with_lambda_name_convention", + srcs = ["Bug62456849TestDataGenerator.java"], + main_class = "com.google.devtools.build.android.desugar.Bug62456849TestDataGenerator", + deps = [ + "//third_party:asm", + "//third_party:guava", + ], +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/Bug62456849TestDataGenerator.java b/src/test/java/com/google/devtools/build/android/desugar/Bug62456849TestDataGenerator.java new file mode 100644 index 0000000000..627b6c7ccd --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/Bug62456849TestDataGenerator.java @@ -0,0 +1,104 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Iterators; +import com.google.common.collect.UnmodifiableIterator; +import com.google.common.io.ByteStreams; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Test data generator for b/62456849. This class converts methods satisfying the following + * conditions to synthetic methods. + * <li>The name starts with "lambda$" + * <li>Not synthetic + */ +public class Bug62456849TestDataGenerator { + + public static void main(String[] args) throws IOException { + checkArgument( + args.length == 2, + "Usage: %s <input-jar> <output-jar>", + Bug62456849TestDataGenerator.class.getName()); + Path inputJar = Paths.get(args[0]); + checkArgument(Files.isRegularFile(inputJar), "The input jar %s is not a file", inputJar); + Path outputJar = Paths.get(args[1]); + + try (ZipFile inputZip = new ZipFile(inputJar.toFile()); + ZipOutputStream outZip = + new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(outputJar)))) { + for (UnmodifiableIterator<? extends ZipEntry> it = + Iterators.forEnumeration(inputZip.entries()); + it.hasNext(); ) { + ZipEntry entry = it.next(); + String entryName = entry.getName(); + byte[] content = + entryName.endsWith(".class") + ? convertClass(inputZip, entry) + : readEntry(inputZip, entry); + writeToZipFile(outZip, entryName, content); + } + } + } + + private static void writeToZipFile(ZipOutputStream outZip, String entryName, byte[] content) + throws IOException { + ZipEntry result = new ZipEntry(entryName); + result.setTime(0L); + outZip.putNextEntry(result); + outZip.write(content); + outZip.closeEntry(); + } + + private static byte[] readEntry(ZipFile file, ZipEntry entry) throws IOException { + try (InputStream is = file.getInputStream(entry)) { + return ByteStreams.toByteArray(is); + } + } + + private static byte[] convertClass(ZipFile file, ZipEntry entry) throws IOException { + try (InputStream content = file.getInputStream(entry)) { + ClassReader reader = new ClassReader(content); + ClassWriter writer = new ClassWriter(0); + ClassVisitor converter = + new ClassVisitor(Opcodes.ASM5, writer) { + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + if (name.startsWith("lambda$") && (access & Opcodes.ACC_SYNTHETIC) == 0) { + access |= Opcodes.ACC_SYNTHETIC; + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + }; + reader.accept(converter, 0); + return writer.toByteArray(); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java b/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java new file mode 100644 index 0000000000..cdc32639b3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java @@ -0,0 +1,234 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.DefaultMethodClassFixer.InterfaceComparator.INSTANCE; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.Closer; +import com.google.devtools.build.android.desugar.Desugar.ThrowingClassLoader; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.TreeSet; +import java.util.concurrent.Callable; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +/** Unit Test for {@link DefaultMethodClassFixer} */ +@RunWith(JUnit4.class) +public class DefaultMethodClassFixerTest { + + private ClassReaderFactory classpathReader; + private ClassReaderFactory bootclassPath; + private ClassLoader classLoader; + private Closer closer; + + @Before + public void setup() throws IOException { + closer = Closer.create(); + CoreLibraryRewriter rewriter = new CoreLibraryRewriter(""); + + IndexedInputs indexedInputs = + toIndexedInputs(closer, System.getProperty("DefaultMethodClassFixerTest.input")); + IndexedInputs indexedClasspath = + toIndexedInputs(closer, System.getProperty("DefaultMethodClassFixerTest.classpath")); + IndexedInputs indexedBootclasspath = + toIndexedInputs(closer, System.getProperty("DefaultMethodClassFixerTest.bootclasspath")); + + bootclassPath = new ClassReaderFactory(indexedBootclasspath, rewriter); + IndexedInputs indexedClasspathAndInputFiles = indexedClasspath.withParent(indexedInputs); + classpathReader = new ClassReaderFactory(indexedClasspathAndInputFiles, rewriter); + ClassLoader bootclassloader = + new HeaderClassLoader(indexedBootclasspath, rewriter, new ThrowingClassLoader()); + classLoader = new HeaderClassLoader(indexedClasspathAndInputFiles, rewriter, bootclassloader); + } + + @After + public void teardown() throws IOException { + closer.close(); + } + + private static IndexedInputs toIndexedInputs(Closer closer, String stringPathList) + throws IOException { + final List<Path> pathList = readPathListFromString(stringPathList); + return new IndexedInputs(Desugar.toRegisteredInputFileProvider(closer, pathList)); + } + + private static List<Path> readPathListFromString(String pathList) { + return Arrays.stream(checkNotNull(pathList).split(File.pathSeparator)) + .map(Paths::get) + .collect(ImmutableList.toImmutableList()); + } + + private byte[] desugar(String classname) { + ClassReader reader = classpathReader.readIfKnown(classname); + return desugar(reader); + } + + private byte[] desugar(ClassReader reader) { + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); + DefaultMethodClassFixer fixer = + new DefaultMethodClassFixer(writer, classpathReader, bootclassPath, classLoader); + reader.accept(fixer, 0); + return writer.toByteArray(); + } + + private byte[] desugar(byte[] classContent) { + ClassReader reader = new ClassReader(classContent); + return desugar(reader); + } + + @Test + public void testDesugaringDirectImplementation() { + byte[] desugaredClass = + desugar( + ("com.google.devtools.build.android.desugar.testdata.java8." + + "DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetOne$C") + .replace('.', '/')); + checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC(desugaredClass); + + byte[] desugaredClassAgain = desugar(desugaredClass); + checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC( + desugaredClassAgain); + + desugaredClassAgain = desugar(desugaredClassAgain); + checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC( + desugar(desugaredClassAgain)); + } + + private void checkClinitForDefaultInterfaceMethodWithStaticInitializerTestInterfaceSetOneC( + byte[] classContent) { + ClassReader reader = new ClassReader(classContent); + reader.accept( + new ClassVisitor(Opcodes.ASM5) { + + class ClinitMethod extends MethodNode { + + public ClinitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + super(Opcodes.ASM5, access, name, desc, signature, exceptions); + } + } + + private ClinitMethod clinit; + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + if ("<clinit>".equals(name)) { + assertThat(clinit).isNull(); + clinit = new ClinitMethod(access, name, desc, signature, exceptions); + return clinit; + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + @Override + public void visitEnd() { + assertThat(clinit).isNotNull(); + assertThat(clinit.instructions.size()).isEqualTo(3); + AbstractInsnNode instruction = clinit.instructions.getFirst(); + { + assertThat(instruction).isInstanceOf(MethodInsnNode.class); + MethodInsnNode field = (MethodInsnNode) instruction; + assertThat(field.owner) + .isEqualTo( + "com/google/devtools/build/android/desugar/testdata/java8/" + + "DefaultInterfaceMethodWithStaticInitializer" + + "$TestInterfaceSetOne$I1$$CC"); + assertThat(field.name) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME); + assertThat(field.desc) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC); + } + { + instruction = instruction.getNext(); + assertThat(instruction).isInstanceOf(MethodInsnNode.class); + MethodInsnNode field = (MethodInsnNode) instruction; + assertThat(field.owner) + .isEqualTo( + "com/google/devtools/build/android/desugar/testdata/java8/" + + "DefaultInterfaceMethodWithStaticInitializer" + + "$TestInterfaceSetOne$I2$$CC"); + assertThat(field.name) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_NAME); + assertThat(field.desc) + .isEqualTo(InterfaceDesugaring.COMPANION_METHOD_TO_TRIGGER_INTERFACE_CLINIT_DESC); + } + { + instruction = instruction.getNext(); + assertThat(instruction).isInstanceOf(InsnNode.class); + assertThat(instruction.getOpcode()).isEqualTo(Opcodes.RETURN); + } + } + }, + 0); + } + + @Test + public void testInterfaceComparator() { + assertThat(INSTANCE.compare(Runnable.class, Runnable.class)).isEqualTo(0); + assertThat(INSTANCE.compare(Runnable.class, MyRunnable1.class)).isEqualTo(1); + assertThat(INSTANCE.compare(MyRunnable2.class, Runnable.class)).isEqualTo(-1); + assertThat(INSTANCE.compare(MyRunnable3.class, Runnable.class)).isEqualTo(-1); + assertThat(INSTANCE.compare(MyRunnable1.class, MyRunnable3.class)).isEqualTo(1); + assertThat(INSTANCE.compare(MyRunnable3.class, MyRunnable2.class)).isEqualTo(-1); + assertThat(INSTANCE.compare(MyRunnable2.class, MyRunnable1.class)).isGreaterThan(0); + assertThat(INSTANCE.compare(Runnable.class, Serializable.class)).isGreaterThan(0); + assertThat(INSTANCE.compare(Serializable.class, Runnable.class)).isLessThan(0); + + TreeSet<Class<?>> orderedSet = new TreeSet<>(INSTANCE); + orderedSet.add(Serializable.class); + orderedSet.add(Runnable.class); + orderedSet.add(MyRunnable2.class); + orderedSet.add(Callable.class); + orderedSet.add(Serializable.class); + orderedSet.add(MyRunnable1.class); + orderedSet.add(MyRunnable3.class); + assertThat(orderedSet) + .containsExactly( + MyRunnable3.class, // subtype before supertype(s) + MyRunnable1.class, + MyRunnable2.class, + Serializable.class, // java... comes textually after com.google... + Runnable.class, + Callable.class) + .inOrder(); + } + + private static interface MyRunnable1 extends Runnable {} + + private static interface MyRunnable2 extends Runnable {} + + private static interface MyRunnable3 extends MyRunnable1, MyRunnable2 {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarCoreLibraryFunctionalTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarCoreLibraryFunctionalTest.java new file mode 100644 index 0000000000..cebbfc9462 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarCoreLibraryFunctionalTest.java @@ -0,0 +1,34 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test that exercises classes in the {@code testdata} package. This is meant to be run against a + * desugared version of those classes, which in turn exercise various desugaring features. + */ +@RunWith(JUnit4.class) +public class DesugarCoreLibraryFunctionalTest { + + @Test + public void testAutoboxedTypeLambda() { + AutoboxedTypes.Lambda lambdaUse = AutoboxedTypes.autoboxedTypeLambda(1); + assertThat(lambdaUse.charAt("Karen")).isEqualTo("a"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarDefaultMethodsFunctionalTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarDefaultMethodsFunctionalTest.java new file mode 100644 index 0000000000..97d02a48a7 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarDefaultMethodsFunctionalTest.java @@ -0,0 +1,29 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Variant of {@link DesugarJava8FunctionalTest} that expects default and static interface methods + * to be desugared + */ +@RunWith(JUnit4.class) +public final class DesugarDefaultMethodsFunctionalTest extends DesugarJava8FunctionalTest { + + public DesugarDefaultMethodsFunctionalTest() { + super(/*expectBridgesFromSeparateTarget*/ true, /*expectDefaultMethods*/ false); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarFunctionalTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarFunctionalTest.java new file mode 100644 index 0000000000..14e8f37491 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarFunctionalTest.java @@ -0,0 +1,339 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static java.lang.reflect.Modifier.isFinal; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.fail; + +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import com.google.devtools.build.android.desugar.testdata.CaptureLambda; +import com.google.devtools.build.android.desugar.testdata.ConcreteFunction; +import com.google.devtools.build.android.desugar.testdata.ConstructorReference; +import com.google.devtools.build.android.desugar.testdata.GuavaLambda; +import com.google.devtools.build.android.desugar.testdata.InnerClassLambda; +import com.google.devtools.build.android.desugar.testdata.InterfaceWithLambda; +import com.google.devtools.build.android.desugar.testdata.Lambda; +import com.google.devtools.build.android.desugar.testdata.LambdaInOverride; +import com.google.devtools.build.android.desugar.testdata.MethodReference; +import com.google.devtools.build.android.desugar.testdata.MethodReferenceInSubclass; +import com.google.devtools.build.android.desugar.testdata.MethodReferenceSuperclass; +import com.google.devtools.build.android.desugar.testdata.OuterReferenceLambda; +import com.google.devtools.build.android.desugar.testdata.SpecializedFunction; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test that exercises classes in the {@code testdata} package. This is meant to be run against a + * desugared version of those classes, which in turn exercise various desugaring features. + */ +@RunWith(JUnit4.class) +public class DesugarFunctionalTest { + + private final int expectedBridgesFromSameTarget; + private final int expectedBridgesFromSeparateTarget; + private final boolean expectLambdaMethodsInInterfaces; + + public DesugarFunctionalTest() { + this(3, 1, false); + } + + /** Constructor for testing desugar while allowing default and static interface methods. */ + protected DesugarFunctionalTest( + boolean expectBridgesFromSeparateTarget, boolean expectDefaultMethods) { + this( + expectDefaultMethods ? 0 : 3, + expectBridgesFromSeparateTarget ? 1 : 0, + expectDefaultMethods); + } + + private DesugarFunctionalTest(int bridgesFromSameTarget, int bridgesFromSeparateTarget, + boolean lambdaMethodsInInterfaces) { + this.expectedBridgesFromSameTarget = bridgesFromSameTarget; + this.expectedBridgesFromSeparateTarget = bridgesFromSeparateTarget; + this.expectLambdaMethodsInInterfaces = lambdaMethodsInInterfaces; + } + + @Test + public void testGuavaLambda() { + GuavaLambda lambdaUse = new GuavaLambda(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(lambdaUse.as()).containsExactly("Alex"); + } + + @Test + public void testJavaLambda() { + Lambda lambdaUse = new Lambda(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(lambdaUse.as()).containsExactly("Alex"); + } + + @Test + public void testLambdaForIntersectionType() throws Exception { + assertThat(Lambda.hello().call()).isEqualTo("hello"); + } + + @Test + public void testCapturingLambda() { + CaptureLambda lambdaUse = new CaptureLambda(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(lambdaUse.prefixed("L")).containsExactly("Larry"); + } + + @Test + public void testOuterReferenceLambda() throws Exception { + OuterReferenceLambda lambdaUse = new OuterReferenceLambda(ImmutableList.of("Sergey", "Larry")); + assertThat(lambdaUse.filter(ImmutableList.of("Larry", "Alex"))).containsExactly("Larry"); + assertThat( + isFinal( + OuterReferenceLambda.class + .getDeclaredMethod("lambda$filter$0$OuterReferenceLambda", String.class) + .getModifiers())) + .isTrue(); + } + + /** + * Tests a lambda in a subclass whose generated lambda$ method has the same name and signature + * as a lambda$ method generated by Javac in a superclass and both of these methods are used + * in the implementation of the subclass (by calling super). Naively this leads to wrong + * behavior (in this case, return a non-empty list) because the lambda$ in the superclass is never + * used once its made non-private during desugaring. + */ + @Test + public void testOuterReferenceLambdaInOverride() throws Exception { + OuterReferenceLambda lambdaUse = new LambdaInOverride(ImmutableList.of("Sergey", "Larry")); + assertThat(lambdaUse.filter(ImmutableList.of("Larry", "Alex"))).isEmpty(); + assertThat( + isFinal( + LambdaInOverride.class + .getDeclaredMethod("lambda$filter$0$LambdaInOverride", String.class) + .getModifiers())) + .isTrue(); + } + + @Test + public void testLambdaInAnonymousClassReferencesSurroundingMethodParameter() throws Exception { + assertThat(Lambda.mult(21).apply(2).call()).isEqualTo(42); + } + + /** Tests a lambda that accesses a method parameter across 2 nested anonymous classes. */ + @Test + public void testLambdaInNestedAnonymousClass() throws Exception { + InnerClassLambda lambdaUse = new InnerClassLambda(ImmutableList.of("Sergey", "Larry")); + assertThat(lambdaUse.prefixFilter("L").apply(ImmutableList.of("Lois", "Larry")).call()) + .containsExactly("Larry"); + } + + @Test + public void testClassMethodReference() { + MethodReference methodrefUse = new MethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + StringBuilder dest = new StringBuilder(); + methodrefUse.appendAll(dest); + assertThat(dest.toString()).isEqualTo("SergeyLarryAlex"); + } + + // Regression test for b/33378312 + @Test + public void testHiddenMethodReference() { + MethodReference methodrefUse = new MethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(methodrefUse.intersect(ImmutableList.of("Alex", "Sundar"))).containsExactly("Alex"); + } + + // Regression test for b/33378312 + @Test + public void testHiddenStaticMethodReference() { + MethodReference methodrefUse = + new MethodReference(ImmutableList.of("Sergey", "Larry", "Sundar")); + assertThat(methodrefUse.some()).containsExactly("Sergey", "Sundar"); + } + + @Test + public void testDuplicateHiddenMethodReference() { + MethodReference methodrefUse = new MethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(methodrefUse.onlyIn(ImmutableList.of("Alex", "Sundar"))).containsExactly("Sundar"); + } + + // Regression test for b/36201257 + @Test + public void testMethodReferenceThatNeedsBridgeInSubclass() { + MethodReferenceInSubclass methodrefUse = + new MethodReferenceInSubclass(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(methodrefUse.containsE()).containsExactly("Sergey", "Alex"); + assertThat(methodrefUse.startsWithL()).containsExactly("Larry"); + // Test sanity: make sure sub- and superclass have bridge methods with matching descriptors but + // different names + Method superclassBridge = findOnlyBridge(MethodReferenceSuperclass.class); + Method subclassBridge = findOnlyBridge(MethodReferenceInSubclass.class); + assertThat(superclassBridge.getName()).isNotEqualTo(subclassBridge.getName()); + assertThat(superclassBridge.getParameterTypes()).isEqualTo(subclassBridge.getParameterTypes()); + } + + private Method findOnlyBridge(Class<?> clazz) { + Method result = null; + for (Method m : clazz.getDeclaredMethods()) { + if (m.getName().startsWith("bridge$")) { + assertThat(result).named(m.getName()).isNull(); + result = m; + } + } + assertThat(result).named(clazz.getSimpleName()).isNotNull(); + return result; + } + + // Regression test for b/33378312 + @Test + public void testThrowingPrivateMethodReference() throws Exception { + MethodReference methodrefUse = new MethodReference(ImmutableList.of("Sergey", "Larry")); + Callable<?> stringer = methodrefUse.stringer(); + try { + stringer.call(); + fail("IOException expected"); + } catch (IOException expected) { + assertThat(expected).hasMessage("SergeyLarry"); + } catch (Exception e) { + throw e; + } + } + + // Regression test for b/33304582 + @Test + public void testInterfaceMethodReference() { + MethodReference methodrefUse = new MethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + MethodReference.Transformer<String> transform = new MethodReference.Transformer<String>() { + @Override + public String transform(String input) { + return input.substring(1); + } + }; + assertThat(methodrefUse.transform(transform)).containsExactly("ergey", "arry", "lex"); + } + + @Test + public void testConstructorReference() { + ConstructorReference initRefUse = new ConstructorReference(ImmutableList.of("1", "2", "42")); + assertThat(initRefUse.toInt()).containsExactly(1, 2, 42); + } + + // Regression test for b/33304582 + @Test + public void testPrivateConstructorReference() { + ConstructorReference initRefUse = ConstructorReference.singleton().apply("17"); + assertThat(initRefUse.toInt()).containsExactly(17); + } + + // This test is similar to testPrivateConstructorReference but the private constructor of an inner + // class is used as a method reference. That causes Javac to generate a bridge constructor and + // a lambda body method that calls it, so the desugaring step doesn't need to do anything to make + // the private constructor visible. This is mostly to double-check that we don't interfere with + // this "already-working" scenario. + @Test + public void testPrivateConstructorAccessedThroughJavacGeneratedBridge() { + try { + @SuppressWarnings("unused") // local is needed to make ErrorProne happy + ConstructorReference unused = ConstructorReference.emptyThroughJavacGeneratedBridge().get(); + fail("RuntimeException expected"); + } catch (RuntimeException expected) { + assertThat(expected).hasMessage("got it!"); + } + } + + @Test + public void testExpressionMethodReference() { + assertThat( + MethodReference.stringChars(new StringBuilder().append("Larry").append("Sergey")) + .apply(5)) + .isEqualTo('S'); + } + + @Test + public void testFieldMethodReference() { + MethodReference methodrefUse = new MethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(methodrefUse.toPredicate().test("Larry")).isTrue(); + assertThat(methodrefUse.toPredicate().test("Sundar")).isFalse(); + } + + @Test + public void testConcreteFunctionWithInheritedBridgeMethods() { + assertThat(new ConcreteFunction().apply("1234567890987654321")).isEqualTo(1234567890987654321L); + assertThat(ConcreteFunction.parseAll(ImmutableList.of("5", "17"), new ConcreteFunction())) + .containsExactly(5L, 17L); + } + + @Test + public void testLambdaWithInheritedBridgeMethods() throws Exception { + assertThat(ConcreteFunction.toInt().apply("123456789")).isEqualTo(123456789); + assertThat(ConcreteFunction.parseAll(ImmutableList.of("5", "17"), ConcreteFunction.toInt())) + .containsExactly(5, 17); + // Expect String apply(Number) and any expected bridges + assertThat(ConcreteFunction.toInt().getClass().getDeclaredMethods()) + .hasLength(expectedBridgesFromSameTarget + 1); + // Sanity check that we only copied over methods, no fields, from the functional interface + try { + ConcreteFunction.toInt().getClass().getDeclaredField("DO_NOT_COPY_INTO_LAMBDA_CLASSES"); + fail("NoSuchFieldException expected"); + } catch (NoSuchFieldException expected) {} + assertThat(SpecializedFunction.class.getDeclaredField("DO_NOT_COPY_INTO_LAMBDA_CLASSES")) + .isNotNull(); // test sanity + } + + /** Tests lambdas with bridge methods when the implemented interface is in a separate target.*/ + @Test + public void testLambdaWithBridgeMethodsForInterfaceInSeparateTarget() { + assertThat(ConcreteFunction.isInt().test(123456789L)).isTrue(); + assertThat( + ConcreteFunction.doFilter( + ImmutableList.of(123456789L, 1234567890987654321L), + ConcreteFunction.isInt())) + .containsExactly(123456789L); + // Expect test(Number) and any expected bridges + assertThat(ConcreteFunction.isInt().getClass().getDeclaredMethods()) + .hasLength(expectedBridgesFromSeparateTarget + 1); + } + + @Test + public void testLambdaInInterfaceStaticInitializer() { + assertThat(InterfaceWithLambda.DIGITS).containsExactly("0", "1").inOrder(); + // <clinit> doesn't count but if there's a lambda method then Jacoco adds more methods + assertThat(InterfaceWithLambda.class.getDeclaredMethods().length != 0) + .isEqualTo(expectLambdaMethodsInInterfaces); + } + + /** + * Sanity-checks that the resource file included in the original Jar is still there unchanged. + */ + @Test + public void testResourcePreserved() throws Exception { + try (InputStream content = Lambda.class.getResource("testresource.txt").openStream()) { + assertThat(new String(ByteStreams.toByteArray(content), UTF_8)).isEqualTo("test"); + } + } + + /** + * Test for b/62456849. After desugar, the method {@code lambda$mult$0} should still be in the + * class. + */ + @Test + public void testCallMethodWithLambdaNamingConvention() + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Method method = Lambda.class.getDeclaredMethod("lambda$mult$0"); + Object value = method.invoke(null); + assertThat(value).isInstanceOf(Integer.class); + assertThat((Integer) value).isEqualTo(0); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarJava8FunctionalTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarJava8FunctionalTest.java new file mode 100644 index 0000000000..8321d75350 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarJava8FunctionalTest.java @@ -0,0 +1,397 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.desugar.testdata.java8.AnnotationsOfDefaultMethodsShouldBeKept.AnnotatedInterface; +import com.google.devtools.build.android.desugar.testdata.java8.AnnotationsOfDefaultMethodsShouldBeKept.SomeAnnotation; +import com.google.devtools.build.android.desugar.testdata.java8.ConcreteDefaultInterfaceWithLambda; +import com.google.devtools.build.android.desugar.testdata.java8.ConcreteOverridesDefaultWithLambda; +import com.google.devtools.build.android.desugar.testdata.java8.DefaultInterfaceMethodWithStaticInitializer; +import com.google.devtools.build.android.desugar.testdata.java8.DefaultInterfaceWithBridges; +import com.google.devtools.build.android.desugar.testdata.java8.FunctionWithDefaultMethod; +import com.google.devtools.build.android.desugar.testdata.java8.FunctionalInterfaceWithInitializerAndDefaultMethods; +import com.google.devtools.build.android.desugar.testdata.java8.GenericDefaultInterfaceWithLambda; +import com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod; +import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithDefaultMethod; +import com.google.devtools.build.android.desugar.testdata.java8.InterfaceWithDuplicateMethods.ClassWithDuplicateMethods; +import com.google.devtools.build.android.desugar.testdata.java8.Java7InterfaceWithBridges; +import com.google.devtools.build.android.desugar.testdata.java8.Named; +import com.google.devtools.build.android.desugar.testdata.java8.TwoInheritedDefaultMethods; +import com.google.devtools.build.android.desugar.testdata.java8.VisibilityTestClass; +import java.lang.annotation.Annotation; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test that exercises classes in the {@code testdata_java8} package. This is meant to be run + * against a desugared version of those classes, which in turn exercise various desugaring features. + */ +@RunWith(JUnit4.class) +public class DesugarJava8FunctionalTest extends DesugarFunctionalTest { + + public DesugarJava8FunctionalTest() { + this(true, true); + } + + protected DesugarJava8FunctionalTest( + boolean expectBridgesFromSeparateTarget, boolean expectDefaultMethods) { + super(expectBridgesFromSeparateTarget, expectDefaultMethods); + } + + @Test + public void testLambdaInDefaultMethod() { + assertThat(new ConcreteDefaultInterfaceWithLambda().defaultWithLambda()) + .containsExactly("0", "1") + .inOrder(); + } + + @Test + public void testLambdaInDefaultCallsInterfaceMethod() { + assertThat(new ConcreteDefaultInterfaceWithLambda().defaultCallsInterfaceMethod()) + .containsExactly("1", "2") + .inOrder(); + } + + @Test + public void testOverrideLambdaInDefault() { + assertThat(new ConcreteOverridesDefaultWithLambda().defaultWithLambda()) + .containsExactly("2", "3") + .inOrder(); + } + + @Test + public void testLambdaInDefaultCallsOverrideMethod() { + assertThat(new ConcreteOverridesDefaultWithLambda().defaultCallsInterfaceMethod()) + .containsExactly("3", "4") + .inOrder(); + } + + @Test + public void testDefaultInterfaceMethodReference() { + InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); + List<String> dest = + methodrefUse.defaultMethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(dest).containsExactly("Sergey"); + } + + @Test + public void testStaticInterfaceMethodReference() { + InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); + List<String> dest = + methodrefUse.staticMethodReference(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(dest).containsExactly("Alex"); + } + + @Test + public void testLambdaCallsDefaultMethod() { + InterfaceMethod methodrefUse = new InterfaceMethod.Concrete(); + List<String> dest = + methodrefUse.lambdaCallsDefaultMethod(ImmutableList.of("Sergey", "Larry", "Alex")); + assertThat(dest).containsExactly("Sergey"); + } + + @Test + public void testStaticMethodsInInterface_explicitAndLambdaBody() { + List<Long> result = FunctionWithDefaultMethod.DoubleInts.add(ImmutableList.of(7, 39, 8), 3); + assertThat(result).containsExactly(10L, 42L, 11L).inOrder(); + } + + @Test + public void testOverriddenDefaultMethod_inHandwrittenClass() { + FunctionWithDefaultMethod<Integer> doubler = new FunctionWithDefaultMethod.DoubleInts(); + assertThat(doubler.apply(7)).isEqualTo(14); + assertThat(doubler.twice(7)).isEqualTo(35); + } + + @Test + public void testOverriddenDefaultMethod_inHandwrittenSuperclass() { + FunctionWithDefaultMethod<Integer> doubler = new FunctionWithDefaultMethod.DoubleInts2(); + assertThat(doubler.apply(7)).isEqualTo(14); + assertThat(doubler.twice(7)).isEqualTo(35); + } + + @Test + public void testInheritedDefaultMethod_inLambda() { + FunctionWithDefaultMethod<Integer> doubler = + FunctionWithDefaultMethod.DoubleInts.doubleLambda(); + assertThat(doubler.apply(7)).isEqualTo(14); + assertThat(doubler.twice(7)).isEqualTo(28); + } + + @Test + public void testDefaultMethodReference_onLambda() { + FunctionWithDefaultMethod<Integer> plus6 = FunctionWithDefaultMethod.DoubleInts.incTwice(3); + assertThat(plus6.apply(18)).isEqualTo(24); + assertThat(plus6.twice(18)).isEqualTo(30); + } + + @Test + public void testDefaultMethodReference_onHandwrittenClass() { + FunctionWithDefaultMethod<Integer> times5 = FunctionWithDefaultMethod.DoubleInts.times5(); + assertThat(times5.apply(6)).isEqualTo(30); + assertThat(times5.twice(6)).isEqualTo(150); // Irrelevant that DoubleInts overrides twice() + } + + @Test + public void testStaticInterfaceMethodReferenceReturned() { + Function<Integer, FunctionWithDefaultMethod<Integer>> factory = + FunctionWithDefaultMethod.DoubleInts.incFactory(); + assertThat(factory.apply(6).apply(7)).isEqualTo(13); + assertThat(factory.apply(6).twice(7)).isEqualTo(19); + } + + @Test + public void testSuperDefaultMethodInvocation() { + assertThat(new TwoInheritedDefaultMethods().name()).isEqualTo("One:Two"); + assertThat(new Named.DefaultName().name()).isEqualTo("DefaultName-once"); + assertThat(new Named.DefaultNameSubclass().name()).isEqualTo("DefaultNameSubclass-once-twice"); + } + + @Test + public void testInheritedPreferredOverDefault() throws Exception { + assertThat(new Named.ExplicitName("hello").name()).isEqualTo("hello"); + // Make sure AbstractName remains abstract, despite default method from implemented interface + assertThat(Modifier.isAbstract(Named.AbstractName.class.getMethod("name").getModifiers())) + .isTrue(); + } + + @Test + public void testRedefinedDefaultMethod() throws Exception { + assertThat(new InterfaceWithDefaultMethod.Version2().version()).isEqualTo(2); + } + + @Test + public void testDefaultMethodRedefinedInSubclass() throws Exception { + assertThat(new InterfaceWithDefaultMethod.AlsoVersion2().version()).isEqualTo(2); + } + + @Test + public void testDefaultMethodVisibility() { + assertThat(new VisibilityTestClass().m()).isEqualTo(42); + } + + /** Test for b/38302860 */ + @Test + public void testAnnotationsOfDefaultMethodsAreKept() throws Exception { + { + Annotation[] annotations = AnnotatedInterface.class.getAnnotations(); + assertThat(annotations).hasLength(1); + assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); + assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(1); + } + { + Annotation[] annotations = + AnnotatedInterface.class.getMethod("annotatedAbstractMethod").getAnnotations(); + assertThat(annotations).hasLength(1); + assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); + assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(2); + } + { + Annotation[] annotations = + AnnotatedInterface.class.getMethod("annotatedDefaultMethod").getAnnotations(); + assertThat(annotations).hasLength(1); + assertThat(annotations[0]).isInstanceOf(SomeAnnotation.class); + assertThat(((SomeAnnotation) annotations[0]).value()).isEqualTo(3); + } + } + /** Test for b/38308515 */ + @Test + public void testDefaultAndStaticMethodNameClash() { + final ClassWithDuplicateMethods instance = new ClassWithDuplicateMethods(); + assertThat(instance.getZero()).isEqualTo(0); + assertThat(instance.getZeroFromStaticInterfaceMethod()).isEqualTo(1); + } + + /** + * Test for b/38257037 + * + * <p>Note that, we intentionally suppress unchecked warnings, because we expect some + * ClassCastException to test bridge methods. + */ + @SuppressWarnings("unchecked") + @Test + public void testBridgeAndDefaultMethods() { + { + DefaultInterfaceWithBridges object = new DefaultInterfaceWithBridges(); + Integer one = 1; + assertThat(object.copy(one)).isEqualTo(one); + assertThat(object.copy((Number) one)).isEqualTo(one); + assertThrows(ClassCastException.class, () -> object.copy(Double.valueOf(1))); + + assertThat(object.getNumber()).isInstanceOf(Double.class); + assertThat(object.getNumber()).isEqualTo(Double.valueOf(2.3d)); + assertThat(object.getDouble()).isEqualTo(Double.valueOf(2.3d)); + } + { + Java7InterfaceWithBridges.ClassAddTwo testObject = + new Java7InterfaceWithBridges.ClassAddTwo(); + assertThat(testObject.add(Integer.valueOf(2))).isEqualTo(4); + + @SuppressWarnings("rawtypes") + Java7InterfaceWithBridges top = testObject; + assertThat(top.add(Integer.valueOf(2))).isEqualTo(4); + assertThrows(ClassCastException.class, () -> top.add(new Object())); + assertThrows(ClassCastException.class, () -> top.add(Double.valueOf(1))); + assertThrows(ClassCastException.class, () -> top.add(Long.valueOf(1))); + + @SuppressWarnings("rawtypes") + Java7InterfaceWithBridges.LevelOne levelOne = testObject; + assertThat(levelOne.add(Integer.valueOf(2))).isEqualTo(4); + assertThrows(ClassCastException.class, () -> top.add(new Object())); + assertThrows(ClassCastException.class, () -> top.add(Double.valueOf(1))); + assertThrows(ClassCastException.class, () -> top.add(Long.valueOf(1))); + + @SuppressWarnings("rawtypes") + Java7InterfaceWithBridges.LevelOne levelTwo = testObject; + assertThat(levelTwo.add(Integer.valueOf(2))).isEqualTo(4); + assertThrows(ClassCastException.class, () -> levelTwo.add(Double.valueOf(1))); + assertThrows(ClassCastException.class, () -> levelTwo.add(Long.valueOf(1))); + } + { + GenericDefaultInterfaceWithLambda.ClassTwo testObject = + new GenericDefaultInterfaceWithLambda.ClassTwo(); + + assertThat(testObject.increment(Integer.valueOf(0))).isEqualTo(1); + assertThat(testObject.toString(Integer.valueOf(0))).isEqualTo("0"); + assertThat(testObject.getBaseValue()).isEqualTo(Integer.valueOf(0)); + + assertThat(testObject.toList(0)).isEmpty(); + assertThat(testObject.toList(1)).containsExactly(0).inOrder(); + assertThat(testObject.toList(2)).containsExactly(0, 1).inOrder(); + + assertThat(((Function<Integer, ArrayList<Integer>>) testObject.toListSupplier()).apply(0)) + .isEmpty(); + assertThat(((Function<Integer, ArrayList<Integer>>) testObject.toListSupplier()).apply(1)) + .containsExactly(0) + .inOrder(); + assertThat(((Function<Integer, ArrayList<Integer>>) testObject.toListSupplier()).apply(2)) + .containsExactly(0, 1) + .inOrder(); + + assertThat(testObject.convertToStringList(ImmutableList.of(0))) + .containsExactly("0") + .inOrder(); + assertThat(testObject.convertToStringList(ImmutableList.of(0, 1))) + .containsExactly("0", "1") + .inOrder(); + + @SuppressWarnings("rawtypes") + GenericDefaultInterfaceWithLambda top = testObject; + assertThrows(ClassCastException.class, () -> top.increment(Long.valueOf(1))); + assertThrows(ClassCastException.class, () -> top.toString(Long.valueOf(1))); + assertThat(top.increment(Integer.valueOf(0))).isEqualTo(1); + assertThat(top.toString(Integer.valueOf(0))).isEqualTo("0"); + assertThat(top.getBaseValue()).isEqualTo(Integer.valueOf(0)); + + assertThat(top.toList(0)).isEmpty(); + assertThat(top.toList(1)).containsExactly(0).inOrder(); + assertThat(top.toList(2)).containsExactly(0, 1).inOrder(); + + assertThat(((Function<Integer, ArrayList<Integer>>) top.toListSupplier()).apply(0)).isEmpty(); + assertThat(((Function<Integer, ArrayList<Integer>>) top.toListSupplier()).apply(1)) + .containsExactly(0) + .inOrder(); + assertThat(((Function<Integer, ArrayList<Integer>>) top.toListSupplier()).apply(2)) + .containsExactly(0, 1) + .inOrder(); + + assertThat(top.convertToStringList(ImmutableList.of(0))).containsExactly("0").inOrder(); + assertThat(top.convertToStringList(ImmutableList.of(0, 1))) + .containsExactly("0", "1") + .inOrder(); + } + { + @SuppressWarnings("rawtypes") + GenericDefaultInterfaceWithLambda testObject = + new GenericDefaultInterfaceWithLambda.ClassThree(); + assertThat(testObject.getBaseValue()).isEqualTo(Long.valueOf(0)); + assertThat(testObject.increment(Long.valueOf(0))).isEqualTo(Long.valueOf(0 + 1)); + assertThat(testObject.toString(Long.valueOf(0))).isEqualTo(Long.valueOf(0).toString()); + assertThrows(ClassCastException.class, () -> testObject.increment(Integer.valueOf(0))); + assertThrows(ClassCastException.class, () -> testObject.toString(Integer.valueOf(0))); + assertThat(testObject.toList(2)).containsExactly(Long.valueOf(0), Long.valueOf(1)).inOrder(); + assertThat(testObject.convertToStringList(testObject.toList(1))).containsExactly("0"); + assertThat(((Function<Integer, ArrayList<Long>>) testObject.toListSupplier()).apply(2)) + .containsExactly(Long.valueOf(0), Long.valueOf(1)); + } + } + + /** + * Test for b/62047432. + * + * <p>When desugaring a functional interface with an executable clinit and default methods, we + * erase the body of clinit to avoid executing it during desugaring. This test makes sure that all + * the constants defined in the interface are still there after desugaring. + */ + @Test + public void testFunctionalInterfaceWithExecutableClinitStillWorkAfterDesugar() { + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CONSTANT.length("").convert()) + .isEqualTo(0); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CONSTANT.length("1").convert()) + .isEqualTo(1); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.BOOLEAN).isFalse(); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.CHAR).isEqualTo('h'); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.BYTE).isEqualTo(0); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.SHORT).isEqualTo(0); + + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.INT).isEqualTo(0); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.FLOAT).isEqualTo(0f); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.LONG).isEqualTo(0); + assertThat(FunctionalInterfaceWithInitializerAndDefaultMethods.DOUBLE).isEqualTo(0d); + } + + /** Test for b/38255926. */ + @Test + public void testDefaultMethodInitializationOrder() { + { + assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne.C().sum()) + .isEqualTo(11); // To trigger loading the class C and its super interfaces. + assertThat( + DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne + .getRealInitializationOrder()) + .isEqualTo( + DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetOne + .getExpectedInitializationOrder()); + } + { + assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo.C().sum()) + .isEqualTo(3); + assertThat( + DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo + .getRealInitializationOrder()) + .isEqualTo( + DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetTwo + .getExpectedInitializationOrder()); + } + { + assertThat(new DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree.C().sum()) + .isEqualTo(11); + assertThat( + DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree + .getRealInitializationOrder()) + .isEqualTo( + DefaultInterfaceMethodWithStaticInitializer.TestInterfaceSetThree + .getExpectedInitializationOrder()); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarJava8LikeAndroidStudioFunctionalTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarJava8LikeAndroidStudioFunctionalTest.java new file mode 100644 index 0000000000..13edc57980 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarJava8LikeAndroidStudioFunctionalTest.java @@ -0,0 +1,31 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Variant of {@link DesugarJava8FunctionalTest} that doesn't expect any bridge methods already + * present on functional interfaces to be also present on generated classes, even where functional + * interfaces are defined in other compilations, which requires compiling against regular jar files + * instead of a classpath of -hjars. + */ +@RunWith(JUnit4.class) +public final class DesugarJava8LikeAndroidStudioFunctionalTest extends DesugarJava8FunctionalTest { + + public DesugarJava8LikeAndroidStudioFunctionalTest() { + super(/*expectBridgesFromSeparateTarget*/ false, /*expectDefaultMethods*/ true); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarLongCompareTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarLongCompareTest.java new file mode 100644 index 0000000000..708fc8e666 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarLongCompareTest.java @@ -0,0 +1,135 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; + +import com.google.devtools.build.android.desugar.testdata.ClassCallingLongCompare; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** The test case for the rewriter rewriting a call of Long.compare(long, long) to lcmp. */ +@RunWith(JUnit4.class) +public class DesugarLongCompareTest { + + @Test + public void testClassCallingLongCompareHasNoReferenceToLong_compare() { + try { + ClassReader reader = new ClassReader(ClassCallingLongCompare.class.getName()); + + AtomicInteger counter = new AtomicInteger(0); + + reader.accept( + new ClassVisitor(Opcodes.ASM5) { + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + return new MethodVisitor(api) { + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == INVOKESTATIC + && owner.equals("java/lang/Long") + && name.equals("compare") + && desc.equals("(JJ)I")) { + counter.incrementAndGet(); + } + } + }; + } + }, + 0); + assertThat(counter.get()).isEqualTo(0); + } catch (IOException e) { + fail(); + } + } + + @Test + public void testCompareLongWithLambda() { + assertThat(ClassCallingLongCompare.compareLongWithLambda(1, 0)).isEqualTo(1); + assertThat(ClassCallingLongCompare.compareLongWithLambda(1, 1)).isEqualTo(0); + assertThat(ClassCallingLongCompare.compareLongWithLambda(1, 2)).isEqualTo(-1); + assertThat(ClassCallingLongCompare.compareLongWithLambda(Long.MAX_VALUE, Long.MIN_VALUE)) + .isEqualTo(1); + assertThat(ClassCallingLongCompare.compareLongWithLambda(Long.MAX_VALUE, Long.MAX_VALUE)) + .isEqualTo(0); + assertThat(ClassCallingLongCompare.compareLongWithLambda(Long.MIN_VALUE, Long.MAX_VALUE)) + .isEqualTo(-1); + } + + @Test + public void testCompareLongWithMethodReference() { + assertThat(ClassCallingLongCompare.compareLongWithMethodReference(1, 0)).isEqualTo(1); + assertThat(ClassCallingLongCompare.compareLongWithMethodReference(1, 1)).isEqualTo(0); + assertThat(ClassCallingLongCompare.compareLongWithMethodReference(1, 2)).isEqualTo(-1); + assertThat( + ClassCallingLongCompare.compareLongWithMethodReference(Long.MAX_VALUE, Long.MIN_VALUE)) + .isEqualTo(1); + assertThat( + ClassCallingLongCompare.compareLongWithMethodReference(Long.MAX_VALUE, Long.MAX_VALUE)) + .isEqualTo(0); + assertThat( + ClassCallingLongCompare.compareLongWithMethodReference(Long.MIN_VALUE, Long.MAX_VALUE)) + .isEqualTo(-1); + } + + @Test + public void testcompareLongByCallingLong_compare() { + assertThat(ClassCallingLongCompare.compareLongByCallingLong_compare(1, 0)).isEqualTo(1); + assertThat(ClassCallingLongCompare.compareLongByCallingLong_compare(1, 1)).isEqualTo(0); + assertThat(ClassCallingLongCompare.compareLongByCallingLong_compare(1, 2)).isEqualTo(-1); + assertThat( + ClassCallingLongCompare.compareLongByCallingLong_compare( + Long.MAX_VALUE, Long.MIN_VALUE)) + .isEqualTo(1); + assertThat( + ClassCallingLongCompare.compareLongByCallingLong_compare( + Long.MAX_VALUE, Long.MAX_VALUE)) + .isEqualTo(0); + assertThat( + ClassCallingLongCompare.compareLongByCallingLong_compare( + Long.MIN_VALUE, Long.MAX_VALUE)) + .isEqualTo(-1); + } + + @Test + public void testcompareLongByCallingLong_compare2() { + assertThat(ClassCallingLongCompare.compareLongByCallingLong_compare2(1, 0)).isEqualTo("g"); + assertThat(ClassCallingLongCompare.compareLongByCallingLong_compare2(1, 1)).isEqualTo("e"); + assertThat(ClassCallingLongCompare.compareLongByCallingLong_compare2(0, 1)).isEqualTo("l"); + + assertThat( + ClassCallingLongCompare.compareLongByCallingLong_compare2( + Long.MAX_VALUE, Long.MIN_VALUE)) + .isEqualTo("g"); + assertThat( + ClassCallingLongCompare.compareLongByCallingLong_compare2( + Long.MAX_VALUE, Long.MAX_VALUE)) + .isEqualTo("e"); + assertThat( + ClassCallingLongCompare.compareLongByCallingLong_compare2( + Long.MIN_VALUE, Long.MAX_VALUE)) + .isEqualTo("l"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarMainClassTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarMainClassTest.java new file mode 100644 index 0000000000..039201282e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarMainClassTest.java @@ -0,0 +1,82 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.LambdaClassMaker.LAMBDA_METAFACTORY_DUMPER_PROPERTY; +import static org.junit.Assert.fail; + +import com.google.common.base.Strings; +import java.io.IOError; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.function.Supplier; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link Desugar} */ +@RunWith(JUnit4.class) +public class DesugarMainClassTest { + + @Test + public void testVerifyLambdaDumpDirectoryRegistration() throws Exception { + if (Strings.isNullOrEmpty(System.getProperty(LAMBDA_METAFACTORY_DUMPER_PROPERTY))) { + testLambdaDumpDirSpecifiedInProgramFail(); + } else { + testLambdaDumpDirPassSpecifiedInCmdPass(); + } + } + + private void testLambdaDumpDirSpecifiedInProgramFail() throws Exception { + // This lambda will fail the dump directory registration, which is intended. + Supplier<Path> supplier = + () -> { + Path path = Paths.get(".").toAbsolutePath(); + System.setProperty(LAMBDA_METAFACTORY_DUMPER_PROPERTY, path.toString()); + return path; + }; + try { + Desugar.verifyLambdaDumpDirectoryRegistered(supplier.get()); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + + /** + * Test the LambdaMetafactory can be correctly set up by specifying the system property {@code + * LAMBDA_METAFACTORY_DUMPER_PROPERTY} in the command line. + */ + private void testLambdaDumpDirPassSpecifiedInCmdPass() throws IOException { + // The following lambda ensures that the LambdaMetafactory is loaded at the beggining of this + // test, so that the dump directory can be registered. + Supplier<Path> supplier = + () -> { + try { + return Desugar.createAndRegisterLambdaDumpDirectory(); + } catch (IOException e) { + throw new IOError(e); + } + }; + Path dumpDirectory = supplier.get(); + assertThat(dumpDirectory.toAbsolutePath().toString()) + .isEqualTo( + Paths.get(System.getProperty(LAMBDA_METAFACTORY_DUMPER_PROPERTY)) + .toAbsolutePath() + .toString()); + Desugar.verifyLambdaDumpDirectoryRegistered(dumpDirectory); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarObjectsRequireNonNullTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarObjectsRequireNonNullTest.java new file mode 100644 index 0000000000..6f3be3c0aa --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarObjectsRequireNonNullTest.java @@ -0,0 +1,153 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; + +import com.google.devtools.build.android.desugar.testdata.ClassCallingRequireNonNull; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * This test case tests the desugaring feature for Objects.requireNonNull. This feature replaces any + * call to this method with o.getClass() to check whether 'o' is null. + */ +@RunWith(JUnit4.class) +public class DesugarObjectsRequireNonNullTest { + + @Test + public void testClassCallingRequireNonNullHasNoReferenceToRequiresNonNull() { + try { + ClassReader reader = new ClassReader(ClassCallingRequireNonNull.class.getName()); + + AtomicInteger counterForSingleArgument = new AtomicInteger(0); + AtomicInteger counterForString = new AtomicInteger(0); + AtomicInteger counterForSupplier = new AtomicInteger(0); + + reader.accept( + new ClassVisitor(Opcodes.ASM5) { + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + return new MethodVisitor(api) { + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == INVOKESTATIC + && owner.equals("java/util/Objects") + && name.equals("requireNonNull")) { + switch (desc) { + case "(Ljava/lang/Object;)Ljava/lang/Object;": + counterForSingleArgument.incrementAndGet(); + break; + case "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;": + counterForString.incrementAndGet(); + break; + case "(Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;": + counterForSupplier.incrementAndGet(); + break; + default: + fail("Unknown overloaded requireNonNull is found: " + desc); + } + } + } + }; + } + }, + 0); + assertThat(counterForSingleArgument.get()).isEqualTo(0); + // we do not desugar requireNonNull(Object, String) or requireNonNull(Object, Supplier) + assertThat(counterForString.get()).isEqualTo(1); + assertThat(counterForSupplier.get()).isEqualTo(1); + } catch (IOException e) { + fail(); + } + } + + @Test + public void testInliningImplicitCallToObjectsRequireNonNull() { + try { + ClassCallingRequireNonNull.getStringLengthWithMethodReference(null); + fail ("NullPointerException expected"); + } catch (NullPointerException e) { + // Expected + } + + assertThat(ClassCallingRequireNonNull.getStringLengthWithMethodReference("")).isEqualTo(0); + assertThat(ClassCallingRequireNonNull.getStringLengthWithMethodReference("1")).isEqualTo(1); + + try { + ClassCallingRequireNonNull.getStringLengthWithLambdaAndExplicitCallToRequireNonNull(null); + fail ("NullPointerException expected"); + } catch (NullPointerException e) { + // Expected + } + + assertThat( + ClassCallingRequireNonNull.getStringLengthWithLambdaAndExplicitCallToRequireNonNull("")) + .isEqualTo(0); + assertThat( + ClassCallingRequireNonNull.getStringLengthWithLambdaAndExplicitCallToRequireNonNull( + "1")) + .isEqualTo(1); + } + + @Test + public void testInliningExplicitCallToObjectsRequireNonNull() { + try { + ClassCallingRequireNonNull.getFirstCharVersionOne(null); + fail ("NullPointerException expected"); + } catch (NullPointerException e) { + // Expected + } + + try { + ClassCallingRequireNonNull.getFirstCharVersionTwo(null); + fail ("NullPointerException expected"); + } catch (NullPointerException e) { + // Expected + } + + try { + ClassCallingRequireNonNull.callRequireNonNullWithArgumentString(null); + fail ("NullPointerException expected"); + } catch (NullPointerException e) { + // Expected + } + + try { + ClassCallingRequireNonNull.callRequireNonNullWithArgumentSupplier(null); + fail ("NullPointerException expected"); + } catch (NullPointerException e) { + // Expected + } + + assertThat(ClassCallingRequireNonNull.getFirstCharVersionOne("hello")).isEqualTo('h'); + assertThat(ClassCallingRequireNonNull.getFirstCharVersionTwo("hello")).isEqualTo('h'); + + assertThat(ClassCallingRequireNonNull.callRequireNonNullWithArgumentString("hello")) + .isEqualTo('h'); + assertThat(ClassCallingRequireNonNull.callRequireNonNullWithArgumentSupplier("hello")) + .isEqualTo('h'); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/DesugarTryWithResourcesFunctionalTest.java b/src/test/java/com/google/devtools/build/android/desugar/DesugarTryWithResourcesFunctionalTest.java new file mode 100644 index 0000000000..38df9e33a4 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/DesugarTryWithResourcesFunctionalTest.java @@ -0,0 +1,99 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.getStrategyClassName; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.getTwrStrategyClassNameSpecifiedInSystemProperty; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isMimicStrategy; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isNullStrategy; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isReuseStrategy; +import static org.junit.Assert.fail; + +import com.google.devtools.build.android.desugar.runtime.ThrowableExtension; +import com.google.devtools.build.android.desugar.testdata.ClassUsingTryWithResources; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** The functional test for desugaring try-with-resources. */ +@RunWith(JUnit4.class) +public class DesugarTryWithResourcesFunctionalTest { + + @Test + public void testCheckSuppressedExceptionsReturningEmptySuppressedExceptions() { + Throwable[] suppressed = ClassUsingTryWithResources.checkSuppressedExceptions(false); + assertThat(suppressed).isEmpty(); + } + + @Test + public void testPrintStackTraceOfCaughtException() { + String trace = ClassUsingTryWithResources.printStackTraceOfCaughtException(); + if (isMimicStrategy()) { + assertThat(trace.toLowerCase()).contains("suppressed"); + } else if (isReuseStrategy()) { + assertThat(trace.toLowerCase()).contains("suppressed"); + } else if (isNullStrategy()) { + assertThat(trace.toLowerCase()).doesNotContain("suppressed"); + } else { + fail("unexpected desugaring strategy " + ThrowableExtension.getStrategy()); + } + } + + @Test + public void testCheckSuppressedExceptionReturningOneSuppressedException() { + Throwable[] suppressed = ClassUsingTryWithResources.checkSuppressedExceptions(true); + + if (isMimicStrategy()) { + assertThat(suppressed).hasLength(1); + } else if (isReuseStrategy()) { + assertThat(suppressed).hasLength(1); + } else if (isNullStrategy()) { + assertThat(suppressed).isEmpty(); + } else { + fail("unexpected desugaring strategy " + ThrowableExtension.getStrategy()); + } + } + + @Test + public void testSimpleTryWithResources() { + + try { + ClassUsingTryWithResources.simpleTryWithResources(); + fail("Expected RuntimeException"); + } catch (Exception expected) { + assertThat(expected.getClass()).isEqualTo(RuntimeException.class); + + String expectedStrategyName = getTwrStrategyClassNameSpecifiedInSystemProperty(); + assertThat(getStrategyClassName()).isEqualTo(expectedStrategyName); + if (isMimicStrategy()) { + assertThat(expected.getSuppressed()).isEmpty(); + assertThat(ThrowableExtension.getSuppressed(expected)).hasLength(1); + assertThat(ThrowableExtension.getSuppressed(expected)[0].getClass()) + .isEqualTo(IOException.class); + } else if (isReuseStrategy()) { + assertThat(expected.getSuppressed()).hasLength(1); + assertThat(expected.getSuppressed()[0].getClass()).isEqualTo(IOException.class); + assertThat(ThrowableExtension.getSuppressed(expected)[0].getClass()) + .isEqualTo(IOException.class); + } else if (isNullStrategy()) { + assertThat(expected.getSuppressed()).isEmpty(); + assertThat(ThrowableExtension.getSuppressed(expected)).isEmpty(); + } else { + fail("unexpected desugaring strategy " + getStrategyClassName()); + } + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/FieldInfoTest.java b/src/test/java/com/google/devtools/build/android/desugar/FieldInfoTest.java new file mode 100644 index 0000000000..afb2beacbd --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/FieldInfoTest.java @@ -0,0 +1,33 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link FieldInfo} */ +@RunWith(JUnit4.class) +public class FieldInfoTest { + + @Test + public void testFieldsAreCorrectlySet() { + FieldInfo info = FieldInfo.create("owner", "name", "desc"); + assertThat(info.owner()).isEqualTo("owner"); + assertThat(info.name()).isEqualTo("name"); + assertThat(info.desc()).isEqualTo("desc"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/IndexedInputsTest.java b/src/test/java/com/google/devtools/build/android/desugar/IndexedInputsTest.java new file mode 100644 index 0000000000..bac3fc92cd --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/IndexedInputsTest.java @@ -0,0 +1,136 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import java.io.File; +import java.io.FileOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test that exercises the behavior of the IndexedInputs class. + */ +@RunWith(JUnit4.class) +public final class IndexedInputsTest { + + private static File lib1; + + private static String lib1Name; + + private static File lib2; + + private static String lib2Name; + + private static InputFileProvider lib1InputFileProvider; + + private static InputFileProvider lib2InputFileProvider; + + @BeforeClass + public static void setUpClass() throws Exception { + lib1 = File.createTempFile("lib1", ".jar"); + lib1Name = lib1.getName(); + try (ZipOutputStream zos1 = new ZipOutputStream(new FileOutputStream(lib1))) { + zos1.putNextEntry(new ZipEntry("a/b/C.class")); + zos1.putNextEntry(new ZipEntry("a/b/D.class")); + zos1.closeEntry(); + } + + lib2 = File.createTempFile("lib2", ".jar"); + lib2Name = lib2.getName(); + try (ZipOutputStream zos2 = new ZipOutputStream(new FileOutputStream(lib2))) { + zos2.putNextEntry(new ZipEntry("a/b/C.class")); + zos2.putNextEntry(new ZipEntry("a/b/E.class")); + zos2.closeEntry(); + } + } + + @Before + public void createProviders() throws Exception { + lib1InputFileProvider = new ZipInputFileProvider(lib1.toPath()); + lib2InputFileProvider = new ZipInputFileProvider(lib2.toPath()); + } + + @After + public void closeProviders() throws Exception { + lib1InputFileProvider.close(); + lib2InputFileProvider.close(); + } + + @AfterClass + public static void tearDownClass() throws Exception { + lib1.delete(); + lib2.delete(); + } + + @Test + public void testClassFoundWithParentLibrary() throws Exception { + IndexedInputs indexedLib2 = new IndexedInputs(ImmutableList.of(lib2InputFileProvider)); + IndexedInputs indexedLib1 = new IndexedInputs(ImmutableList.of(lib1InputFileProvider)); + IndexedInputs indexedLib2AndLib1 = indexedLib1.withParent(indexedLib2); + assertThat(indexedLib2AndLib1.getInputFileProvider("a/b/C.class").toString()) + .isEqualTo(lib2Name); + assertThat(indexedLib2AndLib1.getInputFileProvider("a/b/D.class").toString()) + .isEqualTo(lib1Name); + assertThat(indexedLib2AndLib1.getInputFileProvider("a/b/E.class").toString()) + .isEqualTo(lib2Name); + + indexedLib2 = new IndexedInputs(ImmutableList.of(lib2InputFileProvider)); + indexedLib1 = new IndexedInputs(ImmutableList.of(lib1InputFileProvider)); + IndexedInputs indexedLib1AndLib2 = indexedLib2.withParent(indexedLib1); + assertThat(indexedLib1AndLib2.getInputFileProvider("a/b/C.class").toString()) + .isEqualTo(lib1Name); + assertThat(indexedLib1AndLib2.getInputFileProvider("a/b/D.class").toString()) + .isEqualTo(lib1Name); + assertThat(indexedLib1AndLib2.getInputFileProvider("a/b/E.class").toString()) + .isEqualTo(lib2Name); + } + + @Test + public void testClassFoundWithoutParentLibrary() throws Exception { + IndexedInputs ijLib1Lib2 = + new IndexedInputs(ImmutableList.of(lib1InputFileProvider, lib2InputFileProvider)); + assertThat(ijLib1Lib2.getInputFileProvider("a/b/C.class").toString()) + .isEqualTo(lib1Name); + assertThat(ijLib1Lib2.getInputFileProvider("a/b/D.class").toString()) + .isEqualTo(lib1Name); + assertThat(ijLib1Lib2.getInputFileProvider("a/b/E.class").toString()) + .isEqualTo(lib2Name); + + IndexedInputs ijLib2Lib1 = + new IndexedInputs(ImmutableList.of(lib2InputFileProvider, lib1InputFileProvider)); + assertThat(ijLib2Lib1.getInputFileProvider("a/b/C.class").toString()) + .isEqualTo(lib2Name); + assertThat(ijLib2Lib1.getInputFileProvider("a/b/D.class").toString()) + .isEqualTo(lib1Name); + assertThat(ijLib2Lib1.getInputFileProvider("a/b/E.class").toString()) + .isEqualTo(lib2Name); + } + + @Test + public void testClassNotFound() throws Exception { + IndexedInputs ijLib1Lib2 = + new IndexedInputs(ImmutableList.of(lib1InputFileProvider, lib2InputFileProvider)); + assertThat(ijLib1Lib2.getInputFileProvider("a/b/F.class")).isNull(); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java b/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java new file mode 100644 index 0000000000..b8c8b542e7 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java @@ -0,0 +1,122 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +@RunWith(JUnit4.class) +public class Java7CompatibilityTest { + + @Test + public void testJava7CompatibleInterface() throws Exception { + ClassReader reader = new ClassReader(ExtendsDefault.class.getName()); + ClassTester tester = new ClassTester(); + reader.accept(new Java7Compatibility(tester, null), 0); + assertThat(tester.version).isEqualTo(Opcodes.V1_7); + assertThat(tester.bridgeMethods).isEqualTo(0); // make sure we strip bridge methods + assertThat(tester.clinitMethods).isEqualTo(1); // make sure we don't strip <clinit> + } + + @Test + public void testDefaultMethodFails() throws Exception { + ClassReader reader = new ClassReader(WithDefault.class.getName()); + try { + reader.accept(new Java7Compatibility(null, null), 0); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().contains("getVersion()I"); + } + } + + /** + * Tests that a class implementing interfaces with bridge methods redeclares those bridges. + * This is behavior of javac that we rely on. + */ + @Test + public void testConcreteClassRedeclaresBridges() throws Exception { + ClassReader reader = new ClassReader(Impl.class.getName()); + ClassTester tester = new ClassTester(); + reader.accept(new Java7Compatibility(tester, null), 0); + assertThat(tester.version).isEqualTo(Opcodes.V1_7); + assertThat(tester.bridgeMethods).isEqualTo(2); + } + + private static class ClassTester extends ClassVisitor { + + int version; + int bridgeMethods; + int clinitMethods; + + private ClassTester() { + super(Opcodes.ASM5, null); + } + + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + this.version = version; + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + if (BitFlags.isSet(access, Opcodes.ACC_BRIDGE)) { + ++bridgeMethods; + } + if ("<clinit>".equals(name)) { + ++clinitMethods; + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + } + + interface WithDefault<T> { + default int getVersion() { + return 18; + } + T get(); + } + + // Javac will generate a default bridge method "Object get()" that Java7Compatibility will remove + interface ExtendsDefault<T extends Number> extends WithDefault<T> { + public static final Integer X = Integer.valueOf(37); + String name(); + @Override T get(); + } + + // Javac will generate 2 bridge methods that we *don't* want to remove + static class Impl implements ExtendsDefault<Integer> { + @Override public Integer get() { + return X; + } + @Override public String name() { + return "test"; + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/MethodInfoTest.java b/src/test/java/com/google/devtools/build/android/desugar/MethodInfoTest.java new file mode 100644 index 0000000000..ebe9ba6d24 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/MethodInfoTest.java @@ -0,0 +1,33 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test for {@link MethodInfo} */ +@RunWith(JUnit4.class) +public class MethodInfoTest { + + @Test + public void testMethodInfoAreCorrectlySet() { + MethodInfo method = MethodInfo.create("owner", "name", "desc"); + assertThat(method.owner()).isEqualTo("owner"); + assertThat(method.name()).isEqualTo("name"); + assertThat(method.desc()).isEqualTo("desc"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/StackMapBugTest.java b/src/test/java/com/google/devtools/build/android/desugar/StackMapBugTest.java new file mode 100644 index 0000000000..26aa52821b --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/StackMapBugTest.java @@ -0,0 +1,45 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import test.util.TestClassForStackMapFrame; + +/** This test case is for testing the fix for b/36654936. */ +@RunWith(JUnit4.class) +public class StackMapBugTest { + + /** This is a regression test for b/36654936 (external ASM bug 317785) */ + @Test + public void testAsmBug317785() { + int result = TestClassForStackMapFrame.testInputForAsmBug317785(); + assertThat(result).isEqualTo(20); + } + + /** + * This is a regression test for b/36654936 (external ASM bug 317785). The first attempted fix + * cl/152199391 caused stack map frame corruption, which caused the following test to fail. + */ + @Test + public void testStackMapFrameCorrectness() { + TestClassForStackMapFrame testObject = new TestClassForStackMapFrame(); + assertThat(testObject.joinIntegers(0)).isEmpty(); + assertThat(testObject.joinIntegers(1)).isEqualTo("0=Even"); + assertThat(testObject.joinIntegers(2)).isEqualTo("0=Even,1=Odd"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java b/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java new file mode 100644 index 0000000000..587d4f7b8b --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java @@ -0,0 +1,414 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.getStrategyClassName; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.getTwrStrategyClassNameSpecifiedInSystemProperty; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isMimicStrategy; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isNullStrategy; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isReuseStrategy; +import static org.junit.Assert.fail; +import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS; +import static org.objectweb.asm.Opcodes.ASM5; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; + +import com.google.devtools.build.android.desugar.runtime.ThrowableExtension; +import com.google.devtools.build.android.desugar.testdata.ClassUsingTryWithResources; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** This is the unit test for {@link TryWithResourcesRewriter} */ +@RunWith(JUnit4.class) +public class TryWithResourcesRewriterTest { + + private final DesugaringClassLoader classLoader = + new DesugaringClassLoader(ClassUsingTryWithResources.class.getName()); + private Class<?> desugaredClass; + + @Before + public void setup() { + try { + desugaredClass = classLoader.findClass(ClassUsingTryWithResources.class.getName()); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + + @Test + public void testMethodsAreDesugared() { + // verify whether the desugared class is indeed desugared. + DesugaredThrowableMethodCallCounter origCounter = + countDesugaredThrowableMethodCalls(ClassUsingTryWithResources.class); + DesugaredThrowableMethodCallCounter desugaredCounter = + countDesugaredThrowableMethodCalls(classLoader.classContent, classLoader); + /** + * In java9, javac creates a helper method {@code $closeResource(Throwable, AutoCloseable) + * to close resources. So, the following number 3 is highly dependant on the version of javac. + */ + assertThat(hasAutoCloseable(classLoader.classContent)).isFalse(); + assertThat(classLoader.numOfTryWithResourcesInvoked.intValue()).isAtLeast(2); + assertThat(classLoader.visitedExceptionTypes) + .containsExactly( + "java/lang/Exception", "java/lang/Throwable", "java/io/UnsupportedEncodingException"); + assertDesugaringBehavior(origCounter, desugaredCounter); + } + + @Test + public void testCheckSuppressedExceptionsReturningEmptySuppressedExceptions() { + { + Throwable[] suppressed = ClassUsingTryWithResources.checkSuppressedExceptions(false); + assertThat(suppressed).isEmpty(); + } + try { + Throwable[] suppressed = + (Throwable[]) + desugaredClass + .getMethod("checkSuppressedExceptions", boolean.class) + .invoke(null, Boolean.FALSE); + assertThat(suppressed).isEmpty(); + } catch (Exception e) { + e.printStackTrace(); + throw new AssertionError(e); + } + } + + @Test + public void testPrintStackTraceOfCaughtException() { + { + String trace = ClassUsingTryWithResources.printStackTraceOfCaughtException(); + assertThat(trace.toLowerCase()).contains("suppressed"); + } + try { + String trace = + (String) desugaredClass.getMethod("printStackTraceOfCaughtException").invoke(null); + + if (isMimicStrategy()) { + assertThat(trace.toLowerCase()).contains("suppressed"); + } else if (isReuseStrategy()) { + assertThat(trace.toLowerCase()).contains("suppressed"); + } else if (isNullStrategy()) { + assertThat(trace.toLowerCase()).doesNotContain("suppressed"); + } else { + fail("unexpected desugaring strategy " + ThrowableExtension.getStrategy()); + } + } catch (Exception e) { + e.printStackTrace(); + throw new AssertionError(e); + } + } + + @Test + public void testCheckSuppressedExceptionReturningOneSuppressedException() { + { + Throwable[] suppressed = ClassUsingTryWithResources.checkSuppressedExceptions(true); + assertThat(suppressed).hasLength(1); + } + try { + Throwable[] suppressed = + (Throwable[]) + desugaredClass + .getMethod("checkSuppressedExceptions", boolean.class) + .invoke(null, Boolean.TRUE); + + if (isMimicStrategy()) { + assertThat(suppressed).hasLength(1); + } else if (isReuseStrategy()) { + assertThat(suppressed).hasLength(1); + } else if (isNullStrategy()) { + assertThat(suppressed).isEmpty(); + } else { + fail("unexpected desugaring strategy " + ThrowableExtension.getStrategy()); + } + } catch (Exception e) { + e.printStackTrace(); + throw new AssertionError(e); + } + } + + @Test + public void testSimpleTryWithResources() throws Throwable { + { + try { + ClassUsingTryWithResources.simpleTryWithResources(); + fail("Expected RuntimeException"); + } catch (RuntimeException expected) { + assertThat(expected.getClass()).isEqualTo(RuntimeException.class); + assertThat(expected.getSuppressed()).hasLength(1); + assertThat(expected.getSuppressed()[0].getClass()).isEqualTo(IOException.class); + } + } + + try { + try { + desugaredClass.getMethod("simpleTryWithResources").invoke(null); + fail("Expected RuntimeException"); + } catch (InvocationTargetException e) { + throw e.getCause(); + } + } catch (RuntimeException expected) { + String expectedStrategyName = getTwrStrategyClassNameSpecifiedInSystemProperty(); + assertThat(getStrategyClassName()).isEqualTo(expectedStrategyName); + if (isMimicStrategy()) { + assertThat(expected.getSuppressed()).isEmpty(); + assertThat(ThrowableExtension.getSuppressed(expected)).hasLength(1); + assertThat(ThrowableExtension.getSuppressed(expected)[0].getClass()) + .isEqualTo(IOException.class); + } else if (isReuseStrategy()) { + assertThat(expected.getSuppressed()).hasLength(1); + assertThat(expected.getSuppressed()[0].getClass()).isEqualTo(IOException.class); + assertThat(ThrowableExtension.getSuppressed(expected)[0].getClass()) + .isEqualTo(IOException.class); + } else if (isNullStrategy()) { + assertThat(expected.getSuppressed()).isEmpty(); + assertThat(ThrowableExtension.getSuppressed(expected)).isEmpty(); + } else { + fail("unexpected desugaring strategy " + ThrowableExtension.getStrategy()); + } + } + } + + private static void assertDesugaringBehavior( + DesugaredThrowableMethodCallCounter orig, DesugaredThrowableMethodCallCounter desugared) { + assertThat(desugared.countThrowableGetSuppressed()).isEqualTo(orig.countExtGetSuppressed()); + assertThat(desugared.countThrowableAddSuppressed()).isEqualTo(orig.countExtAddSuppressed()); + assertThat(desugared.countThrowablePrintStackTrace()).isEqualTo(orig.countExtPrintStackTrace()); + assertThat(desugared.countThrowablePrintStackTracePrintStream()) + .isEqualTo(orig.countExtPrintStackTracePrintStream()); + assertThat(desugared.countThrowablePrintStackTracePrintWriter()) + .isEqualTo(orig.countExtPrintStackTracePrintWriter()); + + assertThat(orig.countThrowableGetSuppressed()).isEqualTo(desugared.countExtGetSuppressed()); + // $closeResource is rewritten to ThrowableExtension.closeResource, so addSuppressed() is called + // in the runtime library now. + assertThat(orig.countThrowableAddSuppressed()) + .isAtLeast(desugared.countThrowableAddSuppressed()); + assertThat(orig.countThrowablePrintStackTrace()).isEqualTo(desugared.countExtPrintStackTrace()); + assertThat(orig.countThrowablePrintStackTracePrintStream()) + .isEqualTo(desugared.countExtPrintStackTracePrintStream()); + assertThat(orig.countThrowablePrintStackTracePrintWriter()) + .isEqualTo(desugared.countExtPrintStackTracePrintWriter()); + + assertThat(desugared.countThrowablePrintStackTracePrintStream()).isEqualTo(0); + assertThat(desugared.countThrowablePrintStackTracePrintStream()).isEqualTo(0); + assertThat(desugared.countThrowablePrintStackTracePrintWriter()).isEqualTo(0); + assertThat(desugared.countThrowableAddSuppressed()).isEqualTo(0); + assertThat(desugared.countThrowableGetSuppressed()).isEqualTo(0); + } + + private static DesugaredThrowableMethodCallCounter countDesugaredThrowableMethodCalls( + Class<?> klass) { + try { + ClassReader reader = new ClassReader(klass.getName()); + DesugaredThrowableMethodCallCounter counter = + new DesugaredThrowableMethodCallCounter(klass.getClassLoader()); + reader.accept(counter, 0); + return counter; + } catch (IOException e) { + e.printStackTrace(); + fail(e.toString()); + return null; + } + } + + private static DesugaredThrowableMethodCallCounter countDesugaredThrowableMethodCalls( + byte[] content, ClassLoader loader) { + ClassReader reader = new ClassReader(content); + DesugaredThrowableMethodCallCounter counter = new DesugaredThrowableMethodCallCounter(loader); + reader.accept(counter, 0); + return counter; + } + + /** Check whether java.lang.AutoCloseable is used as arguments of any method. */ + private static boolean hasAutoCloseable(byte[] classContent) { + ClassReader reader = new ClassReader(classContent); + final AtomicInteger counter = new AtomicInteger(); + ClassVisitor visitor = + new ClassVisitor(Opcodes.ASM5) { + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + for (Type argumentType : Type.getArgumentTypes(desc)) { + if ("Ljava/lang/AutoCloseable;".equals(argumentType.getDescriptor())) { + counter.incrementAndGet(); + } + } + return null; + } + }; + reader.accept(visitor, 0); + return counter.get() > 0; + } + + private static class DesugaredThrowableMethodCallCounter extends ClassVisitor { + private final ClassLoader classLoader; + private final Map<String, AtomicInteger> counterMap; + + public DesugaredThrowableMethodCallCounter(ClassLoader loader) { + super(ASM5); + classLoader = loader; + counterMap = new HashMap<>(); + TryWithResourcesRewriter.TARGET_METHODS + .entries() + .forEach(entry -> counterMap.put(entry.getKey() + entry.getValue(), new AtomicInteger())); + TryWithResourcesRewriter.TARGET_METHODS + .entries() + .forEach( + entry -> + counterMap.put( + entry.getKey() + + TryWithResourcesRewriter.METHOD_DESC_MAP.get(entry.getValue()), + new AtomicInteger())); + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + return new InvokeCounter(); + } + + private class InvokeCounter extends MethodVisitor { + + public InvokeCounter() { + super(ASM5); + } + + private boolean isAssignableToThrowable(String owner) { + try { + Class<?> ownerClass = classLoader.loadClass(owner.replace('/', '.')); + return Throwable.class.isAssignableFrom(ownerClass); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + String signature = name + desc; + if ((opcode == INVOKEVIRTUAL && isAssignableToThrowable(owner)) + || (opcode == INVOKESTATIC + && Type.getInternalName(ThrowableExtension.class).equals(owner))) { + AtomicInteger counter = counterMap.get(signature); + if (counter == null) { + return; + } + counter.incrementAndGet(); + } + } + } + + public int countThrowableAddSuppressed() { + return counterMap.get("addSuppressed(Ljava/lang/Throwable;)V").get(); + } + + public int countThrowableGetSuppressed() { + return counterMap.get("getSuppressed()[Ljava/lang/Throwable;").get(); + } + + public int countThrowablePrintStackTrace() { + return counterMap.get("printStackTrace()V").get(); + } + + public int countThrowablePrintStackTracePrintStream() { + return counterMap.get("printStackTrace(Ljava/io/PrintStream;)V").get(); + } + + public int countThrowablePrintStackTracePrintWriter() { + return counterMap.get("printStackTrace(Ljava/io/PrintWriter;)V").get(); + } + + public int countExtAddSuppressed() { + return counterMap.get("addSuppressed(Ljava/lang/Throwable;Ljava/lang/Throwable;)V").get(); + } + + public int countExtGetSuppressed() { + return counterMap.get("getSuppressed(Ljava/lang/Throwable;)[Ljava/lang/Throwable;").get(); + } + + public int countExtPrintStackTrace() { + return counterMap.get("printStackTrace(Ljava/lang/Throwable;)V").get(); + } + + public int countExtPrintStackTracePrintStream() { + return counterMap.get("printStackTrace(Ljava/lang/Throwable;Ljava/io/PrintStream;)V").get(); + } + + public int countExtPrintStackTracePrintWriter() { + return counterMap.get("printStackTrace(Ljava/lang/Throwable;Ljava/io/PrintWriter;)V").get(); + } + } + + private static class DesugaringClassLoader extends ClassLoader { + + private final String targetedClassName; + private Class<?> klass; + private byte[] classContent; + private final Set<String> visitedExceptionTypes = new HashSet<>(); + private final AtomicInteger numOfTryWithResourcesInvoked = new AtomicInteger(); + + public DesugaringClassLoader(String targetedClassName) { + super(DesugaringClassLoader.class.getClassLoader()); + this.targetedClassName = targetedClassName; + } + + @Override + protected Class<?> findClass(String name) throws ClassNotFoundException { + if (name.equals(targetedClassName)) { + if (klass != null) { + return klass; + } + // desugar the class, and return the desugared one. + classContent = desugarTryWithResources(name); + klass = defineClass(name, classContent, 0, classContent.length); + return klass; + } else { + return super.findClass(name); + } + } + + private byte[] desugarTryWithResources(String className) { + try { + ClassReader reader = new ClassReader(className); + ClassWriter writer = new ClassWriter(reader, COMPUTE_MAXS); + TryWithResourcesRewriter rewriter = + new TryWithResourcesRewriter( + writer, + TryWithResourcesRewriterTest.class.getClassLoader(), + visitedExceptionTypes, + numOfTryWithResourcesInvoked); + reader.accept(rewriter, 0); + return writer.toByteArray(); + } catch (IOException e) { + fail(e.toString()); + return null; // suppress compiler error. + } + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/capture_lambda_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/capture_lambda_disassembled_golden.txt new file mode 100644 index 0000000000..752dad6c8e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/capture_lambda_disassembled_golden.txt @@ -0,0 +1,21 @@ +final class com.google.devtools.build.android.desugar.testdata.CaptureLambda$$Lambda$0 implements java.util.function.Predicate { + private final java.lang.String arg$1; + + com.google.devtools.build.android.desugar.testdata.CaptureLambda$$Lambda$0(java.lang.String); + Code: + 0: aload_0 + 1: invokespecial #13 // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: aload_1 + 6: putfield #15 // Field arg$1:Ljava/lang/String; + 9: return + + public boolean test(java.lang.Object); + Code: + 0: aload_0 + 1: getfield #15 // Field arg$1:Ljava/lang/String; + 4: aload_1 + 5: checkcast #19 // class java/lang/String + 8: invokestatic #25 // Method com/google/devtools/build/android/desugar/testdata/CaptureLambda.lambda$prefixed$0$CaptureLambda:(Ljava/lang/String;Ljava/lang/String;)Z + 11: ireturn +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/class_with_inherited_abstract_method_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/class_with_inherited_abstract_method_disassembled_golden.txt new file mode 100644 index 0000000000..9ab9eb720d --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/class_with_inherited_abstract_method_disassembled_golden.txt @@ -0,0 +1,4 @@ +Compiled from "Named.java" +public abstract class com.google.devtools.build.android.desugar.testdata.java8.Named$AbstractName extends com.google.devtools.build.android.desugar.testdata.java8.Named$AbstractNameBase implements com.google.devtools.build.android.desugar.testdata.java8.Named { + public com.google.devtools.build.android.desugar.testdata.java8.Named$AbstractName(); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/class_with_lambdas_in_implemented_interface_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/class_with_lambdas_in_implemented_interface_disassembled_golden.txt new file mode 100644 index 0000000000..48a863299d --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/class_with_lambdas_in_implemented_interface_disassembled_golden.txt @@ -0,0 +1,8 @@ +Compiled from "InterfaceMethod.java" +public class com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod$Concrete implements com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod { + public com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod$Concrete(); + public java.util.List defaultMethodReference(java.util.List); + public java.util.List staticMethodReference(java.util.List); + public java.util.List lambdaCallsDefaultMethod(java.util.List); + public boolean startsWithS(java.lang.String); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/diff.sh b/src/test/java/com/google/devtools/build/android/desugar/diff.sh new file mode 100755 index 0000000000..afe2d96f7d --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/diff.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# +# Copyright 2016 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +diff "$1" "$2" diff --git a/src/test/java/com/google/devtools/build/android/desugar/interface_with_desugared_method_bodies_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/interface_with_desugared_method_bodies_disassembled_golden.txt new file mode 100644 index 0000000000..828cee4d97 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/interface_with_desugared_method_bodies_disassembled_golden.txt @@ -0,0 +1,7 @@ +Compiled from "InterfaceMethod.java" +public interface com.google.devtools.build.android.desugar.testdata.java8.InterfaceMethod { + public abstract java.util.List<java.lang.String> defaultMethodReference(java.util.List<java.lang.String>); + public abstract java.util.List<java.lang.String> staticMethodReference(java.util.List<java.lang.String>); + public abstract java.util.List<java.lang.String> lambdaCallsDefaultMethod(java.util.List<java.lang.String>); + public abstract boolean startsWithS(java.lang.String); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/jacoco_0_7_5_default_method.jar b/src/test/java/com/google/devtools/build/android/desugar/jacoco_0_7_5_default_method.jar Binary files differnew file mode 100644 index 0000000000..1e0deca5a8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/jacoco_0_7_5_default_method.jar diff --git a/src/test/java/com/google/devtools/build/android/desugar/jacoco_legacy_default_method_companion_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/jacoco_legacy_default_method_companion_disassembled_golden.txt new file mode 100644 index 0000000000..0eb51c13ec --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/jacoco_legacy_default_method_companion_disassembled_golden.txt @@ -0,0 +1,37 @@ +public abstract class com.example.gavra.java8coverage.Defaults$$CC { + public static boolean[] $jacocoData; + + public static void foo(com.example.gavra.java8coverage.Defaults); + Code: + 0: invokestatic #12 // Method $jacocoInit$$STATIC$$:()[Z + 3: astore_1 + 4: aload_1 + 5: iconst_0 + 6: iconst_1 + 7: bastore + 8: return + + public static void baz$$STATIC$$(); + Code: + 0: invokestatic #12 // Method $jacocoInit$$STATIC$$:()[Z + 3: astore_0 + 4: aload_0 + 5: iconst_1 + 6: iconst_1 + 7: bastore + 8: return + + static boolean[] $jacocoInit$$STATIC$$(); + Code: + 0: getstatic #18 // Field $jacocoData:[Z + 3: dup + 4: ifnonnull 21 + 7: pop + 8: ldc2_w #19 // long -7447229029980688604l + 11: ldc #22 // String com/example/gavra/java8coverage/Defaults + 13: iconst_2 + 14: invokestatic #28 // Method org/jacoco/agent/rt/internal_773e439/Offline.getProbes:(JLjava/lang/String;I)[Z + 17: dup + 18: putstatic #18 // Field $jacocoData:[Z + 21: areturn +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/mocked_android_framework/android/os/Build.java b/src/test/java/com/google/devtools/build/android/desugar/mocked_android_framework/android/os/Build.java new file mode 100644 index 0000000000..43d2f7df08 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/mocked_android_framework/android/os/Build.java @@ -0,0 +1,31 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package android.os; + +/** This class is a standin for android.os.Build for tests running in a JVM */ +public final class Build { + + public static final String SYSTEM_PROPERTY_NAME = "fortest.simulated.android.sdk_int"; + + /** A simple mock for the real android.os.Build.VERSION */ + public static final class VERSION { + + public static final int SDK_INT; + + static { + String sdkInt = System.getProperty(SYSTEM_PROPERTY_NAME, "0"); + SDK_INT = Integer.parseInt(sdkInt); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/runtime/BUILD b/src/test/java/com/google/devtools/build/android/desugar/runtime/BUILD new file mode 100644 index 0000000000..d6f03e0655 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/runtime/BUILD @@ -0,0 +1,98 @@ +# Description: +# Tests for the extension classes for desugaring try-with-resources for Android. +package( + default_testonly = 1, + default_visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__subpackages__"], +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["*"]), +) + +java_library( + name = "throwable_extension_test_utility", + srcs = ["ThrowableExtensionTestUtility.java"], + deps = [ + "//third_party:guava", + "//third_party:truth", + ], +) + +java_test( + name = "ThrowableExtensionTestWithMimicDesugaringStrategy", + size = "small", + srcs = ["ThrowableExtensionTest.java"], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$MimicDesugaringStrategy'", + ], + test_class = "com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTest", + deps = [ + ":throwable_extension_test_utility", + "//src/test/java/com/google/devtools/build/android/desugar:mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/lib:testutil", + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "ThrowableExtensionTestWithNullDesugaringStrategy", + size = "small", + srcs = ["ThrowableExtensionTest.java"], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=18", + "-Dcom.google.devtools.build.android.desugar.runtime.twr_disable_mimic=true", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$NullDesugaringStrategy'", + ], + test_class = "com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTest", + deps = [ + ":throwable_extension_test_utility", + "//src/test/java/com/google/devtools/build/android/desugar:mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/lib:testutil", + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "ThrowableExtensionTestWithReuseDesugaringStrategy", + size = "small", + srcs = ["ThrowableExtensionTest.java"], + jvm_flags = [ + "-Dfortest.simulated.android.sdk_int=19", + "'-Dexpected.strategy=com.google.devtools.build.android.desugar.runtime.ThrowableExtension$$ReuseDesugaringStrategy'", + ], + test_class = "com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTest", + deps = [ + ":throwable_extension_test_utility", + "//src/test/java/com/google/devtools/build/android/desugar:mocked_android_os_sdk_for_testing", + "//src/test/java/com/google/devtools/build/lib:testutil", + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "ConcurrentWeakIdentityHashMapTest", + timeout = "long", # multi-threaded and rely on GC, so it could take long. + srcs = ["ConcurrentWeakIdentityHashMapTest.java"], + test_class = "com.google.devtools.build.android.desugar.runtime.ConcurrentWeakIdentityHashMapTest", + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", + "//third_party:guava", + "//third_party:guava-testlib", + "//third_party:junit4", + "//third_party:truth", + ], +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/runtime/ConcurrentWeakIdentityHashMapTest.java b/src/test/java/com/google/devtools/build/android/desugar/runtime/ConcurrentWeakIdentityHashMapTest.java new file mode 100644 index 0000000000..2fde7f49ae --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/runtime/ConcurrentWeakIdentityHashMapTest.java @@ -0,0 +1,262 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.runtime; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.GcFinalization; +import com.google.devtools.build.android.desugar.runtime.ThrowableExtension.ConcurrentWeakIdentityHashMap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test for {@link ConcurrentWeakIdentityHashMap}. This test uses multi-threading, and needs GC + * sometime to assert weak references, so it could take long. + */ +@RunWith(JUnit4.class) +public class ConcurrentWeakIdentityHashMapTest { + + private final Random random = new Random(); + + /** + * This method makes sure that after return, all the exceptions in the map should be garbage + * collected. . + */ + private static ConcurrentWeakIdentityHashMap + testConcurrentWeakIdentityHashMapSingleThreadedHelper(CountDownLatch latch) + throws InterruptedException { + ConcurrentWeakIdentityHashMap map = new ConcurrentWeakIdentityHashMap(); + Exception e1 = new ExceptionWithLatch("e1", latch); + assertThat(map.get(e1, false)).isNull(); + assertThat(map.get(e1, true)).isNotNull(); + assertThat(map.get(e1, true)).isEmpty(); + assertThat(map.get(e1, false)).isNotNull(); + + Exception suppressed1 = new ExceptionWithLatch("suppressed1", latch); + map.get(e1, true).add(suppressed1); + assertThat(map.get(e1, true)).containsExactly(suppressed1); + + Exception suppressed2 = new ExceptionWithLatch("suppressed2", latch); + map.get(e1, true).add(suppressed2); + assertThat(map.get(e1, true)).containsExactly(suppressed1, suppressed2); + + assertThat(map.get(suppressed1, false)).isNull(); + assertThat(map.get(suppressed2, false)).isNull(); + assertThat(map.size()).isEqualTo(1); + assertThat(map.get(suppressed1, true)).isNotNull(); + assertThat(map.size()).isEqualTo(2); + assertThat(map.get(suppressed1, true)).isNotNull(); + assertThat(map.size()).isEqualTo(2); + + Exception e2 = new ExceptionWithLatch("e2", latch); + assertThat(map.get(e2, true)).isNotNull(); + Exception e3 = new ExceptionWithLatch("e3", latch); + assertThat(map.get(e3, true)).isNotNull(); + assertThat(map.size()).isEqualTo(4); + return map; + } + + @Test + public void testSingleThreadedUse() throws InterruptedException { + CountDownLatch latch = new CountDownLatch(5); + ConcurrentWeakIdentityHashMap map = + testConcurrentWeakIdentityHashMapSingleThreadedHelper(latch); + for (int i = 0; i < 5; i++) { + map.deleteEmptyKeys(); + GcFinalization.awaitFullGc(); + } + latch.await(); // wait for e1 to be garbage collected. + map.deleteEmptyKeys(); + assertThat(map.size()).isEqualTo(0); + } + + private static Map<Throwable, List<Throwable>> createExceptionWithSuppressed( + int numMainExceptions, int numSuppressedPerMain, CountDownLatch latch) { + Map<Throwable, List<Throwable>> map = new HashMap<>(); + for (int i = 0; i < numMainExceptions; ++i) { + Exception main = new ExceptionWithLatch("main-" + i, latch); + List<Throwable> suppressedList = new ArrayList<>(); + assertThat(map).doesNotContainKey(main); + map.put(main, suppressedList); + for (int j = 0; j < numSuppressedPerMain; ++j) { + Exception suppressed = new ExceptionWithLatch("suppressed-" + j + "-main-" + i, latch); + suppressedList.add(suppressed); + } + } + return map; + } + + private ConcurrentWeakIdentityHashMap testFunctionalCorrectnessForMultiThreadedUse( + int numMainExceptions, int numSuppressedPerMain, CountDownLatch latch) + throws InterruptedException { + Map<Throwable, List<Throwable>> exceptionWithSuppressed = + createExceptionWithSuppressed(numMainExceptions, numSuppressedPerMain, latch); + assertThat(exceptionWithSuppressed).hasSize(numMainExceptions); + List<Pair> allPairs = + exceptionWithSuppressed + .entrySet() + .stream() + .flatMap( + entry -> entry.getValue().stream().map(value -> new Pair(entry.getKey(), value))) + .collect(Collectors.toList()); + Collections.shuffle(allPairs); + ConcurrentWeakIdentityHashMap map = new ConcurrentWeakIdentityHashMap(); + List<Worker> workers = + IntStream.range(1, 11) // ten threads. + .mapToObj(i -> new Worker("worker-" + i, map)) + .collect(Collectors.toList()); + + // Assign tasks to workers. + Iterator<Worker> workIterator = workers.iterator(); + for (Pair pair : allPairs) { + if (!workIterator.hasNext()) { + workIterator = workers.iterator(); + } + assertThat(workIterator.hasNext()).isTrue(); + workIterator.next().exceptionList.add(pair); + } + + // Execute all the workers. + ExecutorService executorService = Executors.newFixedThreadPool(workers.size()); + workers.forEach(executorService::execute); + executorService.shutdown(); + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // wait for completion. + exceptionWithSuppressed + .entrySet() + .forEach( + entry -> { + assertThat(map.get(entry.getKey(), false)).isNotNull(); + assertThat(map.get(entry.getKey(), false)) + .containsExactlyElementsIn(exceptionWithSuppressed.get(entry.getKey())); + }); + return map; + } + + private void testMultiThreadedUse(int numMainExceptions, int numSuppressedPerMain) + throws InterruptedException { + CountDownLatch latch = new CountDownLatch(numMainExceptions * numSuppressedPerMain); + ConcurrentWeakIdentityHashMap map = + testFunctionalCorrectnessForMultiThreadedUse( + numMainExceptions, numSuppressedPerMain, latch); + /* + * Calling the following methods multiple times to make sure the keys are garbage collected, + * and their corresponding entries are removed from the map. + */ + map.deleteEmptyKeys(); + GcFinalization.awaitFullGc(); + map.deleteEmptyKeys(); + GcFinalization.awaitFullGc(); + map.deleteEmptyKeys(); + + assertThat(map.size()).isEqualTo(0); + } + + @Test + public void testMultiThreadedUseMedium() throws InterruptedException { + for (int i = 0; i < 10; ++i) { + testMultiThreadedUse(50, 100); + } + } + + @Test + public void testMultiThreadedUseLarge() throws InterruptedException { + for (int i = 0; i < 5; ++i) { + testMultiThreadedUse(100, 100); + } + } + + @Test + public void testMultiThreadedUseSmall() throws InterruptedException { + for (int i = 0; i < 10; ++i) { + testMultiThreadedUse(20, 100); + } + } + + private static class ExceptionWithLatch extends Exception { + private final CountDownLatch latch; + + private ExceptionWithLatch(String message, CountDownLatch latch) { + super(message); + this.latch = latch; + } + + @Override + public String toString() { + return this.getMessage(); + } + + @Override + protected void finalize() throws Throwable { + latch.countDown(); + } + } + + private static class Pair { + final Throwable throwable; + final Throwable suppressed; + + public Pair(Throwable throwable, Throwable suppressed) { + this.throwable = throwable; + this.suppressed = suppressed; + } + } + + private class Worker implements Runnable { + private final ConcurrentWeakIdentityHashMap map; + private final List<Pair> exceptionList = new ArrayList<>(); + private final String name; + + private Worker(String name, ConcurrentWeakIdentityHashMap map) { + this.name = name; + this.map = map; + } + + public String getName() { + return name; + } + + @Override + public void run() { + Iterator<Pair> iterator = exceptionList.iterator(); + while (iterator.hasNext()) { + int timeToSleep = random.nextInt(3); + if (random.nextBoolean() && timeToSleep > 0) { + try { + Thread.sleep(timeToSleep); // add randomness to the scheduler. + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + Pair pair = iterator.next(); + List<Throwable> suppressed = map.get(pair.throwable, true); + System.out.printf("add suppressed %s to %s\n", pair.suppressed, pair.throwable); + suppressed.add(pair.suppressed); + } + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtensionTest.java b/src/test/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtensionTest.java new file mode 100644 index 0000000000..5ac88b89de --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtensionTest.java @@ -0,0 +1,453 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.runtime; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtension.MimicDesugaringStrategy.SUPPRESSED_PREFIX; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.getTwrStrategyClassNameSpecifiedInSystemProperty; +import static com.google.devtools.build.android.desugar.runtime.ThrowableExtensionTestUtility.isNullStrategy; +import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.fail; + +import com.google.devtools.build.android.desugar.runtime.ThrowableExtension.MimicDesugaringStrategy; +import com.google.devtools.build.android.desugar.runtime.ThrowableExtension.NullDesugaringStrategy; +import com.google.devtools.build.android.desugar.runtime.ThrowableExtension.ReuseDesugaringStrategy; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.function.Consumer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test case for {@link ThrowableExtension} */ +@RunWith(JUnit4.class) +public class ThrowableExtensionTest { + + /** + * This test tests the behavior of closing resources via reflection. This is only enabled below + * API 19. So, if the API level is 19 or above, this test will simply skip. + */ + @Test + public void testCloseResourceViaReflection() throws Throwable { + class Resource extends AbstractResource { + protected Resource(boolean exceptionOnClose) { + super(exceptionOnClose); + } + + public void close() throws Exception { + super.internalClose(); + } + } + if (ThrowableExtension.API_LEVEL >= 19) { + return; + } + { + Resource r = new Resource(false); + assertThat(r.isClosed()).isFalse(); + ThrowableExtension.closeResource(null, r); + assertThat(r.isClosed()).isTrue(); + } + { + Resource r = new Resource(true); + assertThat(r.isClosed()).isFalse(); + assertThrows(IOException.class, () -> ThrowableExtension.closeResource(null, r)); + } + { + Resource r = new Resource(false); + assertThat(r.isClosed()).isFalse(); + ThrowableExtension.closeResource(new Exception(), r); + assertThat(r.isClosed()).isTrue(); + } + { + Resource r = new Resource(true); + assertThat(r.isClosed()).isFalse(); + assertThrows(Exception.class, () -> ThrowableExtension.closeResource(new Exception(), r)); + } + } + + /** + * Test the new method closeResources() in the runtime library. + * + * <p>The method is introduced to fix b/37167433. + */ + @Test + public void testCloseResource() throws Throwable { + + /** + * A resource implementing the interface AutoCloseable. This interface is only available since + * API 19. + */ + class AutoCloseableResource extends AbstractResource implements AutoCloseable { + + protected AutoCloseableResource(boolean exceptionOnClose) { + super(exceptionOnClose); + } + + @Override + public void close() throws Exception { + internalClose(); + } + } + + /** A resource implementing the interface Closeable. */ + class CloseableResource extends AbstractResource implements Closeable { + + protected CloseableResource(boolean exceptionOnClose) { + super(exceptionOnClose); + } + + @Override + public void close() throws IOException { + internalClose(); + } + } + + { + CloseableResource r = new CloseableResource(false); + assertThat(r.isClosed()).isFalse(); + ThrowableExtension.closeResource(null, r); + assertThat(r.isClosed()).isTrue(); + } + { + CloseableResource r = new CloseableResource(false); + assertThat(r.isClosed()).isFalse(); + Exception suppressor = new Exception(); + ThrowableExtension.closeResource(suppressor, r); + assertThat(r.isClosed()).isTrue(); + assertThat(ThrowableExtension.getSuppressed(suppressor)).isEmpty(); + } + { + CloseableResource r = new CloseableResource(true); + assertThat(r.isClosed()).isFalse(); + assertThrows(IOException.class, () -> ThrowableExtension.closeResource(null, r)); + assertThat(r.isClosed()).isFalse(); + } + { + CloseableResource r = new CloseableResource(true); + assertThat(r.isClosed()).isFalse(); + Exception suppressor = new Exception(); + assertThrows(Exception.class, () -> ThrowableExtension.closeResource(suppressor, r)); + assertThat(r.isClosed()).isFalse(); // Failed to close. + if (!isNullStrategy()) { + assertThat(ThrowableExtension.getSuppressed(suppressor)).hasLength(1); + assertThat(ThrowableExtension.getSuppressed(suppressor)[0].getClass()) + .isEqualTo(IOException.class); + } + } + { + AutoCloseableResource r = new AutoCloseableResource(false); + assertThat(r.isClosed()).isFalse(); + ThrowableExtension.closeResource(null, r); + assertThat(r.isClosed()).isTrue(); + } + { + AutoCloseableResource r = new AutoCloseableResource(false); + assertThat(r.isClosed()).isFalse(); + Exception suppressor = new Exception(); + ThrowableExtension.closeResource(suppressor, r); + assertThat(r.isClosed()).isTrue(); + assertThat(ThrowableExtension.getSuppressed(suppressor)).isEmpty(); + } + { + AutoCloseableResource r = new AutoCloseableResource(true); + assertThat(r.isClosed()).isFalse(); + assertThrows(IOException.class, () -> ThrowableExtension.closeResource(null, r)); + assertThat(r.isClosed()).isFalse(); + } + { + AutoCloseableResource r = new AutoCloseableResource(true); + assertThat(r.isClosed()).isFalse(); + Exception suppressor = new Exception(); + assertThrows(Exception.class, () -> ThrowableExtension.closeResource(suppressor, r)); + assertThat(r.isClosed()).isFalse(); // Failed to close. + if (!isNullStrategy()) { + assertThat(ThrowableExtension.getSuppressed(suppressor)).hasLength(1); + assertThat(ThrowableExtension.getSuppressed(suppressor)[0].getClass()) + .isEqualTo(IOException.class); + } + assertThat(r.isClosed()).isFalse(); + } + } + + /** + * LightweightStackTraceRecorder tracks the calls of various printStackTrace(*), and ensures that + * + * <p>suppressed exceptions are printed only once. + */ + @Test + public void testLightweightStackTraceRecorder() throws IOException { + MimicDesugaringStrategy strategy = new MimicDesugaringStrategy(); + ExceptionForTest receiver = new ExceptionForTest(strategy); + FileNotFoundException suppressed = new FileNotFoundException(); + strategy.addSuppressed(receiver, suppressed); + + String trace = printStackTraceStderrToString(() -> strategy.printStackTrace(receiver)); + assertThat(trace).contains(SUPPRESSED_PREFIX); + assertThat(countOccurrences(trace, SUPPRESSED_PREFIX)).isEqualTo(1); + } + + @Test + public void testMimicDesugaringStrategy() throws IOException { + MimicDesugaringStrategy strategy = new MimicDesugaringStrategy(); + IOException receiver = new IOException(); + FileNotFoundException suppressed = new FileNotFoundException(); + strategy.addSuppressed(receiver, suppressed); + + assertThat( + printStackTracePrintStreamToString( + stream -> strategy.printStackTrace(receiver, stream))) + .contains(SUPPRESSED_PREFIX); + + assertThat( + printStackTracePrintWriterToString( + writer -> strategy.printStackTrace(receiver, writer))) + .contains(SUPPRESSED_PREFIX); + + assertThat(printStackTraceStderrToString(() -> strategy.printStackTrace(receiver))) + .contains(SUPPRESSED_PREFIX); + } + + private void testThrowableExtensionWithMimicDesugaringStrategy() throws IOException { + IOException receiver = new IOException(); + FileNotFoundException suppressed = new FileNotFoundException(); + ThrowableExtension.addSuppressed(receiver, suppressed); + + assertThat( + printStackTracePrintStreamToString( + stream -> ThrowableExtension.printStackTrace(receiver, stream))) + .contains(SUPPRESSED_PREFIX); + assertThat( + printStackTracePrintWriterToString( + writer -> ThrowableExtension.printStackTrace(receiver, writer))) + .contains(SUPPRESSED_PREFIX); + assertThat(printStackTraceStderrToString(() -> ThrowableExtension.printStackTrace(receiver))) + .contains(SUPPRESSED_PREFIX); + } + + private interface PrintStackTraceCaller { + void printStackTrace(); + } + + private static String printStackTraceStderrToString(PrintStackTraceCaller caller) + throws IOException { + PrintStream err = System.err; + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + PrintStream newErr = new PrintStream(stream); + System.setErr(newErr); + caller.printStackTrace(); + newErr.flush(); + return stream.toString(); + } finally { + System.setErr(err); + } + } + + private static String printStackTracePrintStreamToString(Consumer<PrintStream> caller) + throws IOException { + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + PrintStream printStream = new PrintStream(stream); + caller.accept(printStream); + printStream.flush(); + return stream.toString(); + } + } + + private static String printStackTracePrintWriterToString(Consumer<PrintWriter> caller) + throws IOException { + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + PrintWriter printWriter = + new PrintWriter(new BufferedWriter(new OutputStreamWriter(stream, UTF_8))); + caller.accept(printWriter); + printWriter.flush(); + return stream.toString(); + } + } + + @Test + public void testNullDesugaringStrategy() throws IOException { + NullDesugaringStrategy strategy = new NullDesugaringStrategy(); + IOException receiver = new IOException(); + FileNotFoundException suppressed = new FileNotFoundException(); + strategy.addSuppressed(receiver, suppressed); + assertThat(strategy.getSuppressed(receiver)).isEmpty(); + + strategy.addSuppressed(receiver, suppressed); + assertThat(strategy.getSuppressed(receiver)).isEmpty(); + + assertThat(printStackTracePrintStreamToString(stream -> receiver.printStackTrace(stream))) + .isEqualTo( + printStackTracePrintStreamToString( + stream -> strategy.printStackTrace(receiver, stream))); + + assertThat(printStackTracePrintWriterToString(receiver::printStackTrace)) + .isEqualTo( + printStackTracePrintWriterToString( + writer -> strategy.printStackTrace(receiver, writer))); + + assertThat(printStackTraceStderrToString(receiver::printStackTrace)) + .isEqualTo(printStackTraceStderrToString(() -> strategy.printStackTrace(receiver))); + } + + private void testThrowableExtensionWithNullDesugaringStrategy() throws IOException { + IOException receiver = new IOException(); + FileNotFoundException suppressed = new FileNotFoundException(); + ThrowableExtension.addSuppressed(receiver, suppressed); + assertThat(ThrowableExtension.getSuppressed(receiver)).isEmpty(); + + ThrowableExtension.addSuppressed(receiver, suppressed); + assertThat(ThrowableExtension.getSuppressed(receiver)).isEmpty(); + + assertThat(printStackTracePrintStreamToString(stream -> receiver.printStackTrace(stream))) + .isEqualTo( + printStackTracePrintStreamToString( + stream -> ThrowableExtension.printStackTrace(receiver, stream))); + assertThat(printStackTracePrintWriterToString(receiver::printStackTrace)) + .isEqualTo( + printStackTracePrintWriterToString( + writer -> ThrowableExtension.printStackTrace(receiver, writer))); + + assertThat(printStackTraceStderrToString(receiver::printStackTrace)) + .isEqualTo( + printStackTraceStderrToString(() -> ThrowableExtension.printStackTrace(receiver))); + } + + @Test + public void testReuseDesugaringStrategy() throws IOException { + ReuseDesugaringStrategy strategy = new ReuseDesugaringStrategy(); + IOException receiver = new IOException(); + FileNotFoundException suppressed = new FileNotFoundException(); + strategy.addSuppressed(receiver, suppressed); + assertThat(strategy.getSuppressed(receiver)) + .asList() + .containsExactly((Object[]) receiver.getSuppressed()); + + assertThat(printStackTracePrintStreamToString(stream -> receiver.printStackTrace(stream))) + .isEqualTo( + printStackTracePrintStreamToString( + stream -> strategy.printStackTrace(receiver, stream))); + + assertThat(printStackTracePrintWriterToString(receiver::printStackTrace)) + .isEqualTo( + printStackTracePrintWriterToString( + writer -> strategy.printStackTrace(receiver, writer))); + assertThat(printStackTraceStderrToString(receiver::printStackTrace)) + .isEqualTo(printStackTraceStderrToString(() -> strategy.printStackTrace(receiver))); + } + + private void testThrowableExtensionWithReuseDesugaringStrategy() throws IOException { + IOException receiver = new IOException(); + FileNotFoundException suppressed = new FileNotFoundException(); + ThrowableExtension.addSuppressed(receiver, suppressed); + assertThat(ThrowableExtension.getSuppressed(receiver)) + .asList() + .containsExactly((Object[]) receiver.getSuppressed()); + + assertThat(printStackTracePrintStreamToString(receiver::printStackTrace)) + .isEqualTo( + printStackTracePrintStreamToString( + stream -> ThrowableExtension.printStackTrace(receiver, stream))); + + assertThat(printStackTracePrintWriterToString(receiver::printStackTrace)) + .isEqualTo( + printStackTracePrintWriterToString( + writer -> ThrowableExtension.printStackTrace(receiver, writer))); + + assertThat(printStackTraceStderrToString(receiver::printStackTrace)) + .isEqualTo( + printStackTraceStderrToString(() -> ThrowableExtension.printStackTrace(receiver))); + } + + /** This class */ + private static class ExceptionForTest extends Exception { + + private final MimicDesugaringStrategy strategy; + + public ExceptionForTest(MimicDesugaringStrategy strategy) { + this.strategy = strategy; + } + + @Override + public void printStackTrace() { + this.printStackTrace(System.err); + } + + /** + * This method should call this.printStackTrace(PrintWriter) directly. I deliberately change it + * to strategy.printStackTrace(Throwable, PrintWriter) to simulate the behavior of Desguar, that + * is, the direct call is intercepted and redirected to ThrowableExtension. + */ + @Override + public void printStackTrace(PrintStream s) { + this.strategy.printStackTrace( + this, new PrintWriter(new BufferedWriter(new OutputStreamWriter(s, UTF_8)))); + } + } + + @Test + public void testStrategySelection() throws ClassNotFoundException, IOException { + String expectedStrategyClassName = getTwrStrategyClassNameSpecifiedInSystemProperty(); + assertThat(expectedStrategyClassName).isNotEmpty(); + assertThat(expectedStrategyClassName) + .isEqualTo(ThrowableExtension.STRATEGY.getClass().getName()); + + Class<?> expectedStrategyClass = Class.forName(expectedStrategyClassName); + if (expectedStrategyClass.equals(ReuseDesugaringStrategy.class)) { + testThrowableExtensionWithReuseDesugaringStrategy(); + } else if (expectedStrategyClass.equals(MimicDesugaringStrategy.class)) { + testThrowableExtensionWithMimicDesugaringStrategy(); + } else if (expectedStrategyClass.equals(NullDesugaringStrategy.class)) { + testThrowableExtensionWithNullDesugaringStrategy(); + } else { + fail("unrecognized expected strategy class " + expectedStrategyClassName); + } + } + + private static int countOccurrences(String string, String substring) { + int i = 0; + int count = 0; + while ((i = string.indexOf(substring, i)) >= 0) { + ++count; + i = i + string.length(); + } + return count; + } + + /** A mocked closeable class, which we can query the closedness. */ + private abstract static class AbstractResource { + private final boolean exceptionOnClose; + private boolean closed; + + protected AbstractResource(boolean exceptionOnClose) { + this.exceptionOnClose = exceptionOnClose; + } + + boolean isClosed() { + return closed; + } + + void internalClose() throws IOException { + if (exceptionOnClose) { + throw new IOException("intended exception"); + } + closed = true; + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtensionTestUtility.java b/src/test/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtensionTestUtility.java new file mode 100644 index 0000000000..b65b8bdd90 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtensionTestUtility.java @@ -0,0 +1,64 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.runtime; + +import static com.google.common.truth.Truth.assertThat; + +import java.lang.reflect.Method; + +/** + * A utility class for testing ThrowableExtension. It uses reflection to get the strategy name, so + * as to avoid dependency on the runtime library. This is beneficial, because we can test whether + * the runtime library is on the classpath. + */ +public class ThrowableExtensionTestUtility { + + private static final String SYSTEM_PROPERTY_EXPECTED_STRATEGY = "expected.strategy"; + + public static String getTwrStrategyClassNameSpecifiedInSystemProperty() { + String className = System.getProperty(SYSTEM_PROPERTY_EXPECTED_STRATEGY); + assertThat(className).isNotEmpty(); + return className; + } + + private static final String THROWABLE_EXTENSION_CLASS_NAME = + "com.google.devtools.build.android.desugar.runtime.ThrowableExtension"; + + private static boolean isStrategyOfClass(String className) { + return getStrategyClassName().equals(className); + } + + public static String getStrategyClassName() { + try { + Class<?> klass = Class.forName(THROWABLE_EXTENSION_CLASS_NAME); + Method method = klass.getMethod("getStrategy"); + Object strategy = method.invoke(null); + return strategy.getClass().getName(); + } catch (Throwable e) { + throw new AssertionError(e); + } + } + + public static boolean isMimicStrategy() { + return isStrategyOfClass(THROWABLE_EXTENSION_CLASS_NAME + "$MimicDesugaringStrategy"); + } + + public static boolean isNullStrategy() { + return isStrategyOfClass(THROWABLE_EXTENSION_CLASS_NAME + "$NullDesugaringStrategy"); + } + + public static boolean isReuseStrategy() { + return isStrategyOfClass(THROWABLE_EXTENSION_CLASS_NAME + "$ReuseDesugaringStrategy"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/simple_instance_method_reference_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/simple_instance_method_reference_disassembled_golden.txt new file mode 100644 index 0000000000..7f26e7ef87 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/simple_instance_method_reference_disassembled_golden.txt @@ -0,0 +1,21 @@ +final class com.google.devtools.build.android.desugar.testdata.MethodReferenceSuperclass$$Lambda$0 implements java.util.function.Predicate { + private final com.google.devtools.build.android.desugar.testdata.MethodReferenceSuperclass arg$1; + + com.google.devtools.build.android.desugar.testdata.MethodReferenceSuperclass$$Lambda$0(com.google.devtools.build.android.desugar.testdata.MethodReferenceSuperclass); + Code: + 0: aload_0 + 1: invokespecial #13 // Method java/lang/Object."<init>":()V + 4: aload_0 + 5: aload_1 + 6: putfield #15 // Field arg$1:Lcom/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass; + 9: return + + public boolean test(java.lang.Object); + Code: + 0: aload_0 + 1: getfield #15 // Field arg$1:Lcom/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass; + 4: aload_1 + 5: checkcast #19 // class java/lang/String + 8: invokevirtual #25 // Method com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.bridge$lambda$0$MethodReferenceSuperclass:(Ljava/lang/String;)Z + 11: ireturn +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/stateless_lambda_disassembled_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/stateless_lambda_disassembled_golden.txt new file mode 100644 index 0000000000..c297c0477a --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/stateless_lambda_disassembled_golden.txt @@ -0,0 +1,24 @@ +final class com.google.devtools.build.android.desugar.testdata.Lambda$$Lambda$0 implements java.util.function.Predicate { + static final java.util.function.Predicate $instance; + + private com.google.devtools.build.android.desugar.testdata.Lambda$$Lambda$0(); + Code: + 0: aload_0 + 1: invokespecial #10 // Method java/lang/Object."<init>":()V + 4: return + + public boolean test(java.lang.Object); + Code: + 0: aload_1 + 1: checkcast #14 // class java/lang/String + 4: invokestatic #20 // Method com/google/devtools/build/android/desugar/testdata/Lambda.lambda$as$0$Lambda:(Ljava/lang/String;)Z + 7: ireturn + + static {}; + Code: + 0: new #2 // class com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$0 + 3: dup + 4: invokespecial #24 // Method "<init>":()V + 7: putstatic #26 // Field $instance:Ljava/util/function/Predicate; + 10: return +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/static_initializer_of_functional_interface_should_not_execute.sh b/src/test/java/com/google/devtools/build/android/desugar/static_initializer_of_functional_interface_should_not_execute.sh new file mode 100755 index 0000000000..32b4c5a6b1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/static_initializer_of_functional_interface_should_not_execute.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e +# +# Copyright 2016 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test whether Desugar runs static initializers of interfaces. +if grep "THIS STRING IS NOT EXPECTED TO APPEAR IN THE OUTPUT OF DESUGAR!!!" "${1}" ; then + exit 1 +else + exit 0 +fi diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/CaptureLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/CaptureLambda.java new file mode 100644 index 0000000000..6c4b6f5169 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/CaptureLambda.java @@ -0,0 +1,33 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.stream.Collectors; + +public class CaptureLambda { + + private final List<String> names; + + public CaptureLambda(List<String> names) { + this.names = names; + } + + public List<String> prefixed(String prefix) { + return names + .stream() + .filter(n -> n.startsWith(prefix)) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.java new file mode 100644 index 0000000000..1026437b9e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.java @@ -0,0 +1,51 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +/** This class calls Long.compare(long, long) */ +public class ClassCallingLongCompare { + + public static int compareLongByCallingLong_compare(long a, long b) { + return Long.compare(a, b); + } + + public static String compareLongByCallingLong_compare2(long a, long b) { + if (Long.compare(a, b) == 0) { + return "e"; + } + if (Long.compare(a, b) > 0) { + return "g"; + } + if (Long.compare(a, b) < 0) { + return "l"; + } + throw new AssertionError("unreachable"); + } + + public static int compareLongWithLambda(long a, long b) { + return internalCompare(a, b, (long l1, long l2) -> Long.compare(l1, l2)); + } + + public static int compareLongWithMethodReference(long a, long b) { + return internalCompare(a, b, Long::compare); + } + + private static interface LongCmpFunc { + int compare(long a, long b); + } + + private static int internalCompare(long a, long b, LongCmpFunc func) { + return func.compare(a, b); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.java new file mode 100644 index 0000000000..3823f1d774 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.java @@ -0,0 +1,53 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.Objects; +import java.util.function.IntSupplier; + +/** This class is for the testing of desugaring calls to Objects.requireNonNull(Object o...) */ +public class ClassCallingRequireNonNull { + + public static int getStringLengthWithMethodReference(String s) { + return toInt(s::length); + } + + public static int toInt(IntSupplier function) { + return function.getAsInt(); + } + + public static int getStringLengthWithLambdaAndExplicitCallToRequireNonNull(final String s) { + return toInt(() -> Objects.requireNonNull(s).length()); + } + + public static char getFirstCharVersionOne(String string) { + Objects.requireNonNull(string); + return string.charAt(0); + } + + public static char getFirstCharVersionTwo(String string) { + string = Objects.requireNonNull(string); + return string.charAt(0); + } + + public static char callRequireNonNullWithArgumentString(String string) { + string = Objects.requireNonNull(string, "the string should not be null"); + return string.charAt(0); + } + + public static char callRequireNonNullWithArgumentSupplier(String string) { + string = Objects.requireNonNull(string, () -> "the string should not be null"); + return string.charAt(0); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.java new file mode 100644 index 0000000000..c340c8489d --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.java @@ -0,0 +1,88 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; + +/** + * This is a test subject for {@link + * com.google.devtools.build.android.desugar.TryWithResourcesRewriter} + */ +public class ClassUsingTryWithResources { + + /** + * A simple resource, which always throws an exception when being closed. + * + * <p>Note that we need to implement java.io.Closeable instead of java.lang.AutoCloseable, because + * AutoCloseable is not available below API 19 + * + * <p>java9 will emit $closeResource(Throwable, AutoCloseable) for the following class. + */ + public static class SimpleResource implements Closeable { + + public void call(boolean throwException) { + if (throwException) { + throw new RuntimeException("exception in call()"); + } + } + + @Override + public void close() throws IOException { + throw new IOException("exception in close()."); + } + } + + /** This method will always throw {@link java.lang.Exception}. */ + public static void simpleTryWithResources() throws Exception { + // Throwable.addSuppressed(Throwable) should be called in the following block. + try (SimpleResource resource = new SimpleResource()) { + resource.call(true); + } + } + + public static Throwable[] checkSuppressedExceptions(boolean throwException) { + // Throwable.addSuppressed(Throwable) should be called in the following block. + try (SimpleResource resource = new SimpleResource()) { + resource.call(throwException); + } catch (Exception e) { + return e.getSuppressed(); // getSuppressed() is called. + } + return new Throwable[0]; + } + + public static String printStackTraceOfCaughtException() { + try { + simpleTryWithResources(); + } catch (Exception e) { + PrintStream err = System.err; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + System.setErr(new PrintStream(stream, true, "utf-8")); + e.printStackTrace(); + } catch (UnsupportedEncodingException e1) { + throw new AssertionError(e1); + } finally { + System.setErr(err); + } + return new String(stream.toByteArray(), UTF_8); + } + return ""; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/ConcreteFunction.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/ConcreteFunction.java new file mode 100644 index 0000000000..69bacdc6a8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/ConcreteFunction.java @@ -0,0 +1,54 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import com.google.devtools.build.android.desugar.testdata.separate.SeparateInterface; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ConcreteFunction implements SpecializedFunction<String, Long> { + @Override + public Long apply(String input) { + return Long.valueOf(input); + } + + // SpecializedParser makes it so we have to search multiple extended interfaces for bridge methods + // when desugaring the lambda returned by this method. + public static SpecializedParser<Integer> toInt() { + return (s -> Integer.valueOf(s)); + } + + public static SeparateInterface<Long> isInt() { + return (l -> Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE); + } + + public static <T extends Number> List<T> parseAll(List<String> in, + SpecializedFunction<String, T> parser) { + return in.stream().map(parser).collect(Collectors.toList()); + } + + public static <T extends Number> List<T> doFilter(List<T> in, SeparateInterface<T> filter) { + return in.stream().filter(filter).collect(Collectors.toList()); + } + + interface Parser<T> extends Function<String, T> { + @Override public T apply(String in); + } + + public interface SpecializedParser<T extends Number> + extends SpecializedFunction<String, T>, Parser<T> { + @Override public T apply(String in); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/ConstructorReference.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/ConstructorReference.java new file mode 100644 index 0000000000..03af2a3ea8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/ConstructorReference.java @@ -0,0 +1,56 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class ConstructorReference { + + private final List<String> names; + + private ConstructorReference(String name) { + names = new ArrayList<>(1); + names.add(name); + } + + public ConstructorReference(List<String> names) { + this.names = names; + } + + public List<Integer> toInt() { + return names.stream().map(Integer::new).collect(Collectors.toList()); + } + + public static Function<String, ConstructorReference> singleton() { + return ConstructorReference::new; + } + + public static Supplier<ConstructorReference> emptyThroughJavacGeneratedBridge() { + // Because Empty is private in another (inner) class, Javac seems to generate a lambda body + // method in this case that calls the Empty(SentinalType) bridge constructor Javac generates. + return Empty::new; + } + + private static class Empty extends ConstructorReference { + + private Empty() { + super(new ArrayList<String>(0)); + throw new RuntimeException("got it!"); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/GuavaLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/GuavaLambda.java new file mode 100644 index 0000000000..4974d2561b --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/GuavaLambda.java @@ -0,0 +1,31 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import static com.google.common.collect.Iterables.filter; + +import java.util.List; + +public class GuavaLambda { + + private final List<String> names; + + public GuavaLambda(List<String> names) { + this.names = names; + } + + public Iterable<String> as() { + return filter(names, n -> n.startsWith("A")); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/InnerClassLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/InnerClassLambda.java new file mode 100644 index 0000000000..0fe12b9a5c --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/InnerClassLambda.java @@ -0,0 +1,51 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class InnerClassLambda { + + protected final List<String> reference; + + public InnerClassLambda(List<String> names) { + this.reference = names; + } + + /** + * Uses a lambda that refers to a method parameter across 2 nested anonymous inner classes as well + * as a field in the outer scope, the former being relatively unusual as it causes javac to emit + * 2 getfields to pass the captured parameter directly to the generated lambda class, covering + * an unusual branch in how we rewrite invokedynamics. + */ + public Function<List<String>, Callable<List<String>>> prefixFilter(String prefix) { + return new Function<List<String>, Callable<List<String>>>() { + @Override + public Callable<List<String>> apply(List<String> input) { + return new Callable<List<String>>() { + @Override + public List<String> call() throws Exception { + return input + .stream() + .filter(n -> n.startsWith(prefix) && reference.contains(n)) + .collect(Collectors.toList()); + } + }; + } + }; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.java new file mode 100644 index 0000000000..47d8ab6ecd --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.java @@ -0,0 +1,25 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import com.google.common.collect.ImmutableList; + +public interface InterfaceWithLambda { + String ZERO = String.valueOf(0); + ImmutableList<String> DIGITS = + ImmutableList.of(0, 1) + .stream() + .map(i -> i == 0 ? ZERO : String.valueOf(i)) + .collect(ImmutableList.toImmutableList()); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/Lambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/Lambda.java new file mode 100644 index 0000000000..48c81c8c87 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/Lambda.java @@ -0,0 +1,60 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class Lambda { + + private final List<String> names; + + public Lambda(List<String> names) { + this.names = names; + } + + public List<String> as() { + return names + .stream() + .filter(n -> n.startsWith("A")) + .collect(Collectors.toList()); + } + + public static Callable<String> hello() { + return (Callable<String> & java.util.RandomAccess) () -> "hello"; + } + + public static Function<Integer, Callable<Long>> mult(int x) { + return new Function<Integer, Callable<Long>>() { + @Override + public Callable<Long> apply(Integer y) { + return () -> (long) x * (long) y; + } + }; + } + + /** + * Test method for b/62456849. This method will first be converted to a synthetic method by {@link + * com.google.devtools.build.android.desugar.Bug62456849TestDataGenerator}, and then Desugar + * should keep it in this class without desugaring it (such as renaming). + * + * <p>Please ignore the lint error on the method name. The method name is intentionally chosen to + * trigger a bug in Desugar. + */ + public static int lambda$mult$0() { + return 0; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/LambdaInOverride.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/LambdaInOverride.java new file mode 100644 index 0000000000..1f683f8788 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/LambdaInOverride.java @@ -0,0 +1,35 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Test class carefully constructed so javac emits a lambda body method called lambda$filter$0, + * which is exactly the name used for the lambda body method generated by javac for the superclass. + */ +public class LambdaInOverride extends OuterReferenceLambda { + public LambdaInOverride(List<String> names) { + super(names); + } + + public List<String> filter(List<String> names) { + return super + .filter(names) + .stream() + .filter(n -> !reference.contains(n)) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReference.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReference.java new file mode 100644 index 0000000000..a293ceab03 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReference.java @@ -0,0 +1,88 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import com.google.devtools.build.android.desugar.testdata.separate.SeparateBaseClass; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class MethodReference extends SeparateBaseClass<String> { + + private final List<String> names; + + public MethodReference(List<String> names) { + super(names); + this.names = names; + } + + // Class method reference + public void appendAll(StringBuilder dest) { + names.stream().forEach(dest::append); + } + + // Interface method reference (regression test for b/33304582) + public List<String> transform(Transformer<String> transformer) { + return names.stream().map(transformer::transform).collect(Collectors.toList()); + } + + // Private method reference (regression test for b/33378312) + public List<String> some() { + return names.stream().filter(MethodReference::startsWithS).collect(Collectors.toList()); + } + + // Protected method reference in a base class of another package (regression test for b/33378312) + public List<String> intersect(List<String> other) { + return other.stream().filter(this::contains).collect(Collectors.toList()); + } + + // Contains the same method reference as intersect + public List<String> onlyIn(List<String> other) { + Predicate<String> p = this::contains; + return other.stream().filter(p.negate()).collect(Collectors.toList()); + } + + // Private method reference to an instance method that throws (regression test for b/33378312) + public Callable<String> stringer() { + return this::throwing; + } + + /** Returns a method reference derived from an expression (object.toString()). */ + public static Function<Integer, Character> stringChars(Object object) { + return (object == null ? "" : object.toString())::charAt; + } + + /** Returns a method reference derived from a field */ + public Predicate<String> toPredicate() { + return names::contains; + } + + private static boolean startsWithS(String input) { + return input.startsWith("S"); + } + + private String throwing() throws Exception { + StringBuilder msg = new StringBuilder(); + appendAll(msg); + throw new IOException(msg.toString()); + } + + /** Interface to create a method reference for in {@link #transform}. */ + public interface Transformer<T> { + T transform(T input); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.java new file mode 100644 index 0000000000..fb1c49ece8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.java @@ -0,0 +1,37 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.stream.Collectors; + +public class MethodReferenceInSubclass extends MethodReferenceSuperclass { + + public MethodReferenceInSubclass(List<String> names) { + super(names); + } + + // Private method reference in subclass that causes a bridge method with the same signature as in + // a superclass in the same package (regression test for b/36201257). Both superclass and this + // class need a method reference to a private *instance* method with the same signature, and they + // should each only one method reference and no lambdas so any class-local counter matches, for + // this class to serve as a repro for b/36201257. + public List<String> containsE() { + return names.stream().filter(this::containsE).collect(Collectors.toList()); + } + + private boolean containsE(String input) { + return input.contains("e"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.java new file mode 100644 index 0000000000..c24cf55b81 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.java @@ -0,0 +1,39 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.stream.Collectors; + +public class MethodReferenceSuperclass { + + protected final List<String> names; + + public MethodReferenceSuperclass(List<String> names) { + this.names = names; + } + + // Method reference that causes a simple bridge method because the referenced method is private. + // We want to make sure that bridge methods generated in subclasses don't clobber this one. + public List<String> startsWithL() { + return names + .stream() + .filter(this::startsWithL) + .collect(Collectors.toList()); + } + + private boolean startsWithL(String input) { + return input.startsWith("L"); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.java new file mode 100644 index 0000000000..d3cf829900 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.java @@ -0,0 +1,33 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.List; +import java.util.stream.Collectors; + +public class OuterReferenceLambda { + + protected final List<String> reference; + + public OuterReferenceLambda(List<String> names) { + this.reference = names; + } + + public List<String> filter(List<String> names) { + return names + .stream() + .filter(n -> reference.contains(n)) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/SpecializedFunction.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/SpecializedFunction.java new file mode 100644 index 0000000000..b7616a0ae3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/SpecializedFunction.java @@ -0,0 +1,22 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata; + +import java.util.function.Function; + +public interface SpecializedFunction<S, T extends Number> extends Function<S, T> { + Integer DO_NOT_COPY_INTO_LAMBDA_CLASSES = Integer.valueOf(42); + @Override + public T apply(S in); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/core_library/java/lang/AutoboxedTypes.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/core_library/java/lang/AutoboxedTypes.java new file mode 100644 index 0000000000..bdf298cce1 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/core_library/java/lang/AutoboxedTypes.java @@ -0,0 +1,36 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This test class is in the java.lang namespace to trigger the hardcoded JVM restrictions that +// desugar --core_library works around +package java.lang; + +/** + * This class will be desugared with --core_library and then functionally tested by {@code + * DesugarCoreLibraryFunctionalTest} + */ +public class AutoboxedTypes { + /** + * Dummy functional interface for autoboxedTypeLambda to return without introducing a dependency + * on any other java.* classes. + */ + @FunctionalInterface + public interface Lambda { + String charAt(String s); + } + + public static Lambda autoboxedTypeLambda(Integer i) { + return n -> n.substring(i, i + 1); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/core_library/test/util/TestClassForStackMapFrame.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/core_library/test/util/TestClassForStackMapFrame.java new file mode 100644 index 0000000000..c39d7bc3bf --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/core_library/test/util/TestClassForStackMapFrame.java @@ -0,0 +1,56 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package test.util; + +/** Test input for b/36654936 */ +public class TestClassForStackMapFrame { + + /** + * This method caused cl/152199391 to fail due to stack map frame corruption. So it is to make + * sure the desugared version of this class still has correct stack map frames. + */ + public String joinIntegers(int integers) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < integers; i++) { + if (i > 0) { + builder.append(","); + } + builder.append(i); + builder.append('='); + Object value = i % 2 == 0 ? "Even" : "Odd"; + if (i % 2 == 0) { + builder.append(value); + } else { + builder.append(value); + } + } + return builder.toString(); + } + + /** + * This method triggers ASM bug 317785 . + * + * @return 20 + */ + public static int testInputForAsmBug317785() { + Integer x = 0; + for (int i = 0; i < 10; ++i) { + x++; + } + for (int i = 0; i < 10; ++i) { + x++; + } + return x; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/AnnotationsOfDefaultMethodsShouldBeKept.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/AnnotationsOfDefaultMethodsShouldBeKept.java new file mode 100644 index 0000000000..eb794885c6 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/AnnotationsOfDefaultMethodsShouldBeKept.java @@ -0,0 +1,43 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Test for b/38302860. The annotations of default methods should be kept after desugaring. */ +public class AnnotationsOfDefaultMethodsShouldBeKept { + + /** + * An interface, that has annotation, annotated abstract methods, and annotated default methods. + * After desugaring, all these annotations should remain in the interface. + */ + @SomeAnnotation(1) + public interface AnnotatedInterface { + + @SomeAnnotation(2) + void annotatedAbstractMethod(); + + @SomeAnnotation(3) + default void annotatedDefaultMethod() {} + } + + /** + * A simple annotation, used for testing. + */ + @Retention(value = RetentionPolicy.RUNTIME) + public @interface SomeAnnotation { + int value(); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/ConcreteDefaultInterfaceWithLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/ConcreteDefaultInterfaceWithLambda.java new file mode 100644 index 0000000000..0d9f70a76b --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/ConcreteDefaultInterfaceWithLambda.java @@ -0,0 +1,28 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import com.google.common.collect.ImmutableList; + +public class ConcreteDefaultInterfaceWithLambda implements DefaultInterfaceWithLambda { + static final String ONE = String.valueOf(1); + + @Override + public ImmutableList<String> digits() { + return ImmutableList.of(0, 2) + .stream() + .map(i -> i == 0 ? ONE : String.valueOf(i)) + .collect(ImmutableList.toImmutableList()); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/ConcreteOverridesDefaultWithLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/ConcreteOverridesDefaultWithLambda.java new file mode 100644 index 0000000000..cdcc5e9ce3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/ConcreteOverridesDefaultWithLambda.java @@ -0,0 +1,37 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import com.google.common.collect.ImmutableList; + +public class ConcreteOverridesDefaultWithLambda implements DefaultInterfaceWithLambda { + static final String TWO = String.valueOf(2); + static final String THREE = String.valueOf(3); + + @Override + public ImmutableList<String> defaultWithLambda() { + return ImmutableList.of(0, 3) + .stream() + .map(i -> i == 0 ? TWO : String.valueOf(i)) + .collect(ImmutableList.toImmutableList()); + } + + @Override + public ImmutableList<String> digits() { + return ImmutableList.of(0, 4) + .stream() + .map(i -> i == 0 ? THREE : String.valueOf(i)) + .collect(ImmutableList.toImmutableList()); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer.java new file mode 100644 index 0000000000..dbbf55569d --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer.java @@ -0,0 +1,172 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import com.google.common.collect.ImmutableList; +import java.util.ArrayList; +import java.util.List; + +/** + * Interfaces with default methods are intialized differently from those without default methods. + * When we load such an interface, its static intializer will be executed. + * + * <p>However, interfaces without default methods are only initialized when their non-primitive + * fields are accessed. + * + * <p>Test data for b/38255926 + */ +public class DefaultInterfaceMethodWithStaticInitializer { + + final List<String> initializationOrder = new ArrayList<>(); + + DefaultInterfaceMethodWithStaticInitializer register(Class<?> enclosingInterfaceClass) { + initializationOrder.add(enclosingInterfaceClass.getSimpleName()); + return this; + } + + private static long getTime() { + return 0; + } + + /** The simplest case: direct implementation. */ + public static class TestInterfaceSetOne { + + /** + * A writable field so that other interfaces can set it in their static initializers. + * (b/64290760) + */ + static long writableStaticField; + + static final DefaultInterfaceMethodWithStaticInitializer RECORDER = + new DefaultInterfaceMethodWithStaticInitializer(); + + /** With a default method, this interface should run clinit. */ + interface I1 { + long NOW = TestInterfaceSetOne.writableStaticField = getTime(); + DefaultInterfaceMethodWithStaticInitializer C = RECORDER.register(I1.class); + + default int defaultM1() { + return 1; + } + } + + /** With a default method, this interface should run clinit. */ + interface I2 { + long NOW = TestInterfaceSetOne.writableStaticField = getTime(); + DefaultInterfaceMethodWithStaticInitializer D = RECORDER.register(I2.class); + + default int defaultM2() { + return 10; + } + } + + /** Class to trigger the clinit. */ + public static class C implements I1, I2 { + public int sum() { + return defaultM1() + defaultM2(); + } + } + + public static ImmutableList<String> getExpectedInitializationOrder() { + return ImmutableList.of(I1.class.getSimpleName(), I2.class.getSimpleName()); + } + + public static ImmutableList<String> getRealInitializationOrder() { + return ImmutableList.copyOf(RECORDER.initializationOrder); + } + } + + /** Test for initializer execution order. */ + public static class TestInterfaceSetTwo { + + static final DefaultInterfaceMethodWithStaticInitializer RECORDER = + new DefaultInterfaceMethodWithStaticInitializer(); + + interface I1 { + DefaultInterfaceMethodWithStaticInitializer C = RECORDER.register(I1.class); + + default int defaultM1() { + return 1; + } + } + + interface I2 extends I1 { + DefaultInterfaceMethodWithStaticInitializer D = RECORDER.register(I2.class); + + default int defaultM2() { + return 2; + } + } + + /** + * Loading this class will trigger the execution of the static initializers of I2 and I1. + * However, I1 will be loaded first, as I2 extends I1. + */ + public static class C implements I2, I1 { + protected static final Integer INT_VALUE = Integer.valueOf(1); // To create a <clinit> + + public int sum() { + return defaultM1() + defaultM2(); + } + } + + public static ImmutableList<String> getExpectedInitializationOrder() { + return ImmutableList.of(I1.class.getSimpleName(), I2.class.getSimpleName()); + } + + public static ImmutableList<String> getRealInitializationOrder() { + return ImmutableList.copyOf(RECORDER.initializationOrder); + } + } + + /** Test: I2's <clinit> should not be executed. */ + public static class TestInterfaceSetThree { + static final DefaultInterfaceMethodWithStaticInitializer RECORDER = + new DefaultInterfaceMethodWithStaticInitializer(); + + interface I1 { + DefaultInterfaceMethodWithStaticInitializer C = RECORDER.register(I1.class); + + default int defaultM1() { + return 6; + } + } + + interface I2 extends I1 { + default int defaultM2() { + return 5; + } + } + + /** + * Loading this class will trigger the execution of the static initializers of I1. I2's will not + * execute. + */ + public static class C implements I2, I1 { + protected static final Integer INT_VALUE = Integer.valueOf(1); // To create a <clinit> + + public int sum() { + return defaultM1() + defaultM2(); + } + } + + public static ImmutableList<String> getExpectedInitializationOrder() { + return ImmutableList.of(I1.class.getSimpleName()); + } + + public static ImmutableList<String> getRealInitializationOrder() { + return ImmutableList.copyOf(RECORDER.initializationOrder); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithBridges.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithBridges.java new file mode 100644 index 0000000000..c5fb2d35e5 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithBridges.java @@ -0,0 +1,66 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** + * The base interface, which is generic, and has two default methods. These two default methods will + * introduce bridge methods in the child-interfaces + */ +interface GenericInterfaceWithDefaultMethod<T extends Number> { + default T copy(T t) { + return t; + } + + default Number getNumber() { + return 1; + } +} + +/** This interface generate two additional bridge methods */ +interface InterfaceWithDefaultAndBridgeMethods extends GenericInterfaceWithDefaultMethod<Integer> { + @Override + default Integer copy(Integer t) { + return GenericInterfaceWithDefaultMethod.super.copy(t); + } + + @Override + default Double getNumber() { + return 2.3d; + } +} + +/** A class implementing the interface. */ +class ClassWithDefaultAndBridgeMethods implements InterfaceWithDefaultAndBridgeMethods {} + +/** The client class that uses the interfaces and the class that implements the interfaces. */ +public class DefaultInterfaceWithBridges { + private final ClassWithDefaultAndBridgeMethods c = new ClassWithDefaultAndBridgeMethods(); + + public Integer copy(Integer i) { + return c.copy(i); + } + + public Number getNumber() { + return ((GenericInterfaceWithDefaultMethod) c).getNumber(); + } + + public Double getDouble() { + return c.getNumber(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Number copy(Number n) { + return ((GenericInterfaceWithDefaultMethod) c).copy(n); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithLambda.java new file mode 100644 index 0000000000..e97cae9561 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithLambda.java @@ -0,0 +1,33 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import com.google.common.collect.ImmutableList; + +public interface DefaultInterfaceWithLambda { + String ZERO = String.valueOf(0); + + public default ImmutableList<String> defaultWithLambda() { + return ImmutableList.of(0, 1) + .stream() + .map(i -> i == 0 ? ZERO : String.valueOf(i)) + .collect(ImmutableList.toImmutableList()); + } + + public default ImmutableList<String> defaultCallsInterfaceMethod() { + return digits(); + } + + public ImmutableList<String> digits(); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod.java new file mode 100644 index 0000000000..176eacece0 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod.java @@ -0,0 +1,78 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** Desugaring test input interface that includes a default method and a static method. */ +public interface FunctionWithDefaultMethod<T extends Number> extends Function<T, T> { + + @Override + T apply(T input); + + static <T extends Number> Function<T, Long> toLong() { + return input -> input.longValue(); + } + + default T twice(T input) { + return apply(apply(input)); + } + + /** Don't call this method from tests, it won't work since Desugar moves it! */ + static FunctionWithDefaultMethod<Integer> inc(int add) { + return input -> input + add; + } + + /** + * Implementation of {@link FunctionWithDefaultMethod} that overrides the default method. + * Also declares static methods the test uses to exercise the code in this file. + */ + public static class DoubleInts implements FunctionWithDefaultMethod<Integer> { + @Override + public Integer apply(Integer input) { + return 2 * input; + } + + @Override + public Integer twice(Integer input) { + return 5 * input; // deliberately wrong :) + } + + public static List<Long> add(List<Integer> ints, int add) { + return ints.stream().map(inc(add)).map(toLong()).collect(Collectors.toList()); + } + + public static FunctionWithDefaultMethod<Integer> doubleLambda() { + return input -> 2 * input; + } + + public static FunctionWithDefaultMethod<Integer> incTwice(int add) { + return inc(add)::twice; + } + + public static FunctionWithDefaultMethod<Integer> times5() { + return new DoubleInts2()::twice; + } + + public static Function<Integer, FunctionWithDefaultMethod<Integer>> incFactory() { + return FunctionWithDefaultMethod::inc; + } + } + + /** Empty subclass that explicitly implements the interface the superclass already implements. */ + public static class DoubleInts2 extends DoubleInts implements FunctionWithDefaultMethod<Integer> { + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/FunctionalInterfaceWithInitializerAndDefaultMethods.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/FunctionalInterfaceWithInitializerAndDefaultMethods.java new file mode 100644 index 0000000000..cde6c7b679 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/FunctionalInterfaceWithInitializerAndDefaultMethods.java @@ -0,0 +1,60 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** + * An interface that has a default method, and a non-empty static initializer. The initializer is + * NOT expected to run during desugaring. + */ +public interface FunctionalInterfaceWithInitializerAndDefaultMethods { + + ClassWithInitializer CONSTANT = new ClassWithInitializer(); + boolean BOOLEAN = getFalse(); + char CHAR = "hello".charAt(0); + byte BYTE = Byte.parseByte("0"); + short SHORT = Short.parseShort("0"); + int INT = Integer.parseInt("0"); + float FLOAT = Float.parseFloat("0"); + long LONG = Long.parseLong("0"); + double DOUBLE = Double.parseDouble("0"); + + int convert(); + + /** + * The default method ensures that the static initializer of this interface will be executed when + * the interface is loaded. + */ + default void defaultMethod() {} + + static boolean getFalse() { + return false; + } + + /** + * A class with a static initializer that has side effects (In this class, the printing to stdout) + */ + class ClassWithInitializer { + static { + System.out.println("THIS STRING IS NOT EXPECTED TO APPEAR IN THE OUTPUT OF DESUGAR!!!"); + } + + /** + * A lambda to trigger Desugar to load the interface {@link + * FunctionalInterfaceWithInitializerAndDefaultMethods} + */ + public FunctionalInterfaceWithInitializerAndDefaultMethods length(String s) { + return s::length; + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda.java new file mode 100644 index 0000000000..58396794bf --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda.java @@ -0,0 +1,97 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** An interface with default methods, lambdas, and generics */ +public interface GenericDefaultInterfaceWithLambda<T> { + + T getBaseValue(); + + T increment(T value); + + String toString(T value); + + public default ArrayList<T> toList(int bound) { + ArrayList<T> result = new ArrayList<>(); + if (bound == 0) { + return result; + } + result.add(getBaseValue()); + for (int i = 1; i < bound; ++i) { + result.add(increment(result.get(i - 1))); + } + return result; + } + + public default List<String> convertToStringList(List<T> list) { + return list.stream().map(this::toString).collect(Collectors.toList()); + } + + public default Function<Integer, ArrayList<T>> toListSupplier() { + return this::toList; + } + + /** The type parameter is concretized to {@link Number} */ + interface LevelOne<T extends Number> extends GenericDefaultInterfaceWithLambda<T> {} + + /** The type parameter is instantiated to {@link Integer} */ + interface LevelTwo extends LevelOne<Integer> { + + @Override + default Integer getBaseValue() { + return 0; + } + } + + /** An abstract class with no implementing methods. */ + abstract static class ClassOne implements LevelTwo {} + + /** A class for {@link Integer} */ + class ClassTwo extends ClassOne { + + @Override + public Integer increment(Integer value) { + return value + 1; + } + + @Override + public String toString(Integer value) { + return value.toString(); + } + } + + /** A class fo {@link Long} */ + class ClassThree implements LevelOne<Long> { + + @Override + public Long getBaseValue() { + return Long.valueOf(0); + } + + @Override + public Long increment(Long value) { + return value + 1; + } + + @Override + public String toString(Long value) { + return value.toString(); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod.java new file mode 100644 index 0000000000..622e6e51e7 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod.java @@ -0,0 +1,49 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Desugar test input interface that declares lambdas and method references in default and static + * interface methods. + */ +public interface InterfaceMethod { + public default List<String> defaultMethodReference(List<String> names) { + return names.stream().filter(this::startsWithS).collect(Collectors.toList()); + } + + public default List<String> staticMethodReference(List<String> names) { + return names.stream().filter(InterfaceMethod::startsWithA).collect(Collectors.toList()); + } + + public default List<String> lambdaCallsDefaultMethod(List<String> names) { + return names.stream().filter(s -> startsWithS(s)).collect(Collectors.toList()); + } + + public static boolean startsWithA(String input) { + return input.startsWith("A"); + } + + public default boolean startsWithS(String input) { + return input.startsWith("S"); + } + + /** + * Empty class implementing {@link InterfaceMethod} so the test can instantiate and call default + * methods. + */ + public static class Concrete implements InterfaceMethod {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod.java new file mode 100644 index 0000000000..b2d1beb7cb --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod.java @@ -0,0 +1,41 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** Interface for testing default methods overridden by extending interfaces. */ +public interface InterfaceWithDefaultMethod { + default int version() { + return 1; + } + + /** Interface that overrides {@link #version}. */ + public interface Redefine extends InterfaceWithDefaultMethod { + @Override + default int version() { + return 2; + } + } + + /** Class that implements both interfaces, supertype before subtype. */ + public static class Version2 implements InterfaceWithDefaultMethod, Redefine {} + + /** Base class that just implements {@link Redefine}. */ + static class Version2Base implements Redefine {} + + /** + * Subclass that implements an interface explicitly that the superclass also implements, + * but the superclass implements a more specific interface that overrides a defautl method. + */ + public static class AlsoVersion2 extends Version2Base implements InterfaceWithDefaultMethod {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDuplicateMethods.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDuplicateMethods.java new file mode 100644 index 0000000000..56217b8c08 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDuplicateMethods.java @@ -0,0 +1,45 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** + * Test for b/38308515. This interface has one instance method {@code m()} and one static method + * {@code m(InterfaceWithDuplicateMethods)}, which may cause Desugar to dump a companion class with + * duplicate method signatures. + */ +public interface InterfaceWithDuplicateMethods { + + /** + * In the companion class, this default method will be transformed to {@code int + * getZero(InterfaceWithDuplicateMethods)}, which has the same signature as the static interface + * method below. + */ + @SuppressWarnings("AmbiguousMethodReference") + default int getZero() { + return 0; + } + + /** Should not be called. Should only be called by the class {@link ClassWithDuplicateMethods} */ + @SuppressWarnings("AmbiguousMethodReference") + static int getZero(InterfaceWithDuplicateMethods i) { + return 1; + } + + /** This class implements the interface, and calls the static interface method. */ + class ClassWithDuplicateMethods implements InterfaceWithDuplicateMethods { + public int getZeroFromStaticInterfaceMethod() { + return InterfaceWithDuplicateMethods.getZero(this); + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges.java new file mode 100644 index 0000000000..ae9ef4f765 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges.java @@ -0,0 +1,56 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** + * The test classes for desugaring default methods. Desugar is not expected to generate companion + * classes for interfaces without default methods. The bridge methods are automatically generated by + * javac and put in the implementing classes. + * + * <p>NOTE: There should be NO companion class generated for this class. + */ +public interface Java7InterfaceWithBridges<T> { + T add(T value); + + /** Concretize T to {@link Number)} */ + interface LevelOne<T extends Number> extends Java7InterfaceWithBridges<T> { + @Override + T add(T value); + } + + /** Concretize to {@link Integer} */ + interface LevelTwo extends LevelOne<Integer> { + @Override + Integer add(Integer value); + } + + /** Empty abstract class. This class should have no bridge methods */ + abstract static class AbstractClassOne implements LevelTwo {} + + /** Implementing class. */ + static class ClassAddOne extends AbstractClassOne { + @Override + public Integer add(Integer value) { + return value + 1; + } + } + + /** Implementing class. */ + static class ClassAddTwo extends AbstractClassOne implements LevelTwo { + @Override + public Integer add(Integer value) { + return value + 2; + } + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/Named.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/Named.java new file mode 100644 index 0000000000..4c44ffd611 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/Named.java @@ -0,0 +1,72 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** Desugar test interface to test precedence of inherited methods over default methods. */ +public interface Named { + default String name() { + return getClass().getSimpleName(); + } + + /** Base class defining {@link #name} without implementing {@link Named}. */ + static class ExplicitNameBase { + private final String name; + + public ExplicitNameBase(String name) { + this.name = name; + } + + public String name() { + return name; + } + } + + /** Class whose base class implementes {@link #name}. */ + public static class ExplicitName extends ExplicitNameBase implements Named { + public ExplicitName(String name) { + super(name); + } + } + + /** Class that explicitly defers to the default method in {@link Named}. */ + public static class DefaultName extends ExplicitNameBase implements Named { + public DefaultName() { + super(null); + } + + @Override + public String name() { + return Named.super.name() + "-once"; + } + } + + /** Subclass of {@link DefaultName} that uses {@code super} as well. */ + public static class DefaultNameSubclass extends DefaultName { + @Override + public String name() { + return super.name() + "-twice"; + } + } + + /** Base class that declares {@link #name} abstract. */ + abstract static class AbstractNameBase { + public abstract String name(); + } + + /** + * Class that inherits {@link #name} abstract so subclasses must implement it despite default + * method in implemented interface. + */ + public abstract static class AbstractName extends AbstractNameBase implements Named {} +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/TwoInheritedDefaultMethods.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/TwoInheritedDefaultMethods.java new file mode 100644 index 0000000000..e9456ead99 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/TwoInheritedDefaultMethods.java @@ -0,0 +1,36 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +/** Desugar test class that explicitly calls default methods from two implemented interfaces. */ +public class TwoInheritedDefaultMethods implements Name1, Name2 { + @Override + public String name() { + return Name1.super.name() + ":" + Name2.super.name(); + } +} + +/** Test interface for {@link TwoInheritedDefaultMethods}. */ +interface Name1 { + default String name() { + return "One"; + } +} + +/** Test interface for {@link TwoInheritedDefaultMethods}. */ +interface Name2 { + default String name() { + return "Two"; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/VisibilityTestClass.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/VisibilityTestClass.java new file mode 100644 index 0000000000..b6bd37997e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/VisibilityTestClass.java @@ -0,0 +1,23 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8; + +import com.google.devtools.build.android.desugar.testdata.java8.subpackage.PublicInterface; + +/** + * Class that transitively implements a package-private interface in another package. Default + * method desugaring will need to make the default method defined in that interface publicly + * accessible. + */ +public class VisibilityTestClass implements PublicInterface {} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/subpackage/PackagePrivateInterface.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/subpackage/PackagePrivateInterface.java new file mode 100644 index 0000000000..9153c12264 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/subpackage/PackagePrivateInterface.java @@ -0,0 +1,33 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8.subpackage; + +/** Package-private interface with default method. */ +interface PackagePrivateInterface { + + /** + * This field makes this interface need to be initialized. With the default methods, when this + * interface is loaded, its initializer should also be run. + * + * <p>However, this test interface is different, as it is package-private. We need to to make sure + * the desugared code does not trigger IllegalAccessError. + * + * <p>See b/38255926. + */ + Integer VERSION = Integer.valueOf(0); + + default int m() { + return 42; + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/subpackage/PublicInterface.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/subpackage/PublicInterface.java new file mode 100644 index 0000000000..c3d51db446 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/java8/subpackage/PublicInterface.java @@ -0,0 +1,22 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.java8.subpackage; + +/** + * Public interface extending a package-private interface so classes in other packages can + * transitively implement a package-private interface. + * + * @see com.google.devtools.build.android.desugar.testdata.java8.VisibilityTestClass + */ +public interface PublicInterface extends PackagePrivateInterface {} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/separate/SeparateBaseClass.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/separate/SeparateBaseClass.java new file mode 100644 index 0000000000..a182c53329 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/separate/SeparateBaseClass.java @@ -0,0 +1,32 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.separate; + +import java.util.List; + +/** + * Test base class for testing method references to protected methods in another compilation. + */ +public class SeparateBaseClass<T> { + + private final List<T> list; + + protected SeparateBaseClass(List<T> list) { + this.list = list; + } + + protected boolean contains(T elem) { + return list.contains(elem); + } +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/separate/SeparateInterface.java b/src/test/java/com/google/devtools/build/android/desugar/testdata/separate/SeparateInterface.java new file mode 100644 index 0000000000..ce4b0584b9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/separate/SeparateInterface.java @@ -0,0 +1,21 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.testdata.separate; + +import java.util.function.Predicate; + +public interface SeparateInterface<T extends Number> extends Predicate<T> { + @Override + boolean test(T input); +} diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata/testresource.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata/testresource.txt new file mode 100644 index 0000000000..30d74d2584 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata/testresource.txt @@ -0,0 +1 @@ +test
\ No newline at end of file diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_core_library_jar_toc_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_core_library_jar_toc_golden.txt new file mode 100644 index 0000000000..cb0d40c97e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_core_library_jar_toc_golden.txt @@ -0,0 +1,18 @@ +META-INF/ +META-INF/MANIFEST.MF +com/ +com/google/ +com/google/devtools/ +com/google/devtools/build/ +com/google/devtools/build/android/ +com/google/devtools/build/android/desugar/ +com/google/devtools/build/android/desugar/testdata/ +com/google/devtools/build/android/desugar/testdata/testresource.txt +java/ +java/lang/ +java/lang/AutoboxedTypes$Lambda.class +java/lang/AutoboxedTypes.class +test/ +test/util/ +test/util/TestClassForStackMapFrame.class +java/lang/AutoboxedTypes$$Lambda$0.class
\ No newline at end of file diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_for_disabling_twr_with_large_minsdkversion_jar_toc_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_for_disabling_twr_with_large_minsdkversion_jar_toc_golden.txt new file mode 100644 index 0000000000..b7c3c25f38 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_for_disabling_twr_with_large_minsdkversion_jar_toc_golden.txt @@ -0,0 +1,65 @@ +META-INF/ +META-INF/MANIFEST.MF +com/ +com/google/ +com/google/devtools/ +com/google/devtools/build/ +com/google/devtools/build/android/ +com/google/devtools/build/android/desugar/ +com/google/devtools/build/android/desugar/testdata/ +com/google/devtools/build/android/desugar/testdata/testresource.txt +com/google/devtools/build/android/desugar/testdata/CaptureLambda.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$LongCmpFunc.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources$SimpleResource.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$Parser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$SpecializedParser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$Empty.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/Lambda$1.class +com/google/devtools/build/android/desugar/testdata/Lambda.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride.class +com/google/devtools/build/android/desugar/testdata/MethodReference$Transformer.class +com/google/devtools/build/android/desugar/testdata/MethodReference.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.class +com/google/devtools/build/android/desugar/testdata/SpecializedFunction.class +com/google/devtools/build/android/desugar/testdata/CaptureLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$3.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$4.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$5.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$6.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$7.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda$$Lambda$0.class
\ No newline at end of file diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_for_try_with_resources_jar_toc_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_for_try_with_resources_jar_toc_golden.txt new file mode 100644 index 0000000000..d03b121dcb --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_for_try_with_resources_jar_toc_golden.txt @@ -0,0 +1,72 @@ +META-INF/ +META-INF/MANIFEST.MF +com/ +com/google/ +com/google/devtools/ +com/google/devtools/build/ +com/google/devtools/build/android/ +com/google/devtools/build/android/desugar/ +com/google/devtools/build/android/desugar/testdata/ +com/google/devtools/build/android/desugar/testdata/testresource.txt +com/google/devtools/build/android/desugar/testdata/CaptureLambda.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$LongCmpFunc.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources$SimpleResource.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$Parser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$SpecializedParser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$Empty.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/Lambda$1.class +com/google/devtools/build/android/desugar/testdata/Lambda.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride.class +com/google/devtools/build/android/desugar/testdata/MethodReference$Transformer.class +com/google/devtools/build/android/desugar/testdata/MethodReference.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.class +com/google/devtools/build/android/desugar/testdata/SpecializedFunction.class +com/google/devtools/build/android/desugar/testdata/CaptureLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$3.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$4.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$5.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$6.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$7.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$AbstractDesugaringStrategy.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$ConcurrentWeakIdentityHashMap.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$ConcurrentWeakIdentityHashMap$WeakKey.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$MimicDesugaringStrategy.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$NullDesugaringStrategy.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$ReuseDesugaringStrategy.class
\ No newline at end of file diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_jar_test.sh b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_jar_test.sh new file mode 100755 index 0000000000..7aa7d19353 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_jar_test.sh @@ -0,0 +1,44 @@ +#!/bin/bash -e +# +# Copyright 2016 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test that lists the content of the desugared Jar and compares it to a golden +# file. This makes sure that output is deterministic and the resulting Jar +# doesn't contain any unwanted files, such as lambdas generated as part of +# running the desugaring tool. + +progdir="$(dirname "$0")" + +if [ -d "$TEST_TMPDIR" ]; then + # Running as part of blaze test + tmpdir="$TEST_TMPDIR" +else + # Manual run from command line + tmpdir="/tmp/test-$$" + mkdir "${tmpdir}" +fi + +if [ -d "$TEST_UNDECLARED_OUTPUTS_DIR" ]; then + # Running as part of blaze test: capture test output + output="$TEST_UNDECLARED_OUTPUTS_DIR" +else + # Manual run from command line: just write into temp dir + output="${tmpdir}" +fi + +JAVABASE=$3 +$JAVABASE/bin/jar tf "$1" >"${output}/actual_toc.txt" +# sorting can be removed when cl/145334839 is released +diff <(sort "$2") <(sort "${output}/actual_toc.txt") diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_jar_toc_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_jar_toc_golden.txt new file mode 100644 index 0000000000..91fc415f95 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_jar_toc_golden.txt @@ -0,0 +1,72 @@ +META-INF/ +META-INF/MANIFEST.MF +com/ +com/google/ +com/google/devtools/ +com/google/devtools/build/ +com/google/devtools/build/android/ +com/google/devtools/build/android/desugar/ +com/google/devtools/build/android/desugar/testdata/ +com/google/devtools/build/android/desugar/testdata/testresource.txt +com/google/devtools/build/android/desugar/testdata/CaptureLambda.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$LongCmpFunc.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources$SimpleResource.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$Parser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$SpecializedParser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$Empty.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/Lambda$1.class +com/google/devtools/build/android/desugar/testdata/Lambda.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride.class +com/google/devtools/build/android/desugar/testdata/MethodReference$Transformer.class +com/google/devtools/build/android/desugar/testdata/MethodReference.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.class +com/google/devtools/build/android/desugar/testdata/SpecializedFunction.class +com/google/devtools/build/android/desugar/testdata/CaptureLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$3.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$4.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$5.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$6.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$7.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$AbstractDesugaringStrategy.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$ConcurrentWeakIdentityHashMap.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$ConcurrentWeakIdentityHashMap$WeakKey.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$MimicDesugaringStrategy.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$NullDesugaringStrategy.class +com/google/devtools/build/android/desugar/runtime/ThrowableExtension$ReuseDesugaringStrategy.class diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_java8_jar_toc_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_java8_jar_toc_golden.txt new file mode 100644 index 0000000000..8664932324 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_java8_jar_toc_golden.txt @@ -0,0 +1,145 @@ +META-INF/ +META-INF/MANIFEST.MF +com/ +com/google/ +com/google/devtools/ +com/google/devtools/build/ +com/google/devtools/build/android/ +com/google/devtools/build/android/desugar/ +com/google/devtools/build/android/desugar/testdata/ +com/google/devtools/build/android/desugar/testdata/testresource.txt +com/google/devtools/build/android/desugar/testdata/CaptureLambda.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$LongCmpFunc.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources$SimpleResource.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$Parser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$SpecializedParser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$Empty.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/Lambda$1.class +com/google/devtools/build/android/desugar/testdata/Lambda.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride.class +com/google/devtools/build/android/desugar/testdata/MethodReference$Transformer.class +com/google/devtools/build/android/desugar/testdata/MethodReference.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.class +com/google/devtools/build/android/desugar/testdata/SpecializedFunction.class +com/google/devtools/build/android/desugar/testdata/java8/ +com/google/devtools/build/android/desugar/testdata/java8/AnnotationsOfDefaultMethodsShouldBeKept$AnnotatedInterface.class +com/google/devtools/build/android/desugar/testdata/java8/AnnotationsOfDefaultMethodsShouldBeKept$SomeAnnotation.class +com/google/devtools/build/android/desugar/testdata/java8/AnnotationsOfDefaultMethodsShouldBeKept.class +com/google/devtools/build/android/desugar/testdata/java8/ClassWithDefaultAndBridgeMethods.class +com/google/devtools/build/android/desugar/testdata/java8/ConcreteDefaultInterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/java8/ConcreteOverridesDefaultWithLambda.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetOne$C.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetOne$I1.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetOne$I2.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetOne.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetThree$C.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetThree$I1.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetThree$I2.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetThree.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetTwo$C.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetTwo$I1.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetTwo$I2.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer$TestInterfaceSetTwo.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceMethodWithStaticInitializer.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithBridges.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$DoubleInts.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$DoubleInts2.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionalInterfaceWithInitializerAndDefaultMethods$ClassWithInitializer.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionalInterfaceWithInitializerAndDefaultMethods.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$ClassOne.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$ClassThree.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$ClassTwo.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$LevelOne.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$LevelTwo.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/java8/GenericInterfaceWithDefaultMethod.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod$Concrete.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultAndBridgeMethods.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod$AlsoVersion2.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod$Redefine.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod$Version2.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod$Version2Base.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDefaultMethod.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDuplicateMethods$ClassWithDuplicateMethods.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceWithDuplicateMethods.class +com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges$AbstractClassOne.class +com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges$ClassAddOne.class +com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges$ClassAddTwo.class +com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges$LevelOne.class +com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges$LevelTwo.class +com/google/devtools/build/android/desugar/testdata/java8/Java7InterfaceWithBridges.class +com/google/devtools/build/android/desugar/testdata/java8/Name1.class +com/google/devtools/build/android/desugar/testdata/java8/Name2.class +com/google/devtools/build/android/desugar/testdata/java8/Named$AbstractName.class +com/google/devtools/build/android/desugar/testdata/java8/Named$AbstractNameBase.class +com/google/devtools/build/android/desugar/testdata/java8/Named$DefaultName.class +com/google/devtools/build/android/desugar/testdata/java8/Named$DefaultNameSubclass.class +com/google/devtools/build/android/desugar/testdata/java8/Named$ExplicitName.class +com/google/devtools/build/android/desugar/testdata/java8/Named$ExplicitNameBase.class +com/google/devtools/build/android/desugar/testdata/java8/Named.class +com/google/devtools/build/android/desugar/testdata/java8/TwoInheritedDefaultMethods.class +com/google/devtools/build/android/desugar/testdata/java8/VisibilityTestClass.class +com/google/devtools/build/android/desugar/testdata/java8/subpackage/ +com/google/devtools/build/android/desugar/testdata/java8/subpackage/PackagePrivateInterface.class +com/google/devtools/build/android/desugar/testdata/java8/subpackage/PublicInterface.class +com/google/devtools/build/android/desugar/testdata/CaptureLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$1$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/Lambda$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$3.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$4.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$5.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$6.class +com/google/devtools/build/android/desugar/testdata/MethodReference$$Lambda$7.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/ConcreteDefaultInterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/ConcreteOverridesDefaultWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/ConcreteOverridesDefaultWithLambda$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/java8/DefaultInterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$DoubleInts$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$DoubleInts$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$DoubleInts$$Lambda$2.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$DoubleInts$$Lambda$3.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionWithDefaultMethod$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/java8/FunctionalInterfaceWithInitializerAndDefaultMethods$ClassWithInitializer$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/GenericDefaultInterfaceWithLambda$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod$$Lambda$0.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod$$Lambda$1.class +com/google/devtools/build/android/desugar/testdata/java8/InterfaceMethod$$Lambda$2.class
\ No newline at end of file diff --git a/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_without_lambda_desugared_jar_toc_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_without_lambda_desugared_jar_toc_golden.txt new file mode 100644 index 0000000000..256760b32e --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/testdata_desugared_without_lambda_desugared_jar_toc_golden.txt @@ -0,0 +1,36 @@ +META-INF/ +META-INF/MANIFEST.MF +com/ +com/google/ +com/google/devtools/ +com/google/devtools/build/ +com/google/devtools/build/android/ +com/google/devtools/build/android/desugar/ +com/google/devtools/build/android/desugar/testdata/ +com/google/devtools/build/android/desugar/testdata/CaptureLambda.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare$LongCmpFunc.class +com/google/devtools/build/android/desugar/testdata/ClassCallingLongCompare.class +com/google/devtools/build/android/desugar/testdata/ClassCallingRequireNonNull.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources$SimpleResource.class +com/google/devtools/build/android/desugar/testdata/ClassUsingTryWithResources.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$Parser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction$SpecializedParser.class +com/google/devtools/build/android/desugar/testdata/ConcreteFunction.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$1.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference$Empty.class +com/google/devtools/build/android/desugar/testdata/ConstructorReference.class +com/google/devtools/build/android/desugar/testdata/GuavaLambda.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda$1.class +com/google/devtools/build/android/desugar/testdata/InnerClassLambda.class +com/google/devtools/build/android/desugar/testdata/InterfaceWithLambda.class +com/google/devtools/build/android/desugar/testdata/Lambda$1.class +com/google/devtools/build/android/desugar/testdata/Lambda.class +com/google/devtools/build/android/desugar/testdata/LambdaInOverride.class +com/google/devtools/build/android/desugar/testdata/MethodReference$Transformer.class +com/google/devtools/build/android/desugar/testdata/MethodReference.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceInSubclass.class +com/google/devtools/build/android/desugar/testdata/MethodReferenceSuperclass.class +com/google/devtools/build/android/desugar/testdata/OuterReferenceLambda.class +com/google/devtools/build/android/desugar/testdata/SpecializedFunction.class +com/google/devtools/build/android/desugar/testdata/testresource.txt
\ No newline at end of file |