blob: a3bc5b48727e79a1ed3757d003d269c0841bb2f2 [file] [log] [blame]
// Copyright (c) 2024, 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.optimize.serviceloader;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK_TR;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.R8_L8SHRINK_TR;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.Serializable;
import java.time.LocalTime;
import java.time.chrono.AbstractChronology;
import java.time.chrono.ChronoLocalDate;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.ChronoPeriod;
import java.time.chrono.Chronology;
import java.time.chrono.Era;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
import java.time.temporal.ValueRange;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
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;
@RunWith(Parameterized.class)
public class ServiceLoaderDesugaredLibraryTest extends DesugaredLibraryTestBase {
@Parameter(0)
public TestParameters parameters;
@Parameter(1)
public LibraryDesugaringSpecification libraryDesugaringSpecification;
@Parameter(2)
public CompilationSpecification compilationSpecification;
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
ImmutableList.of(JDK11),
ImmutableList.of(D8_L8SHRINK_TR, R8_L8SHRINK_TR));
}
private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true", "1", "1");
private static final String servicesPath = "META-INF/services/" + Chronology.class.getTypeName();
private static final String servicesPathRewritten = servicesPath.replace("java.", "j$.");
private static final String servicesFile =
StringUtils.lines(SimpleChronology.class.getTypeName());
private void configureR8(R8TestBuilder<?> builder) {
// When testing R8 add the META-INF/services to the input to apply rewriting.
builder
.addDataEntryResources(
DataEntryResource.fromBytes(servicesFile.getBytes(), servicesPath, Origin.unknown()))
.addKeepClassAndMembersRulesWithAllowObfuscation(SimpleChronology.class);
}
private void configureD8(D8TestBuilder builder, boolean useJDollarType) {
// When testing D8 add a manually rewritten META-INF/services to the output.
try {
builder.addRunClasspathFiles(
ZipBuilder.builder(temp.newFile("services.jar").toPath())
.addBytes(
useJDollarType ? servicesPathRewritten : servicesPath, servicesFile.getBytes())
.build());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
public void testWithDesugaredLibrary() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.applyIfD8TestBuilder(
b -> configureD8(b, libraryDesugaringSpecification.hasTimeDesugaring(parameters)))
.applyIfR8TestBuilder(this::configureR8)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void testD8WithoutDesugaredLibrary() throws Throwable {
parameters.assumeR8TestParameters();
assumeTrue(compilationSpecification == D8_L8SHRINK_TR);
testForD8(parameters.getBackend())
.addLibraryFiles(ToolHelper.getAndroidJar(apiLevelWithJavaTime()))
.addInnerClasses(getClass())
.apply(b -> configureD8(b, false))
.setMinApi(parameters)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
runtimeWithJavaTime(parameters),
r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
SingleTestRunResult::assertFailure);
}
@Test
public void testR8WithoutDesugaredLibrary() throws Throwable {
parameters.assumeR8TestParameters();
assumeTrue(compilationSpecification == D8_L8SHRINK_TR);
testForR8(parameters.getBackend())
.addLibraryFiles(ToolHelper.getAndroidJar(apiLevelWithJavaTime()))
.addInnerClasses(getClass())
.apply(this::configureR8)
.setMinApi(parameters)
.addKeepMainRule(TestClass.class)
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
runtimeWithJavaTime(parameters),
r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
SingleTestRunResult::assertFailure);
}
static class TestClass {
public static void main(String[] args) {
Chronology chronology = Chronology.of("Simple");
System.out.println(chronology instanceof SimpleChronology);
ChronoLocalDate simpleDate = chronology.date(1, 1, 1);
System.out.println(simpleDate instanceof SimpleDate);
System.out.println(chronology.range(ChronoField.DAY_OF_MONTH).getMinimum());
System.out.println(chronology.range(ChronoField.DAY_OF_MONTH).getMaximum());
}
}
public static final class SimpleChronology extends AbstractChronology {
public static final SimpleChronology INSTANCE = new SimpleChronology();
public SimpleChronology() {}
@Override
public String getId() {
return "Simple";
}
@Override
public String getCalendarType() {
return "simple";
}
@Override
public SimpleDate date(int prolepticYear, int month, int dayOfMonth) {
return new SimpleDate();
}
@Override
public SimpleDate dateYearDay(int prolepticYear, int dayOfYear) {
return new SimpleDate();
}
@Override
public SimpleDate dateEpochDay(long epochDay) {
return new SimpleDate();
}
@Override
public SimpleDate date(TemporalAccessor dateTime) {
if (dateTime instanceof SimpleDate) {
return (SimpleDate) dateTime;
}
return new SimpleDate();
}
@Override
public boolean isLeapYear(long prolepticYear) {
return false;
}
@Override
public int prolepticYear(Era era, int yearOfEra) {
throw new UnsupportedOperationException();
}
@Override
public Era eraOf(int eraValue) {
return SimpleEra.ERA;
}
@Override
public List<Era> eras() {
return Arrays.asList(SimpleEra.values());
}
@Override
public ValueRange range(ChronoField field) {
return ValueRange.of(1, 1);
}
}
public static class SimpleDate implements ChronoLocalDate, Serializable {
public SimpleDate() {}
@Override
public Chronology getChronology() {
return null;
}
@Override
public Era getEra() {
return SimpleEra.ERA;
}
@Override
public boolean isLeapYear() {
return false;
}
@Override
public int lengthOfMonth() {
return 0;
}
@Override
public int lengthOfYear() {
return 0;
}
@Override
public boolean isSupported(TemporalField field) {
return true;
}
@Override
public ValueRange range(TemporalField field) {
return ValueRange.of(1, 1);
}
@Override
public int get(TemporalField field) {
return 1;
}
@Override
public long getLong(TemporalField field) {
return 1;
}
@Override
public boolean isSupported(TemporalUnit unit) {
return true;
}
@Override
public ChronoLocalDate with(TemporalAdjuster adjuster) {
return new SimpleDate();
}
@Override
public ChronoLocalDate with(TemporalField field, long newValue) {
return new SimpleDate();
}
@Override
public ChronoLocalDate plus(TemporalAmount amount) {
return new SimpleDate();
}
@Override
public ChronoLocalDate plus(long amountToAdd, TemporalUnit unit) {
return new SimpleDate();
}
@Override
public ChronoLocalDate minus(TemporalAmount amount) {
return new SimpleDate();
}
@Override
public ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) {
return new SimpleDate();
}
@Override
public <R> R query(TemporalQuery<R> query) {
throw new UnsupportedOperationException();
}
@Override
public Temporal adjustInto(Temporal temporal) {
throw new UnsupportedOperationException();
}
@Override
public long until(Temporal endExclusive, TemporalUnit unit) {
return 0;
}
@Override
public ChronoPeriod until(ChronoLocalDate endDateExclusive) {
return null;
}
@Override
public String format(DateTimeFormatter formatter) {
return "1";
}
@Override
public ChronoLocalDateTime<?> atTime(LocalTime localTime) {
throw new UnsupportedOperationException();
}
@Override
public long toEpochDay() {
throw new UnsupportedOperationException();
}
@Override
public int compareTo(ChronoLocalDate other) {
return 0;
}
@Override
public boolean isAfter(ChronoLocalDate other) {
return false;
}
@Override
public boolean isBefore(ChronoLocalDate other) {
return false;
}
@Override
public boolean isEqual(ChronoLocalDate other) {
return true;
}
}
public enum SimpleEra implements Era {
ERA;
@Override
public int getValue() {
return 0;
}
@Override
public boolean isSupported(TemporalField field) {
return Era.super.isSupported(field);
}
@Override
public ValueRange range(TemporalField field) {
return ValueRange.of(1, 1);
}
@Override
public int get(TemporalField field) {
return 1;
}
@Override
public long getLong(TemporalField field) {
return 1;
}
@Override
public <R> R query(TemporalQuery<R> query) {
throw new UnsupportedOperationException();
}
@Override
public Temporal adjustInto(Temporal temporal) {
throw new UnsupportedOperationException();
}
@Override
public String getDisplayName(TextStyle style, Locale locale) {
return "1";
}
}
}