| // Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| package com.android.tools.r8.desugar.desugaredlibrary; |
| |
| import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11; |
| |
| import com.android.tools.r8.LibraryDesugaringTestConfiguration; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.ZipUtils.ZipBuilder; |
| import java.lang.reflect.InvocationTargetException; |
| import java.nio.file.Path; |
| import java.util.List; |
| import java.util.function.Function; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameter; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| // Test of an API which is to be treated as a platform API. This is simulating an API which can be |
| // implemented by an OEM in an on-device APK containing the DEX code. The program will probe for the |
| // OEM class and if not found it will use a built-in program class implementing the interface. |
| // |
| // This is tested with and without library desugaring. |
| |
| // For context see b/229793269. |
| @RunWith(Parameterized.class) |
| public class PseudoPlatformApiTest extends DesugaredLibraryTestBase { |
| |
| @Parameter(0) |
| public TestParameters parameters; |
| |
| @Parameter(1) |
| public LibraryDesugaringSpecification libraryDesugaringSpecification; |
| |
| @Parameters(name = "{0}, spec: {1}") |
| public static List<Object[]> data() { |
| return buildParameters( |
| getTestParameters() |
| .withDexRuntimes() |
| .withApiLevelsStartingAtIncluding(AndroidApiLevel.P) |
| .build(), |
| getJdk8Jdk11()); |
| } |
| |
| private Path androidJarAdditions() throws Exception { |
| return ZipBuilder.builder(temp.newFile("additions_android.jar").toPath()) |
| .addFilesRelative( |
| ToolHelper.getClassPathForTests(), |
| ToolHelper.getClassFileForTestClass(PseudoPlatformInterface.class)) |
| .build(); |
| } |
| |
| private Path androidJarAdditionsDex() throws Exception { |
| // Build the OEM DEX files without library desugaring enabled and using min API 28. |
| return testForD8(parameters.getBackend()) |
| .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)) |
| .addProgramClasses(PseudoPlatformInterface.class) |
| .setMinApi(AndroidApiLevel.B) |
| .compile() |
| .writeToZip(); |
| } |
| |
| private Path oemDex() throws Exception { |
| // Build the OEM DEX files without library desugaring enabled and using min API 28. |
| return testForD8(parameters.getBackend()) |
| .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)) |
| // This is equivalent to adding the PseudoPlatformInterface to android.jar. |
| .addLibraryClasses(PseudoPlatformInterface.class) |
| .addProgramClasses(OemClass.class) |
| .setMinApi(AndroidApiLevel.P) |
| .compile() |
| .writeToZip(); |
| } |
| |
| @Test |
| public void testD8WithoutLibraryDesugaringOemClassNotPresent() throws Exception { |
| testForD8(parameters.getBackend()) |
| .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)) |
| .addLibraryFiles(androidJarAdditions()) |
| .addProgramClasses(ProgramClass.class) |
| .setMinApi(AndroidApiLevel.H_MR2) |
| .addRunClasspathFiles(androidJarAdditionsDex()) |
| .run(parameters.getRuntime(), ProgramClass.class) |
| .assertSuccessWithOutputLines("DEFAULT-X", "Y-DEFAULT"); |
| } |
| |
| @Test |
| public void testD8WithoutLibraryDesugaringOemClassPresent() throws Exception { |
| testForD8(parameters.getBackend()) |
| .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)) |
| .addLibraryFiles(androidJarAdditions()) |
| .addProgramClasses(ProgramClass.class) |
| .setMinApi(AndroidApiLevel.H_MR2) |
| .addRunClasspathFiles(androidJarAdditionsDex()) |
| .addRunClasspathFiles(oemDex()) |
| .run(parameters.getRuntime(), ProgramClass.class) |
| .assertSuccessWithOutputLines("OEM-X", "Y-OEM"); |
| } |
| |
| @Test |
| public void testD8WithLibraryDesugaringOemClassNotPresent() throws Exception { |
| // Enable library desugaring with an effective min API of 1. |
| testForD8(parameters.getBackend()) |
| .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)) |
| .addLibraryFiles(androidJarAdditions()) |
| .addProgramClasses(ProgramClass.class) |
| .setMinApi(AndroidApiLevel.H_MR2) |
| .enableCoreLibraryDesugaring( |
| LibraryDesugaringTestConfiguration.forSpecification( |
| libraryDesugaringSpecification.getSpecification())) |
| .addRunClasspathFiles(androidJarAdditionsDex()) |
| .addRunClasspathFiles( |
| getNonShrunkDesugaredLib( |
| AndroidApiLevel.H_MR2, parameters.getBackend(), libraryDesugaringSpecification)) |
| .run(parameters.getRuntime(), ProgramClass.class) |
| .assertSuccessWithOutputLines("DEFAULT-X", "Y-DEFAULT"); |
| } |
| |
| @Test |
| public void testD8WithLibraryDesugaringOemClassPresent() throws Exception { |
| // Enable library desugaring with an effective min API of 1. |
| testForD8(parameters.getBackend()) |
| .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)) |
| .addLibraryFiles(androidJarAdditions()) |
| .addProgramClasses(ProgramClass.class) |
| .setMinApi(AndroidApiLevel.H_MR2) |
| .enableCoreLibraryDesugaring( |
| LibraryDesugaringTestConfiguration.forSpecification( |
| libraryDesugaringSpecification.getSpecification())) |
| .addRunClasspathFiles(androidJarAdditionsDex()) |
| .addRunClasspathFiles(oemDex()) |
| .addRunClasspathFiles( |
| getNonShrunkDesugaredLib( |
| AndroidApiLevel.H_MR2, parameters.getBackend(), libraryDesugaringSpecification)) |
| .run(parameters.getRuntime(), ProgramClass.class) |
| .assertSuccessWithOutputLines("OEM-X", "Y-OEM"); |
| } |
| |
| interface PseudoPlatformInterface { |
| Function<String, String> api(Function<String, String> fn); |
| } |
| |
| static class OemClass implements PseudoPlatformInterface { |
| public Function<String, String> api(Function<String, String> fn) { |
| System.out.println(fn.apply("OEM-")); |
| return (v) -> v + "-OEM"; |
| } |
| } |
| |
| static class ProgramClass implements PseudoPlatformInterface { |
| public Function<String, String> api(Function<String, String> fn) { |
| System.out.println(fn.apply("DEFAULT-")); |
| return (v) -> v + "-DEFAULT"; |
| } |
| |
| public static void main(String[] args) { |
| PseudoPlatformInterface pseudoPlatformInterface = null; |
| try { |
| Class<?> oemClass = |
| Class.forName( |
| "com.android.tools.r8.desugar.desugaredlibrary.PseudoPlatformApiTest$OemClass"); |
| pseudoPlatformInterface = |
| (PseudoPlatformInterface) oemClass.getDeclaredConstructor().newInstance(); |
| } catch (ClassNotFoundException |
| | NoSuchMethodException |
| | InstantiationException |
| | InvocationTargetException |
| | IllegalAccessException e) { |
| // Could not load OEM class use built-in program class. |
| pseudoPlatformInterface = new ProgramClass(); |
| } |
| Function<String, String> fn = (v) -> v + "X"; |
| System.out.println(pseudoPlatformInterface.api(fn).apply("Y")); |
| } |
| } |
| } |