blob: 9e116da8d256c196e9a1660313d030088f939723 [file] [log] [blame]
// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dexsplitter;
import static com.android.tools.r8.rewrite.ServiceLoaderRewritingTest.getServiceLoaderLoads;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ServiceLoader;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class R8FeatureSplitServiceLoaderTest extends SplitterTestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
public R8FeatureSplitServiceLoaderTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testR8AllServiceConfigurationInBaseAndNoTypesInFeatures() throws Exception {
Path base = temp.newFile("base.zip").toPath();
Path feature1Path = temp.newFile("feature1.zip").toPath();
testForR8(parameters.getBackend())
.addProgramClasses(Base.class, I.class, Feature1I.class, Feature2I.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Base.class)
.addFeatureSplit(
builder -> simpleSplitProvider(builder, feature1Path, temp, Feature3Dummy.class))
.addDataEntryResources(
DataEntryResource.fromBytes(
StringUtils.lines(Feature1I.class.getTypeName(), Feature2I.class.getTypeName())
.getBytes(),
"META-INF/services/" + I.class.getTypeName(),
Origin.unknown()))
.compile()
.inspect(
inspector -> {
assertEquals(0, getServiceLoaderLoads(inspector, Base.class));
})
.writeToZip(base)
.run(parameters.getRuntime(), Base.class)
.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
}
@Test
public void testR8AllServiceConfigurationInBase() throws Exception {
Path base = temp.newFile("base.zip").toPath();
Path feature1Path = temp.newFile("feature1.zip").toPath();
Path feature2Path = temp.newFile("feature2.zip").toPath();
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClasses(Base.class, I.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Base.class)
.addFeatureSplit(
builder -> simpleSplitProvider(builder, feature1Path, temp, Feature1I.class))
.addFeatureSplit(
builder -> simpleSplitProvider(builder, feature2Path, temp, Feature2I.class))
.addDataEntryResources(
DataEntryResource.fromBytes(
StringUtils.lines(Feature1I.class.getTypeName(), Feature2I.class.getTypeName())
.getBytes(),
"META-INF/services/" + I.class.getTypeName(),
Origin.unknown()))
.compile()
.inspect(
inspector -> {
assertEquals(1, getServiceLoaderLoads(inspector, Base.class));
})
.writeToZip(base)
.addRunClasspathFiles(feature1Path, feature2Path)
.run(parameters.getRuntime(), Base.class);
// TODO(b/160888348): This is failing on 7.0
if (parameters.getRuntime().isDex()
&& parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
runResult.assertFailureWithErrorThatMatches(containsString("ServiceConfigurationError"));
} else {
runResult.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
}
}
@Test
public void testR8AllLoaded() throws Exception {
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClasses(Base.class, I.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Base.class)
.addFeatureSplitWithResources(
ImmutableList.of(
new Pair<>(
"META-INF/services/" + I.class.getTypeName(),
StringUtils.lines(Feature1I.class.getTypeName()))),
Feature1I.class)
.addFeatureSplitWithResources(
ImmutableList.of(
new Pair<>(
"META-INF/services/" + I.class.getTypeName(),
StringUtils.lines(Feature2I.class.getTypeName()))),
Feature2I.class)
.compile()
.inspect(
baseInspector -> assertEquals(1, getServiceLoaderLoads(baseInspector, Base.class)),
feature1Inspector ->
assertThat(feature1Inspector.clazz(Feature1I.class), isPresent()),
feature2Inspector ->
assertThat(feature2Inspector.clazz(Feature2I.class), isPresent()))
.addFeatureSplitsToRunClasspathFiles()
.run(parameters.getRuntime(), Base.class);
// TODO(b/160888348): This is failing on 7.0
if (parameters.getRuntime().isDex()
&& parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
runResult.assertFailureWithErrorThatMatches(containsString("ServiceConfigurationError"));
} else {
runResult.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
}
}
@Test
public void testR8WithServiceFileInSeparateFeature() throws Exception {
R8TestRunResult runResult =
testForR8(parameters.getBackend())
.addProgramClasses(Base.class, I.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Base.class)
.addFeatureSplit(Feature1I.class)
.addFeatureSplit(Feature2I.class)
.addFeatureSplitWithResources(
ImmutableList.of(
new Pair<>(
"META-INF/services/" + I.class.getTypeName(),
StringUtils.lines(
Feature1I.class.getTypeName(), Feature2I.class.getTypeName()))),
Feature3Dummy.class)
.compile()
.inspect(
baseInspector -> assertEquals(1, getServiceLoaderLoads(baseInspector, Base.class)),
feature1Inspector ->
assertThat(feature1Inspector.clazz(Feature1I.class), isPresent()),
feature2Inspector ->
assertThat(feature2Inspector.clazz(Feature2I.class), isPresent()),
feature3Inspector -> assertTrue(feature3Inspector.allClasses().isEmpty()))
.addFeatureSplitsToRunClasspathFiles()
.run(parameters.getRuntime(), Base.class);
// TODO(b/160888348): This is failing on 7.0
if (parameters.getRuntime().isDex()
&& parameters.getRuntime().asDex().getVm().getVersion() == Version.V7_0_0) {
runResult.assertFailureWithErrorThatMatches(containsString("ServiceConfigurationError"));
} else {
runResult.assertSuccessWithOutputLines("Feature1I.foo()", "Feature2I.foo()");
}
}
@Test
public void testR8OnlyFeature2() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(Base.class, I.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Base.class)
.addFeatureSplitWithResources(
ImmutableList.of(
new Pair<>(
"META-INF/services/" + I.class.getTypeName(),
StringUtils.lines(Feature1I.class.getTypeName()))),
Feature1I.class)
.addFeatureSplitWithResources(
ImmutableList.of(
new Pair<>(
"META-INF/services/" + I.class.getTypeName(),
StringUtils.lines(Feature2I.class.getTypeName()))),
Feature2I.class)
.compile()
.inspect(
baseInspector -> assertEquals(1, getServiceLoaderLoads(baseInspector, Base.class)),
feature1Inspector -> assertThat(feature1Inspector.clazz(Feature1I.class), isPresent()),
feature2Inspector -> assertThat(feature2Inspector.clazz(Feature2I.class), isPresent()))
.apply(compileResult -> compileResult.addRunClasspathFiles(compileResult.getFeature(1)))
.run(parameters.getRuntime(), Base.class)
// TODO(b/160889305): This should work.
.assertFailureWithErrorThatMatches(containsString("java.lang.ClassNotFoundException"));
}
public interface I {
void foo();
}
public static class Base {
public static void main(String[] args) {
for (I i : ServiceLoader.load(I.class, null)) {
i.foo();
}
}
}
public static class Feature1I implements I {
@Override
public void foo() {
System.out.println("Feature1I.foo()");
}
}
public static class Feature2I implements I {
@Override
public void foo() {
System.out.println("Feature2I.foo()");
}
}
public static class Feature3Dummy {}
}