blob: fc59c926bcad6b75e4ee4d102afc75cd5e68049b [file] [log] [blame]
/*
* Copyright (C) 2020 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dagger.internal.codegen;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;
import java.util.Arrays;
import javax.tools.JavaFileObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ComponentShardTest {
private static final int BINDINGS_PER_SHARD = 10;
@Test
public void testNewShardCreated() {
// Create 2N + 1 bindings: N in DaggerTestComponent, N in Shard1, and 1 in Shard2
int numBindings = 2 * BINDINGS_PER_SHARD + 1;
ImmutableList.Builder<JavaFileObject> javaFileObjects = ImmutableList.builder();
ImmutableList.Builder<String> entryPoints = ImmutableList.builder();
for (int i = 0; i < numBindings; i++) {
String bindingName = "Binding" + i;
entryPoints.add(String.format("%1$s get%1$s();", bindingName));
entryPoints.add(String.format("Provider<%1$s> get%1$sProvider();", bindingName));
// Add dependencies between main component and shard1: 9 -> 10 -> Provider<9>
// Add dependencies between shard1 and shard2: 19 -> 20 -> Provider<19>
switch (i) {
case 9:
javaFileObjects.add(createBinding(bindingName, "Binding10 dep"));
break;
case 10:
javaFileObjects.add(createBinding(bindingName, "Provider<Binding9> dep"));
break;
case 19:
javaFileObjects.add(createBinding(bindingName, "Binding20 dep"));
break;
case 20:
javaFileObjects.add(createBinding(bindingName, "Provider<Binding19> dep"));
break;
default:
javaFileObjects.add(createBinding(bindingName));
break;
}
}
javaFileObjects.add(createComponent(entryPoints.build()));
// This generated component shows a couple things:
// 1. Binding locations:
// * Binding #9 belongs to DaggerTestComponent
// * Binding #10 belongs to Shard1
// * Binding #20 belongs to Shard2
// 2. DaggerTestComponent entry point methods:
// * Binding #9 implementation is inlined DaggerTestComponent.
// * Binding #10 implementation is delegated to Shard1.
// * Binding #20 implementation is delegated to Shard2.
// 3. Dependencies between component and shard:
// * Binding #9 in DaggerTestComponent depends on #10 in Shard1.
// * Binding #10 in Shard1 depends on Provider<#9> in DaggerTestComponent.
// 4. Dependencies between shard and shard:
// * Binding #19 in Shard1 depends on #20 in Shard2.
// * Binding #20 in Shard2 depends on Provider<#19> in Shard1.
JavaFileObject generatedComponent =
JavaFileObjects.forSourceLines(
"dagger.internal.codegen.DaggerTestComponent",
"package dagger.internal.codegen;",
GENERATED_CODE_ANNOTATIONS,
"final class DaggerTestComponent implements TestComponent {",
" private final Shard1 shard1 = new Shard1();",
"",
" private volatile Provider<Binding9> binding9Provider;",
"",
" private volatile Object binding9 = new MemoizedSentinel();",
"",
" @Override",
" public Binding9 getBinding9() {",
" Object local = binding9;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
" local = binding9;",
" if (local instanceof MemoizedSentinel) {",
" local = new Binding9(DaggerTestComponent.this.shard1.binding10());",
" binding9 = DoubleCheck.reentrantCheck(binding9, local);",
" }",
" }",
" }",
" return (Binding9) local;",
" }",
"",
" @Override",
" public Provider<Binding9> getBinding9Provider() {",
" Object local = binding9Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(9);",
" binding9Provider = (Provider<Binding9>) local;",
" }",
" return (Provider<Binding9>) local;",
" }",
"",
" @Override",
" public Binding10 getBinding10() {",
" return DaggerTestComponent.this.shard1.binding10();",
" }",
"",
" @Override",
" public Provider<Binding10> getBinding10Provider() {",
" return DaggerTestComponent.this.shard1.binding10Provider();",
" }",
"",
" @Override",
" public Binding20 getBinding20() {",
" return DaggerTestComponent.this.shard2.binding20();",
" }",
"",
" @Override",
" public Provider<Binding20> getBinding20Provider() {",
" return DaggerTestComponent.this.shard2.binding20Provider();",
" }",
"",
" private final class Shard1 {",
" private volatile Object binding10 = new MemoizedSentinel();",
"",
" private volatile Provider<Binding10> binding10Provider;",
"",
" private volatile Provider<Binding19> binding19Provider;",
"",
" private volatile Object binding19 = new MemoizedSentinel();",
"",
" private Binding10 binding10() {",
" Object local = binding10;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
" local = binding10;",
" if (local instanceof MemoizedSentinel) {",
" local = new Binding10(",
" DaggerTestComponent.this.getBinding9Provider());",
" binding10 = DoubleCheck.reentrantCheck(binding10, local);",
" }",
" }",
" }",
" return (Binding10) local;",
" }",
"",
" private Provider<Binding10> binding10Provider() {",
" Object local = binding10Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(10);",
" binding10Provider = (Provider<Binding10>) local;",
" }",
" return (Provider<Binding10>) local;",
" }",
"",
" private Provider<Binding19> binding19Provider() {",
" Object local = binding19Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(19);",
" binding19Provider = (Provider<Binding19>) local;",
" }",
" return (Provider<Binding19>) local;",
" }",
"",
" private Binding19 binding19() {",
" Object local = binding19;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
" local = binding19;",
" if (local instanceof MemoizedSentinel) {",
" local = new Binding19(DaggerTestComponent.this.shard2.binding20());",
" binding19 = DoubleCheck.reentrantCheck(binding19, local);",
" }",
" }",
" }",
" return (Binding19) local;",
" }",
" }",
"",
" private final class Shard2 {",
" private volatile Object binding20 = new MemoizedSentinel();",
"",
" private volatile Provider<Binding20> binding20Provider;",
"",
" private Binding20 binding20() {",
" Object local = binding20;",
" if (local instanceof MemoizedSentinel) {",
" synchronized (local) {",
" local = binding20;",
" if (local instanceof MemoizedSentinel) {",
" local = new Binding20(",
" DaggerTestComponent.this.shard1.binding19Provider());",
" binding20 = DoubleCheck.reentrantCheck(binding20, local);",
" }",
" }",
" }",
" return (Binding20) local;",
" }",
"",
" private Provider<Binding20> binding20Provider() {",
" Object local = binding20Provider;",
" if (local == null) {",
" local = new SwitchingProvider<>(20);",
" binding20Provider = (Provider<Binding20>) local;",
" }",
" return (Provider<Binding20>) local;",
" }",
" }",
"}");
Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("dagger.internal.codegen.DaggerTestComponent")
.containsElementsIn(generatedComponent);
}
private static JavaFileObject createBinding(String bindingName, String... deps) {
return JavaFileObjects.forSourceLines(
"dagger.internal.codegen." + bindingName,
"package dagger.internal.codegen;",
"",
"import javax.inject.Inject;",
"import javax.inject.Provider;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"final class " + bindingName + " {",
" @Inject",
" " + bindingName + "(" + Arrays.stream(deps).collect(joining(", ")) + ") {}",
"}");
}
private static JavaFileObject createComponent(ImmutableList<String> entryPoints) {
return JavaFileObjects.forSourceLines(
"dagger.internal.codegen.TestComponent",
"package dagger.internal.codegen;",
"",
"import dagger.Component;",
"import javax.inject.Provider;",
"import javax.inject.Singleton;",
"",
"@Singleton",
"@Component",
"interface TestComponent {",
" " + entryPoints.stream().collect(joining("\n ")),
"}");
}
private static Compiler compilerWithAndroidMode() {
return javac()
.withProcessors(new ComponentProcessor())
.withOptions(
ImmutableSet.builder()
.add("-Adagger.keysPerComponentShard=" + BINDINGS_PER_SHARD)
.addAll(CompilerMode.FAST_INIT_MODE.javacopts())
.build());
}
}