/* * 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 dagger.internal.codegen.CompilerMode.DEFAULT_MODE; import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; import static dagger.internal.codegen.Compilers.compilerWithOptions; import static java.util.stream.Collectors.joining; import com.google.common.collect.ImmutableCollection; 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.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class ComponentShardTest { private static final int BINDINGS_PER_SHARD = 2; @Parameters(name = "{0}") public static ImmutableCollection parameters() { return CompilerMode.TEST_PARAMETERS; } private final CompilerMode compilerMode; public ComponentShardTest(CompilerMode compilerMode) { this.compilerMode = compilerMode; } @Test public void testNewShardCreated() { // Add all bindings. // // 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 // ^--------/ // ImmutableList.Builder javaFileObjects = ImmutableList.builder(); javaFileObjects // Shard 2: Bindings (1) .add(createBinding("Binding1", "Binding2 binding2")) // Shard 1: Bindings (2, 3, 4, 5). Contains more than 2 bindings due to cycle. .add(createBinding("Binding2", "Binding3 binding3")) .add(createBinding("Binding3", "Binding4 binding4")) .add(createBinding("Binding4", "Binding5 binding5, Provider binding2Provider")) .add(createBinding("Binding5", "Binding6 binding6")) // Component shard: Bindings (6, 7) .add(createBinding("Binding6", "Binding7 binding7")) .add(createBinding("Binding7")); // Add the component with entry points for each binding and its provider. javaFileObjects.add( 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 {", " Binding1 binding1();", " Binding2 binding2();", " Binding3 binding3();", " Binding4 binding4();", " Binding5 binding5();", " Binding6 binding6();", " Binding7 binding7();", " Provider providerBinding1();", " Provider providerBinding2();", " Provider providerBinding3();", " Provider providerBinding4();", " Provider providerBinding5();", " Provider providerBinding6();", " Provider providerBinding7();", "}")); Compilation compilation = compiler().compile(javaFileObjects.build()); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("dagger.internal.codegen.DaggerTestComponent") .containsElementsIn( compilerMode .javaFileBuilder("dagger.internal.codegen.DaggerTestComponent") .addLines( "package dagger.internal.codegen;", "", GeneratedLines.generatedAnnotations(), "final class DaggerTestComponent implements TestComponent {", " private Shard1 shard1;", " private Shard2 shard2;", " private final DaggerTestComponent testComponent = this;", " private Provider binding7Provider;", " private Provider binding6Provider;", "", " private DaggerTestComponent() {", " initialize();", " shard1 = new Shard1();", " shard2 = new Shard2();", " }") .addLinesIn( DEFAULT_MODE, " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.binding7Provider =", " DoubleCheck.provider(Binding7_Factory.create());", " this.binding6Provider =", " DoubleCheck.provider(Binding6_Factory.create(binding7Provider));", " }") .addLinesIn( FAST_INIT_MODE, " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.binding7Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 6));", " this.binding6Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 5));", " }") .addLines( " @Override", " public Binding1 binding1() {", " return testComponent.shard2.binding1Provider.get();", " }", "", " @Override", " public Binding2 binding2() {", " return testComponent.shard1.binding2Provider.get();", " }", "", " @Override", " public Binding3 binding3() {", " return testComponent.shard1.binding3Provider.get();", " }", "", " @Override", " public Binding4 binding4() {", " return testComponent.shard1.binding4Provider.get();", " }", "", " @Override", " public Binding5 binding5() {", " return testComponent.shard1.binding5Provider.get();", " }", "", " @Override", " public Binding6 binding6() {", " return binding6Provider.get();", " }", "", " @Override", " public Binding7 binding7() {", " return binding7Provider.get();", " }", "", " @Override", " public Provider providerBinding1() {", " return testComponent.shard2.binding1Provider;", " }", "", " @Override", " public Provider providerBinding2() {", " return testComponent.shard1.binding2Provider;", " }", "", " @Override", " public Provider providerBinding3() {", " return testComponent.shard1.binding3Provider;", " }", "", " @Override", " public Provider providerBinding4() {", " return testComponent.shard1.binding4Provider;", " }", "", " @Override", " public Provider providerBinding5() {", " return testComponent.shard1.binding5Provider;", " }", "", " @Override", " public Provider providerBinding6() {", " return binding6Provider;", " }", "", " @Override", " public Provider providerBinding7() {", " return binding7Provider;", " }", "", " private final class Shard1 {", " private Provider binding5Provider;", " private Provider binding2Provider;", " private Provider binding4Provider;", " private Provider binding3Provider;") .addLinesIn( DEFAULT_MODE, " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.binding5Provider =", " DoubleCheck.provider(", " Binding5_Factory.create(testComponent.binding6Provider));", " this.binding2Provider = new DelegateFactory<>();", " this.binding4Provider =", " DoubleCheck.provider(", " Binding4_Factory.create(binding5Provider, binding2Provider));", " this.binding3Provider =", " DoubleCheck.provider(Binding3_Factory.create(binding4Provider));", " DelegateFactory.setDelegate(", " binding2Provider,", " DoubleCheck.provider(Binding2_Factory.create(binding3Provider)));", " }", " }") .addLinesIn( FAST_INIT_MODE, " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.binding5Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 4));", " this.binding4Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 3));", " this.binding3Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 2));", " this.binding2Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 1));", " }", " }") .addLines( " private final class Shard2 {", " private Provider binding1Provider;") .addLinesIn( DEFAULT_MODE, " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.binding1Provider =", " DoubleCheck.provider(", " Binding1_Factory.create(", " testComponent.shard1.binding2Provider));", " }", " }") .addLinesIn( FAST_INIT_MODE, " @SuppressWarnings(\"unchecked\")", " private void initialize() {", " this.binding1Provider = DoubleCheck.provider(", " new SwitchingProvider(testComponent, 0));", " }", " }", "", " private static final class SwitchingProvider implements Provider {", " @SuppressWarnings(\"unchecked\")", " @Override", " public T get() {", " switch (id) {", " case 0: return (T) new Binding1(", " testComponent.shard1.binding2Provider.get());", " case 1: return (T) new Binding2(", " testComponent.shard1.binding3Provider.get());", " case 2: return (T) new Binding3(", " testComponent.shard1.binding4Provider.get());", " case 3: return (T) new Binding4(", " testComponent.shard1.binding5Provider.get(),", " testComponent.shard1.binding2Provider);", " case 4: return (T) new Binding5(", " testComponent.binding6Provider.get());", " case 5: return (T) new Binding6(", " testComponent.binding7Provider.get());", " case 6: return (T) new Binding7();", " default: throw new AssertionError(id);", " }", " }", " }") .build()); } 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 Compiler compiler() { return compilerWithOptions( ImmutableSet.builder() .add("-Adagger.keysPerComponentShard=" + BINDINGS_PER_SHARD) .addAll(compilerMode.javacopts()) .build()); } }