blob: 81d71f5cd53d84a0b41e40b0bbf81d45723cf70a [file] [log] [blame]
// 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.dagger;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassTransformer;
import com.android.tools.r8.transformers.MethodTransformer;
import com.android.tools.r8.utils.DaggerUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DaggerBasicTestBase extends TestBase {
public static class CompiledCode {
public final Path compiledProgramNotDependingOnDagger;
public final Path compiledProgramDependingOnDagger;
public final Path daggerGeneratedSourceFiles;
public CompiledCode(
Path compiledProgramNotDependingOnDagger,
Path compiledProgramDependingOnDagger,
Path daggerGeneratedSourceFiles) {
this.compiledProgramNotDependingOnDagger = compiledProgramNotDependingOnDagger;
this.compiledProgramDependingOnDagger = compiledProgramDependingOnDagger;
this.daggerGeneratedSourceFiles = daggerGeneratedSourceFiles;
}
}
public static final List<String> javacTargets = ImmutableList.of("1.8", "11");
private static Map<String, CompiledCode> compilerCode;
private static void compile(
List<Path> sourceNotDependingOnDagger,
List<Path> sourceDependingOnDagger,
BiFunction<String, byte[], byte[]> transformer)
throws Exception {
ImmutableMap.Builder<String, CompiledCode> builder = ImmutableMap.builder();
for (String target : javacTargets) {
Path compiledProgramNotDependingOnDagger =
DaggerUtils.compileWithoutAnnotationProcessing(target, sourceNotDependingOnDagger);
// Use transformer on code not depending on Dagger.
if (transformer != null) {
compiledProgramNotDependingOnDagger =
ZipUtils.map(
compiledProgramNotDependingOnDagger,
getStaticTemp().newFolder().toPath().resolve("transformed.jar"),
(entry, bytes) ->
FileUtils.isClassFile(entry.getName())
? transformer.apply(
entry
.getName()
.substring(
0,
entry.getName().length() - FileUtils.CLASS_EXTENSION.length()),
bytes)
: bytes);
}
// Compile with Dagger annotation processor. The part of the program not relying on dagger
// generated types are passed as class files (potentially after transformation) and on command
// line for being included in annotation processing.
Path compiledProgramDependingOnDagger =
DaggerUtils.compileWithAnnotationProcessing(
target,
sourceDependingOnDagger,
ImmutableList.of(compiledProgramNotDependingOnDagger));
Path daggerGeneratedSourceFiles =
ZipUtils.filter(
compiledProgramDependingOnDagger,
getStaticTemp().newFolder().toPath().resolve("source.jar"),
entry -> entry.getName().endsWith(".java"));
// Check the generated Dagger source files.
Set<String> generatedFiles = new HashSet<>();
ZipUtils.iter(
daggerGeneratedSourceFiles, (entry, unused) -> generatedFiles.add(entry.getName()));
assertEquals(
ImmutableSet.of(
"basic/I1Impl1_Factory.java",
"basic/I2Impl1_Factory.java",
"basic/I3Impl1_Factory.java",
"basic/ModuleUsingProvides_I1Factory.java",
"basic/ModuleUsingProvides_I2Factory.java",
"basic/ModuleUsingProvides_I3Factory.java",
"basic/ModuleUsingProvides_Proxy.java",
"basic/DaggerMainComponentUsingBinds.java",
"basic/DaggerMainComponentUsingProvides.java"),
generatedFiles);
builder.put(
target,
new CompiledCode(
compiledProgramNotDependingOnDagger,
compiledProgramDependingOnDagger,
daggerGeneratedSourceFiles));
}
compilerCode = builder.build();
}
private static boolean sourceFileReferingDaggerGeneratedClasses(Path file) {
return file.getFileName().toString().startsWith("Main");
}
static void compileWithSingleton() throws Exception {
compile(
javaSourceFiles("basic", path -> !sourceFileReferingDaggerGeneratedClasses(path)),
javaSourceFiles("basic", DaggerBasicTestBase::sourceFileReferingDaggerGeneratedClasses),
DaggerBasicTestBase::transformAddSingleton);
}
static void compileWithoutSingleton() throws Exception {
compile(
javaSourceFiles("basic", path -> !sourceFileReferingDaggerGeneratedClasses(path)),
javaSourceFiles("basic", DaggerBasicTestBase::sourceFileReferingDaggerGeneratedClasses),
null);
}
private static byte[] transformAddSingleton(String binaryName, byte[] bytes) {
// Add @Singleton to the constructors used with @Bind.
if (binaryName.endsWith("Impl1")) {
return transformer(bytes, Reference.classFromBinaryName(binaryName))
.addClassTransformer(
new ClassTransformer() {
@Override
public void visitEnd() {
super.visitAnnotation("Ljavax/inject/Singleton;", true);
}
})
.transform();
}
// Add @Singleton to the methods annotated with @Provides.
if (binaryName.endsWith("ModuleUsingProvides")) {
return transformer(bytes, Reference.classFromBinaryName(binaryName))
.addMethodTransformer(
new MethodTransformer() {
@Override
public void visitEnd() {
super.visitAnnotation("Ljavax/inject/Singleton;", true);
}
})
.transform();
}
return bytes;
}
static List<Path> javaSourceFiles(String testDir, Predicate<Path> filter) throws Exception {
try (Stream<Path> walk =
Files.walk(Paths.get(ToolHelper.TESTS_DIR, "examplesDagger", testDir))) {
return walk.filter(filter).filter(FileUtils::isJavaFile).collect(Collectors.toList());
}
}
Collection<Path> getProgramFiles(String target) {
ImmutableList.Builder<Path> builder = ImmutableList.builder();
builder.add(compilerCode.get(target).compiledProgramDependingOnDagger);
builder.add(compilerCode.get(target).compiledProgramNotDependingOnDagger);
builder.addAll(DaggerUtils.getDaggerRuntime());
return builder.build();
}
}