blob: c95c372eff50a64116b97036d303b017116b182f [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.kotlin.lambda.b148525512;
import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class B148525512 extends KotlinTestBase {
private static final Package pkg = B148525512.class.getPackage();
private static final String kotlinTestClassesPackage = pkg.getName();
private static final String baseKtClassName = kotlinTestClassesPackage + ".BaseKt";
private static final String featureKtClassNamet = kotlinTestClassesPackage + ".FeatureKt";
private static final String baseClassName = kotlinTestClassesPackage + ".Base";
private static final Map<KotlinTargetVersion, Path> kotlinBaseClasses = new HashMap<>();
private static final Map<KotlinTargetVersion, Path> kotlinFeatureClasses = new HashMap<>();
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0},{1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withDexRuntimes().withAllApiLevels().build(),
KotlinTargetVersion.values());
}
public B148525512(TestParameters parameters, KotlinTargetVersion targetVersion) {
super(targetVersion);
this.parameters = parameters;
}
@ClassRule public static TemporaryFolder classTemp = new TemporaryFolder();
@BeforeClass
public static void compileKotlin() throws Exception {
// Compile the base Kotlin with the FeatureAPI Java class on classpath.
Path featureApiJar = classTemp.newFile("feature_api.jar").toPath();
writeClassesToJar(featureApiJar, FeatureAPI.class);
for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
Path ktBaseClasses =
kotlinc(KOTLINC, targetVersion)
.addClasspathFiles(featureApiJar)
.addSourceFiles(getKotlinFileInTestPackage(pkg, "base"))
.compile();
kotlinBaseClasses.put(targetVersion, ktBaseClasses);
// Compile the feature Kotlin code with the base classes on classpath.
Path ktFeatureClasses =
kotlinc(KOTLINC, targetVersion)
.addClasspathFiles(ktBaseClasses)
.addSourceFiles(getKotlinFileInTestPackage(pkg, "feature"))
.compile();
kotlinFeatureClasses.put(targetVersion, ktFeatureClasses);
}
}
private void checkLambdaGroups(CodeInspector inspector) {
List<FoundClassSubject> lambdaGroups =
inspector.allClasses().stream()
.filter(clazz -> clazz.getOriginalName().contains("LambdaGroup"))
.collect(Collectors.toList());
assertEquals(1, lambdaGroups.size());
MethodSubject invokeMethod = lambdaGroups.get(0).uniqueMethodWithName("invoke");
assertThat(invokeMethod, isPresent());
// The lambda group has 2 captures which capture "Base".
assertEquals(
2,
invokeMethod
.streamInstructions()
.filter(InstructionSubject::isCheckCast)
.filter(
instruction ->
instruction.asCheckCast().getType().toSourceString().contains("Base"))
.count());
// The lambda group has no captures which capture "Feature" (lambdas in the feature are not
// in this lambda group).
assertTrue(
invokeMethod
.streamInstructions()
.filter(InstructionSubject::isCheckCast)
.noneMatch(
instruction ->
instruction.asCheckCast().getType().toSourceString().contains("Feature")));
}
@Test
public void test() throws Exception {
Path featureCode = temp.newFile("feature.zip").toPath();
R8TestCompileResult compileResult =
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getKotlinStdlibJar())
.addProgramFiles(kotlinBaseClasses.get(targetVersion))
.addProgramClasses(FeatureAPI.class)
.addKeepMainRule(baseKtClassName)
.addKeepClassAndMembersRules(baseClassName)
.addKeepClassAndMembersRules(featureKtClassNamet)
.addKeepClassAndMembersRules(FeatureAPI.class)
.setMinApi(parameters.getApiLevel())
.noMinification() // The check cast inspection above relies on original names.
.addFeatureSplit(
builder ->
builder
.addProgramResourceProvider(
ArchiveResourceProvider.fromArchive(
kotlinFeatureClasses.get(targetVersion), true))
.setProgramConsumer(new ArchiveConsumer(featureCode, false))
.build())
.compile()
.inspect(this::checkLambdaGroups);
// Run the code without the feature code present.
compileResult
.run(parameters.getRuntime(), baseKtClassName)
.assertSuccessWithOutputLines("1", "2");
// Run the code with the feature code present.
compileResult
.addRunClasspathFiles(featureCode)
.run(parameters.getRuntime(), baseKtClassName)
.assertSuccessWithOutputLines("1", "2", "3", "4");
}
}