• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static com.google.testing.compile.CompilationSubject.assertThat;
20 import static com.google.testing.compile.Compiler.javac;
21 import static java.util.stream.Collectors.joining;
22 
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.testing.compile.Compilation;
26 import com.google.testing.compile.Compiler;
27 import com.google.testing.compile.JavaFileObjects;
28 import java.util.Arrays;
29 import javax.tools.JavaFileObject;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.JUnit4;
33 
34 @RunWith(JUnit4.class)
35 public class ComponentShardTest {
36   private static final int BINDINGS_PER_SHARD = 10;
37 
38   @Test
testNewShardCreated()39   public void testNewShardCreated() {
40     // Create 2N + 1 bindings: N in DaggerTestComponent, N in Shard1, and 1 in Shard2
41     int numBindings = 2 * BINDINGS_PER_SHARD + 1;
42     ImmutableList.Builder<JavaFileObject> javaFileObjects = ImmutableList.builder();
43     ImmutableList.Builder<String> entryPoints = ImmutableList.builder();
44     for (int i = 0; i < numBindings; i++) {
45       String bindingName = "Binding" + i;
46       entryPoints.add(String.format("%1$s get%1$s();", bindingName));
47       entryPoints.add(String.format("Provider<%1$s> get%1$sProvider();", bindingName));
48 
49       // Add dependencies between main component and shard1: 9 -> 10 -> Provider<9>
50       // Add dependencies between shard1 and shard2:        19 -> 20 -> Provider<19>
51       switch (i) {
52         case 9:
53           javaFileObjects.add(createBinding(bindingName, "Binding10 dep"));
54           break;
55         case 10:
56           javaFileObjects.add(createBinding(bindingName, "Provider<Binding9> dep"));
57           break;
58         case 19:
59           javaFileObjects.add(createBinding(bindingName, "Binding20 dep"));
60           break;
61         case 20:
62           javaFileObjects.add(createBinding(bindingName, "Provider<Binding19> dep"));
63           break;
64         default:
65           javaFileObjects.add(createBinding(bindingName));
66           break;
67       }
68     }
69 
70     javaFileObjects.add(createComponent(entryPoints.build()));
71 
72     // This generated component shows a couple things:
73     //   1. Binding locations:
74     //     * Binding #9 belongs to DaggerTestComponent
75     //     * Binding #10 belongs to Shard1
76     //     * Binding #20 belongs to Shard2
77     //   2. DaggerTestComponent entry point methods:
78     //     * Binding #9 implementation is inlined DaggerTestComponent.
79     //     * Binding #10 implementation is delegated to Shard1.
80     //     * Binding #20 implementation is delegated to Shard2.
81     //   3. Dependencies between component and shard:
82     //     * Binding #9 in DaggerTestComponent depends on #10 in Shard1.
83     //     * Binding #10 in Shard1 depends on Provider<#9> in DaggerTestComponent.
84     //   4. Dependencies between shard and shard:
85     //     * Binding #19 in Shard1 depends on #20 in Shard2.
86     //     * Binding #20 in Shard2 depends on Provider<#19> in Shard1.
87     JavaFileObject generatedComponent =
88         JavaFileObjects.forSourceLines(
89             "dagger.internal.codegen.DaggerTestComponent",
90                 "package dagger.internal.codegen;",
91             GeneratedLines.generatedAnnotations(),
92                 "final class DaggerTestComponent implements TestComponent {",
93                 "  private final Shard1 shard1 = new Shard1();",
94                 "",
95                 "  private volatile Provider<Binding9> binding9Provider;",
96                 "",
97                 "  private volatile Object binding9 = new MemoizedSentinel();",
98                 "",
99                 "  @Override",
100                 "  public Binding9 getBinding9() {",
101                 "    Object local = binding9;",
102                 "    if (local instanceof MemoizedSentinel) {",
103                 "      synchronized (local) {",
104                 "        local = binding9;",
105                 "        if (local instanceof MemoizedSentinel) {",
106                 "          local = new Binding9(DaggerTestComponent.this.shard1.binding10());",
107                 "          binding9 = DoubleCheck.reentrantCheck(binding9, local);",
108                 "        }",
109                 "      }",
110                 "    }",
111                 "    return (Binding9) local;",
112                 "  }",
113                 "",
114                 "  @Override",
115                 "  public Provider<Binding9> getBinding9Provider() {",
116                 "    Object local = binding9Provider;",
117                 "    if (local == null) {",
118                 "      local = new SwitchingProvider<>(9);",
119                 "      binding9Provider = (Provider<Binding9>) local;",
120                 "    }",
121                 "    return (Provider<Binding9>) local;",
122                 "  }",
123                 "",
124                 "  @Override",
125                 "  public Binding10 getBinding10() {",
126                 "    return DaggerTestComponent.this.shard1.binding10();",
127                 "  }",
128                 "",
129                 "  @Override",
130                 "  public Provider<Binding10> getBinding10Provider() {",
131                 "    return DaggerTestComponent.this.shard1.binding10Provider();",
132                 "  }",
133                 "",
134                 "  @Override",
135                 "  public Binding20 getBinding20() {",
136                 "    return DaggerTestComponent.this.shard2.binding20();",
137                 "  }",
138                 "",
139                 "  @Override",
140                 "  public Provider<Binding20> getBinding20Provider() {",
141                 "    return DaggerTestComponent.this.shard2.binding20Provider();",
142                 "  }",
143                 "",
144                 "  private final class Shard1 {",
145                 "    private volatile Object binding10 = new MemoizedSentinel();",
146                 "",
147                 "    private volatile Provider<Binding10> binding10Provider;",
148                 "",
149                 "    private volatile Provider<Binding19> binding19Provider;",
150                 "",
151                 "    private volatile Object binding19 = new MemoizedSentinel();",
152                 "",
153                 "    private Binding10 binding10() {",
154                 "      Object local = binding10;",
155                 "      if (local instanceof MemoizedSentinel) {",
156                 "        synchronized (local) {",
157                 "          local = binding10;",
158                 "          if (local instanceof MemoizedSentinel) {",
159                 "            local = new Binding10(",
160                 "                DaggerTestComponent.this.getBinding9Provider());",
161                 "            binding10 = DoubleCheck.reentrantCheck(binding10, local);",
162                 "          }",
163                 "        }",
164                 "      }",
165                 "      return (Binding10) local;",
166                 "    }",
167                 "",
168                 "    private Provider<Binding10> binding10Provider() {",
169                 "      Object local = binding10Provider;",
170                 "      if (local == null) {",
171                 "        local = new SwitchingProvider<>(10);",
172                 "        binding10Provider = (Provider<Binding10>) local;",
173                 "      }",
174                 "      return (Provider<Binding10>) local;",
175                 "    }",
176                 "",
177                 "    private Provider<Binding19> binding19Provider() {",
178                 "      Object local = binding19Provider;",
179                 "      if (local == null) {",
180                 "        local = new SwitchingProvider<>(19);",
181                 "        binding19Provider = (Provider<Binding19>) local;",
182                 "      }",
183                 "      return (Provider<Binding19>) local;",
184                 "    }",
185                 "",
186                 "    private Binding19 binding19() {",
187                 "      Object local = binding19;",
188                 "      if (local instanceof MemoizedSentinel) {",
189                 "        synchronized (local) {",
190                 "          local = binding19;",
191                 "          if (local instanceof MemoizedSentinel) {",
192                 "            local = new Binding19(DaggerTestComponent.this.shard2.binding20());",
193                 "            binding19 = DoubleCheck.reentrantCheck(binding19, local);",
194                 "          }",
195                 "        }",
196                 "      }",
197                 "      return (Binding19) local;",
198                 "    }",
199                 "  }",
200                 "",
201                 "  private final class Shard2 {",
202                 "    private volatile Object binding20 = new MemoizedSentinel();",
203                 "",
204                 "    private volatile Provider<Binding20> binding20Provider;",
205                 "",
206                 "    private Binding20 binding20() {",
207                 "      Object local = binding20;",
208                 "      if (local instanceof MemoizedSentinel) {",
209                 "        synchronized (local) {",
210                 "          local = binding20;",
211                 "          if (local instanceof MemoizedSentinel) {",
212                 "            local = new Binding20(",
213                 "                DaggerTestComponent.this.shard1.binding19Provider());",
214                 "            binding20 = DoubleCheck.reentrantCheck(binding20, local);",
215                 "          }",
216                 "        }",
217                 "      }",
218                 "      return (Binding20) local;",
219                 "    }",
220                 "",
221                 "    private Provider<Binding20> binding20Provider() {",
222                 "      Object local = binding20Provider;",
223                 "      if (local == null) {",
224                 "        local = new SwitchingProvider<>(20);",
225                 "        binding20Provider = (Provider<Binding20>) local;",
226                 "      }",
227                 "      return (Provider<Binding20>) local;",
228                 "    }",
229                 "  }",
230                 "}");
231 
232     Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
233     assertThat(compilation).succeededWithoutWarnings();
234     assertThat(compilation)
235         .generatedSourceFile("dagger.internal.codegen.DaggerTestComponent")
236         .containsElementsIn(generatedComponent);
237   }
238 
createBinding(String bindingName, String... deps)239   private static JavaFileObject createBinding(String bindingName, String... deps) {
240     return JavaFileObjects.forSourceLines(
241         "dagger.internal.codegen." + bindingName,
242         "package dagger.internal.codegen;",
243         "",
244         "import javax.inject.Inject;",
245         "import javax.inject.Provider;",
246         "import javax.inject.Singleton;",
247         "",
248         "@Singleton",
249         "final class " + bindingName + " {",
250         "  @Inject",
251         "  " + bindingName + "(" + Arrays.stream(deps).collect(joining(", ")) + ") {}",
252         "}");
253   }
254 
createComponent(ImmutableList<String> entryPoints)255   private static JavaFileObject createComponent(ImmutableList<String> entryPoints) {
256     return JavaFileObjects.forSourceLines(
257         "dagger.internal.codegen.TestComponent",
258         "package dagger.internal.codegen;",
259         "",
260         "import dagger.Component;",
261         "import javax.inject.Provider;",
262         "import javax.inject.Singleton;",
263         "",
264         "@Singleton",
265         "@Component",
266         "interface TestComponent {",
267         "  " + entryPoints.stream().collect(joining("\n  ")),
268         "}");
269   }
270 
compilerWithAndroidMode()271   private static Compiler compilerWithAndroidMode() {
272     return javac()
273         .withProcessors(new ComponentProcessor())
274         .withOptions(
275             ImmutableSet.builder()
276                 .add("-Adagger.keysPerComponentShard=" + BINDINGS_PER_SHARD)
277                 .addAll(CompilerMode.FAST_INIT_MODE.javacopts())
278                 .build());
279   }
280 }
281