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