• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
22 import com.google.common.collect.ImmutableList;
23 import com.google.testing.compile.Compilation;
24 import com.google.testing.compile.Compiler;
25 import com.google.testing.compile.JavaFileObjects;
26 import javax.tools.JavaFileObject;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.junit.runners.JUnit4;
30 
31 @RunWith(JUnit4.class)
32 public class SwitchingProviderTest {
33   @Test
switchingProviderTest()34   public void switchingProviderTest() {
35     ImmutableList.Builder<JavaFileObject> javaFileObjects = ImmutableList.builder();
36     StringBuilder entryPoints = new StringBuilder();
37     for (int i = 0; i <= 100; i++) {
38       String bindingName = "Binding" + i;
39       javaFileObjects.add(
40           JavaFileObjects.forSourceLines(
41               "test." + bindingName,
42               "package test;",
43               "",
44               "import javax.inject.Inject;",
45               "",
46               "final class " + bindingName + " {",
47               "  @Inject",
48               "  " + bindingName + "() {}",
49               "}"));
50       entryPoints.append(String.format("  Provider<%1$s> get%1$sProvider();\n", bindingName));
51     }
52 
53     javaFileObjects.add(
54         JavaFileObjects.forSourceLines(
55             "test.TestComponent",
56             "package test;",
57             "",
58             "import dagger.Component;",
59             "import javax.inject.Provider;",
60             "",
61             "@Component",
62             "interface TestComponent {",
63             entryPoints.toString(),
64             "}"));
65 
66     JavaFileObject generatedComponent =
67         JavaFileObjects.forSourceLines(
68             "test.DaggerTestComponent",
69                 "package test;",
70             GeneratedLines.generatedAnnotations(),
71                 "final class DaggerTestComponent implements TestComponent {",
72                 "  private final class SwitchingProvider<T> implements Provider<T> {",
73                 "    @SuppressWarnings(\"unchecked\")",
74                 "    private T get0() {",
75                 "      switch (id) {",
76                 "        case 0:  return (T) new Binding0();",
77                 "        case 1:  return (T) new Binding1();",
78                 "        case 2:  return (T) new Binding2();",
79                 "        case 3:  return (T) new Binding3();",
80                 "        case 4:  return (T) new Binding4();",
81                 "        case 5:  return (T) new Binding5();",
82                 "        case 6:  return (T) new Binding6();",
83                 "        case 7:  return (T) new Binding7();",
84                 "        case 8:  return (T) new Binding8();",
85                 "        case 9:  return (T) new Binding9();",
86                 "        case 10: return (T) new Binding10();",
87                 "        case 11: return (T) new Binding11();",
88                 "        case 12: return (T) new Binding12();",
89                 "        case 13: return (T) new Binding13();",
90                 "        case 14: return (T) new Binding14();",
91                 "        case 15: return (T) new Binding15();",
92                 "        case 16: return (T) new Binding16();",
93                 "        case 17: return (T) new Binding17();",
94                 "        case 18: return (T) new Binding18();",
95                 "        case 19: return (T) new Binding19();",
96                 "        case 20: return (T) new Binding20();",
97                 "        case 21: return (T) new Binding21();",
98                 "        case 22: return (T) new Binding22();",
99                 "        case 23: return (T) new Binding23();",
100                 "        case 24: return (T) new Binding24();",
101                 "        case 25: return (T) new Binding25();",
102                 "        case 26: return (T) new Binding26();",
103                 "        case 27: return (T) new Binding27();",
104                 "        case 28: return (T) new Binding28();",
105                 "        case 29: return (T) new Binding29();",
106                 "        case 30: return (T) new Binding30();",
107                 "        case 31: return (T) new Binding31();",
108                 "        case 32: return (T) new Binding32();",
109                 "        case 33: return (T) new Binding33();",
110                 "        case 34: return (T) new Binding34();",
111                 "        case 35: return (T) new Binding35();",
112                 "        case 36: return (T) new Binding36();",
113                 "        case 37: return (T) new Binding37();",
114                 "        case 38: return (T) new Binding38();",
115                 "        case 39: return (T) new Binding39();",
116                 "        case 40: return (T) new Binding40();",
117                 "        case 41: return (T) new Binding41();",
118                 "        case 42: return (T) new Binding42();",
119                 "        case 43: return (T) new Binding43();",
120                 "        case 44: return (T) new Binding44();",
121                 "        case 45: return (T) new Binding45();",
122                 "        case 46: return (T) new Binding46();",
123                 "        case 47: return (T) new Binding47();",
124                 "        case 48: return (T) new Binding48();",
125                 "        case 49: return (T) new Binding49();",
126                 "        case 50: return (T) new Binding50();",
127                 "        case 51: return (T) new Binding51();",
128                 "        case 52: return (T) new Binding52();",
129                 "        case 53: return (T) new Binding53();",
130                 "        case 54: return (T) new Binding54();",
131                 "        case 55: return (T) new Binding55();",
132                 "        case 56: return (T) new Binding56();",
133                 "        case 57: return (T) new Binding57();",
134                 "        case 58: return (T) new Binding58();",
135                 "        case 59: return (T) new Binding59();",
136                 "        case 60: return (T) new Binding60();",
137                 "        case 61: return (T) new Binding61();",
138                 "        case 62: return (T) new Binding62();",
139                 "        case 63: return (T) new Binding63();",
140                 "        case 64: return (T) new Binding64();",
141                 "        case 65: return (T) new Binding65();",
142                 "        case 66: return (T) new Binding66();",
143                 "        case 67: return (T) new Binding67();",
144                 "        case 68: return (T) new Binding68();",
145                 "        case 69: return (T) new Binding69();",
146                 "        case 70: return (T) new Binding70();",
147                 "        case 71: return (T) new Binding71();",
148                 "        case 72: return (T) new Binding72();",
149                 "        case 73: return (T) new Binding73();",
150                 "        case 74: return (T) new Binding74();",
151                 "        case 75: return (T) new Binding75();",
152                 "        case 76: return (T) new Binding76();",
153                 "        case 77: return (T) new Binding77();",
154                 "        case 78: return (T) new Binding78();",
155                 "        case 79: return (T) new Binding79();",
156                 "        case 80: return (T) new Binding80();",
157                 "        case 81: return (T) new Binding81();",
158                 "        case 82: return (T) new Binding82();",
159                 "        case 83: return (T) new Binding83();",
160                 "        case 84: return (T) new Binding84();",
161                 "        case 85: return (T) new Binding85();",
162                 "        case 86: return (T) new Binding86();",
163                 "        case 87: return (T) new Binding87();",
164                 "        case 88: return (T) new Binding88();",
165                 "        case 89: return (T) new Binding89();",
166                 "        case 90: return (T) new Binding90();",
167                 "        case 91: return (T) new Binding91();",
168                 "        case 92: return (T) new Binding92();",
169                 "        case 93: return (T) new Binding93();",
170                 "        case 94: return (T) new Binding94();",
171                 "        case 95: return (T) new Binding95();",
172                 "        case 96: return (T) new Binding96();",
173                 "        case 97: return (T) new Binding97();",
174                 "        case 98: return (T) new Binding98();",
175                 "        case 99: return (T) new Binding99();",
176                 "        default: throw new AssertionError(id);",
177                 "      }",
178                 "    }",
179                 "",
180                 "    @SuppressWarnings(\"unchecked\")",
181                 "    private T get1() {",
182                 "      switch (id) {",
183                 "        case 100: return (T) new Binding100();",
184                 "        default:  throw new AssertionError(id);",
185                 "      }",
186                 "    }",
187                 "",
188                 "    @Override",
189                 "    public T get() {",
190                 "      switch (id / 100) {",
191                 "        case 0:  return get0();",
192                 "        case 1:  return get1();",
193                 "        default: throw new AssertionError(id);",
194                 "      }",
195                 "    }",
196                 "  }",
197                 "}");
198 
199     Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
200     assertThat(compilation).succeededWithoutWarnings();
201     assertThat(compilation)
202         .generatedSourceFile("test.DaggerTestComponent")
203         .containsElementsIn(generatedComponent);
204   }
205 
206   @Test
unscopedBinds()207   public void unscopedBinds() {
208     JavaFileObject module =
209         JavaFileObjects.forSourceLines(
210             "test.TestModule",
211             "package test;",
212             "",
213             "import dagger.Binds;",
214             "import dagger.Module;",
215             "import dagger.Provides;",
216             "",
217             "@Module",
218             "interface TestModule {",
219             "  @Provides",
220             "  static String s() {",
221             "    return new String();",
222             "  }",
223             "",
224             "  @Binds CharSequence c(String s);",
225             "  @Binds Object o(CharSequence c);",
226             "}");
227     JavaFileObject component =
228         JavaFileObjects.forSourceLines(
229             "test.TestComponent",
230             "package test;",
231             "",
232             "import dagger.Component;",
233             "import javax.inject.Provider;",
234             "",
235             "@Component(modules = TestModule.class)",
236             "interface TestComponent {",
237             "  Provider<Object> objectProvider();",
238             "  Provider<CharSequence> charSequenceProvider();",
239             "}");
240 
241     Compilation compilation = compilerWithAndroidMode().compile(module, component);
242     assertThat(compilation).succeeded();
243     assertThat(compilation)
244         .generatedSourceFile("test.DaggerTestComponent")
245         .containsElementsIn(
246             JavaFileObjects.forSourceLines(
247                 "test.DaggerTestComponent",
248                 "package test;",
249                 "",
250                 GeneratedLines.generatedAnnotations(),
251                 "final class DaggerTestComponent implements TestComponent {",
252                 "  private volatile Provider<String> sProvider;",
253                 "",
254                 "  private Provider<String> stringProvider() {",
255                 "    Object local = sProvider;",
256                 "    if (local == null) {",
257                 "      local = new SwitchingProvider<>(0);",
258                 "      sProvider = (Provider<String>) local;",
259                 "    }",
260                 "    return (Provider<String>) local;",
261                 "  }",
262                 "",
263                 "  @Override",
264                 "  public Provider<Object> objectProvider() {",
265                 "    return (Provider) stringProvider();",
266                 "  }",
267                 "",
268                 "  @Override",
269                 "  public Provider<CharSequence> charSequenceProvider() {",
270                 "    return (Provider) stringProvider();",
271                 "  }",
272                 "",
273                 "  private final class SwitchingProvider<T> implements Provider<T> {",
274                 "    @SuppressWarnings(\"unchecked\")",
275                 "    @Override",
276                 "    public T get() {",
277                 "      switch (id) {",
278                 "        case 0:",
279                 "          return (T) TestModule_SFactory.s();",
280                 "        default:",
281                 "          throw new AssertionError(id);",
282                 "      }",
283                 "    }",
284                 "  }",
285                 "}"));
286   }
287 
288   @Test
scopedBinds()289   public void scopedBinds() {
290     JavaFileObject module =
291         JavaFileObjects.forSourceLines(
292             "test.TestModule",
293             "package test;",
294             "",
295             "import dagger.Binds;",
296             "import dagger.Module;",
297             "import dagger.Provides;",
298             "import javax.inject.Singleton;",
299             "",
300             "@Module",
301             "interface TestModule {",
302             "  @Provides",
303             "  static String s() {",
304             "    return new String();",
305             "  }",
306             "",
307             "  @Binds @Singleton Object o(CharSequence s);",
308             "  @Binds @Singleton CharSequence c(String s);",
309             "}");
310     JavaFileObject component =
311         JavaFileObjects.forSourceLines(
312             "test.TestComponent",
313             "package test;",
314             "",
315             "import dagger.Component;",
316             "import javax.inject.Provider;",
317             "import javax.inject.Singleton;",
318             "",
319             "@Singleton",
320             "@Component(modules = TestModule.class)",
321             "interface TestComponent {",
322             "  Provider<Object> objectProvider();",
323             "  Provider<CharSequence> charSequenceProvider();",
324             "}");
325 
326     Compilation compilation = compilerWithAndroidMode().compile(module, component);
327     assertThat(compilation).succeeded();
328     assertThat(compilation)
329         .generatedSourceFile("test.DaggerTestComponent")
330         .containsElementsIn(
331             JavaFileObjects.forSourceLines(
332                 "test.DaggerTestComponent",
333                 "package test;",
334                 "",
335                 GeneratedLines.generatedAnnotations(),
336                 "final class DaggerTestComponent implements TestComponent {",
337                 "  private volatile Object charSequence = new MemoizedSentinel();",
338                 "  private volatile Provider<CharSequence> cProvider;",
339                 "",
340                 "  private CharSequence charSequence() {",
341                 "    Object local = charSequence;",
342                 "    if (local instanceof MemoizedSentinel) {",
343                 "      synchronized (local) {",
344                 "        local = charSequence;",
345                 "        if (local instanceof MemoizedSentinel) {",
346                 "          local = TestModule_SFactory.s();",
347                 "          charSequence = DoubleCheck.reentrantCheck(charSequence, local);",
348                 "        }",
349                 "      }",
350                 "    }",
351                 "    return (CharSequence) local;",
352                 "  }",
353                 "",
354                 "  @Override",
355                 "  public Provider<Object> objectProvider() {",
356                 "    return (Provider) charSequenceProvider();",
357                 "  }",
358                 "",
359                 "  @Override",
360                 "  public Provider<CharSequence> charSequenceProvider() {",
361                 "    Object local = cProvider;",
362                 "    if (local == null) {",
363                 "      local = new SwitchingProvider<>(0);",
364                 "      cProvider = (Provider<CharSequence>) local;",
365                 "    }",
366                 "    return (Provider<CharSequence>) local;",
367                 "  }",
368                 "",
369                 "  private final class SwitchingProvider<T> implements Provider<T> {",
370                 "    @SuppressWarnings(\"unchecked\")",
371                 "    @Override",
372                 "    public T get() {",
373                 "      switch (id) {",
374                 "        case 0:",
375                 "          return (T) DaggerTestComponent.this.charSequence();",
376                 "        default:",
377                 "          throw new AssertionError(id);",
378                 "      }",
379                 "    }",
380                 "  }",
381                 "}"));
382   }
383 
384   @Test
emptyMultibindings_avoidSwitchProviders()385   public void emptyMultibindings_avoidSwitchProviders() {
386     JavaFileObject module =
387         JavaFileObjects.forSourceLines(
388             "test.TestModule",
389             "package test;",
390             "",
391             "import dagger.multibindings.Multibinds;",
392             "import dagger.Module;",
393             "import java.util.Map;",
394             "import java.util.Set;",
395             "",
396             "@Module",
397             "interface TestModule {",
398             "  @Multibinds Set<String> set();",
399             "  @Multibinds Map<String, String> map();",
400             "}");
401     JavaFileObject component =
402         JavaFileObjects.forSourceLines(
403             "test.TestComponent",
404             "package test;",
405             "",
406             "import dagger.Component;",
407             "import java.util.Map;",
408             "import java.util.Set;",
409             "import javax.inject.Provider;",
410             "",
411             "@Component(modules = TestModule.class)",
412             "interface TestComponent {",
413             "  Provider<Set<String>> setProvider();",
414             "  Provider<Map<String, String>> mapProvider();",
415             "}");
416 
417     Compilation compilation = compilerWithAndroidMode().compile(module, component);
418     assertThat(compilation).succeeded();
419     assertThat(compilation)
420         .generatedSourceFile("test.DaggerTestComponent")
421         .containsElementsIn(
422             JavaFileObjects.forSourceLines(
423                 "test.DaggerTestComponent",
424                 "package test;",
425                 "",
426                 GeneratedLines.generatedAnnotations(),
427                 "final class DaggerTestComponent implements TestComponent {",
428                 "  @Override",
429                 "  public Provider<Set<String>> setProvider() {",
430                 "    return SetFactory.<String>empty();",
431                 "  }",
432                 "",
433                 "  @Override",
434                 "  public Provider<Map<String, String>> mapProvider() {",
435                 "    return MapFactory.<String, String>emptyMapProvider();",
436                 "  }",
437                 "}"));
438   }
439 
440   @Test
memberInjectors()441   public void memberInjectors() {
442     JavaFileObject foo =
443         JavaFileObjects.forSourceLines(
444             "test.Foo",
445             "package test;",
446             "",
447             "class Foo {}");
448     JavaFileObject component =
449         JavaFileObjects.forSourceLines(
450             "test.TestComponent",
451             "package test;",
452             "",
453             "import dagger.Component;",
454             "import dagger.MembersInjector;",
455             "import javax.inject.Provider;",
456             "",
457             "@Component",
458             "interface TestComponent {",
459             "  Provider<MembersInjector<Foo>> providerOfMembersInjector();",
460             "}");
461 
462     Compilation compilation = compilerWithAndroidMode().compile(foo, component);
463     assertThat(compilation).succeeded();
464     assertThat(compilation)
465         .generatedSourceFile("test.DaggerTestComponent")
466         .containsElementsIn(
467             JavaFileObjects.forSourceLines(
468                 "test.DaggerTestComponent",
469                 "package test;",
470                 "",
471                 GeneratedLines.generatedAnnotations(),
472                 "final class DaggerTestComponent implements TestComponent {",
473                 "  private Provider<MembersInjector<Foo>> fooMembersInjectorProvider;",
474                 "",
475                 "  @SuppressWarnings(\"unchecked\")",
476                 "  private void initialize() {",
477                 "    this.fooMembersInjectorProvider = ",
478                 "        InstanceFactory.create(MembersInjectors.<Foo>noOp());",
479                 "  }",
480                 "",
481                 "  @Override",
482                 "  public Provider<MembersInjector<Foo>> providerOfMembersInjector() {",
483                 "    return fooMembersInjectorProvider;",
484                 "  }",
485                 "}"));
486   }
487 
488   @Test
optionals()489   public void optionals() {
490     JavaFileObject present =
491         JavaFileObjects.forSourceLines(
492             "test.Present",
493             "package test;",
494             "",
495             "class Present {}");
496     JavaFileObject absent =
497         JavaFileObjects.forSourceLines(
498             "test.Absent",
499             "package test;",
500             "",
501             "class Absent {}");
502     JavaFileObject module =
503         JavaFileObjects.forSourceLines(
504             "test.TestModule",
505             "package test;",
506             "",
507             "import dagger.BindsOptionalOf;",
508             "import dagger.Module;",
509             "import dagger.Provides;",
510             "",
511             "@Module",
512             "interface TestModule {",
513             "  @BindsOptionalOf Present bindOptionalOfPresent();",
514             "  @BindsOptionalOf Absent bindOptionalOfAbsent();",
515             "",
516             "  @Provides static Present p() { return new Present(); }",
517             "}");
518     JavaFileObject component =
519         JavaFileObjects.forSourceLines(
520             "test.TestComponent",
521             "package test;",
522             "",
523             "import dagger.Component;",
524             "import java.util.Optional;",
525             "import javax.inject.Provider;",
526             "",
527             "@Component(modules = TestModule.class)",
528             "interface TestComponent {",
529             "  Provider<Optional<Present>> providerOfOptionalOfPresent();",
530             "  Provider<Optional<Absent>> providerOfOptionalOfAbsent();",
531             "}");
532 
533     Compilation compilation = compilerWithAndroidMode().compile(present, absent, module, component);
534     assertThat(compilation).succeeded();
535     assertThat(compilation)
536         .generatedSourceFile("test.DaggerTestComponent")
537         .containsElementsIn(
538             JavaFileObjects.forSourceLines(
539                 "test.DaggerTestComponent",
540                 "package test;",
541                 "",
542                 GeneratedLines.generatedAnnotations(),
543                 "final class DaggerTestComponent implements TestComponent {",
544                 "  @SuppressWarnings(\"rawtypes\")",
545                 "  private static final Provider ABSENT_JDK_OPTIONAL_PROVIDER =",
546                 "      InstanceFactory.create(Optional.empty());",
547                 "",
548                 "  private volatile Provider<Optional<Present>> optionalOfPresentProvider;",
549                 "",
550                 "  private Provider<Optional<Absent>> optionalOfAbsentProvider;",
551                 "",
552                 "  @SuppressWarnings(\"unchecked\")",
553                 "  private void initialize() {",
554                 "    this.optionalOfAbsentProvider = absentJdkOptionalProvider();",
555                 "  }",
556                 "",
557                 "  @Override",
558                 "  public Provider<Optional<Present>> providerOfOptionalOfPresent() {",
559                 "    Object local = optionalOfPresentProvider;",
560                 "    if (local == null) {",
561                 "      local = new SwitchingProvider<>(0);",
562                 "      optionalOfPresentProvider = (Provider<Optional<Present>>) local;",
563                 "    }",
564                 "    return (Provider<Optional<Present>>) local;",
565                 "  }",
566                 "",
567                 "  @Override",
568                 "  public Provider<Optional<Absent>> providerOfOptionalOfAbsent() {",
569                 "    return optionalOfAbsentProvider;",
570                 "  }",
571                 "",
572                 "  private static <T> Provider<Optional<T>> absentJdkOptionalProvider() {",
573                 "    @SuppressWarnings(\"unchecked\")",
574                 "    Provider<Optional<T>> provider = ",
575                 "          (Provider<Optional<T>>) ABSENT_JDK_OPTIONAL_PROVIDER;",
576                 "    return provider;",
577                 "  }",
578                 "",
579                 "  private final class SwitchingProvider<T> implements Provider<T> {",
580                 "    @SuppressWarnings(\"unchecked\")",
581                 "    @Override",
582                 "    public T get() {",
583                 "      switch (id) {",
584                 "        case 0: // java.util.Optional<test.Present>",
585                 "          return (T) Optional.of(TestModule_PFactory.p());",
586                 "        default:",
587                 "          throw new AssertionError(id);",
588                 "      }",
589                 "    }",
590                 "  }",
591                 "}"));
592   }
593 
compilerWithAndroidMode()594   private Compiler compilerWithAndroidMode() {
595     return javac()
596         .withProcessors(new ComponentProcessor())
597         .withOptions(CompilerMode.FAST_INIT_MODE.javacopts());
598   }
599 }
600