• 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 dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
22 import static dagger.internal.codegen.Compilers.compilerWithOptions;
23 
24 import com.google.common.collect.ImmutableCollection;
25 import com.google.testing.compile.Compilation;
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.Parameterized;
31 import org.junit.runners.Parameterized.Parameters;
32 
33 @RunWith(Parameterized.class)
34 public class AssistedFactoryTest {
35   @Parameters(name = "{0}")
parameters()36   public static ImmutableCollection<Object[]> parameters() {
37     return CompilerMode.TEST_PARAMETERS;
38   }
39 
40   private final CompilerMode compilerMode;
41 
AssistedFactoryTest(CompilerMode compilerMode)42   public AssistedFactoryTest(CompilerMode compilerMode) {
43     this.compilerMode = compilerMode;
44   }
45 
46   @Test
testAssistedFactory()47   public void testAssistedFactory() {
48     JavaFileObject foo =
49         JavaFileObjects.forSourceLines(
50             "test.Foo",
51             "package test;",
52             "",
53             "import dagger.assisted.Assisted;",
54             "import dagger.assisted.AssistedInject;",
55             "",
56             "class Foo {",
57             "  @AssistedInject",
58             "  Foo(@Assisted String str, Bar bar) {}",
59             "}");
60     JavaFileObject fooFactory =
61         JavaFileObjects.forSourceLines(
62             "test.FooFactory",
63             "package test;",
64             "",
65             "import dagger.assisted.AssistedFactory;",
66             "",
67             "@AssistedFactory",
68             "interface FooFactory {",
69             "  Foo create(String factoryStr);",
70             "}");
71     JavaFileObject bar =
72         JavaFileObjects.forSourceLines(
73             "test.Bar",
74             "package test;",
75             "",
76             "import javax.inject.Inject;",
77             "",
78             "class Bar {",
79             "  @Inject Bar() {}",
80             "}");
81     JavaFileObject component =
82         JavaFileObjects.forSourceLines(
83             "test.TestComponent",
84             "package test;",
85             "",
86             "import dagger.Component;",
87             "",
88             "@Component",
89             "interface TestComponent {",
90             "  FooFactory fooFactory();",
91             "}");
92     Compilation compilation =
93         compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component);
94     assertThat(compilation).succeeded();
95     JavaFileObject generatedComponent =
96         compilerMode
97             .javaFileBuilder("test.DaggerTestComponent")
98             .addLines("package test;", "", GeneratedLines.generatedAnnotations())
99             .addLinesIn(
100                 FAST_INIT_MODE,
101                 "final class DaggerTestComponent implements TestComponent {",
102                 "  private final DaggerTestComponent testComponent = this;",
103                 "  private Provider<FooFactory> fooFactoryProvider;",
104                 "",
105                 "  @SuppressWarnings(\"unchecked\")",
106                 "  private void initialize() {",
107                 "    this.fooFactoryProvider = SingleCheck.provider(new"
108                     + " SwitchingProvider<FooFactory>(testComponent, 0));",
109                 "  }",
110                 "",
111                 "  @Override",
112                 "  public FooFactory fooFactory() {",
113                 "    return fooFactoryProvider.get();",
114                 "  }",
115                 "",
116                 "  private static final class SwitchingProvider<T> implements Provider<T> {",
117                 "    private final DaggerTestComponent testComponent;",
118                 "    private final int id;",
119                 "",
120                 "    @SuppressWarnings(\"unchecked\")",
121                 "    @Override",
122                 "    public T get() {",
123                 "      switch (id) {",
124                 "        case 0:",
125                 "        return (T) new FooFactory() {",
126                 "          @Override",
127                 "          public Foo create(String str) {",
128                 "            return new Foo(str, new Bar());",
129                 "          }",
130                 "        };",
131                 "",
132                 "        default: throw new AssertionError(id);",
133                 "      }",
134                 "    }",
135                 "  }",
136                 "}")
137             .addLinesIn(
138                 DEFAULT_MODE,
139                 "final class DaggerTestComponent implements TestComponent {",
140                 "",
141                 "  private Foo_Factory fooProvider;",
142                 "",
143                 "  private Provider<FooFactory> fooFactoryProvider;",
144                 "",
145                 "  @SuppressWarnings(\"unchecked\")",
146                 "  private void initialize() {",
147                 "    this.fooProvider = Foo_Factory.create(Bar_Factory.create());",
148                 "    this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);",
149                 "  }",
150                 "",
151                 "  @Override",
152                 "  public FooFactory fooFactory() {",
153                 "    return fooFactoryProvider.get();",
154                 "  }",
155                 "}")
156             .build();
157     assertThat(compilation)
158         .generatedSourceFile("test.DaggerTestComponent")
159         .containsElementsIn(generatedComponent);
160   }
161 
162   @Test
testAssistedFactoryCycle()163   public void testAssistedFactoryCycle() {
164     JavaFileObject foo =
165         JavaFileObjects.forSourceLines(
166             "test.Foo",
167             "package test;",
168             "",
169             "import dagger.assisted.Assisted;",
170             "import dagger.assisted.AssistedInject;",
171             "",
172             "class Foo {",
173             "  @AssistedInject",
174             "  Foo(@Assisted String str, Bar bar) {}",
175             "}");
176     JavaFileObject fooFactory =
177         JavaFileObjects.forSourceLines(
178             "test.FooFactory",
179             "package test;",
180             "",
181             "import dagger.assisted.AssistedFactory;",
182             "",
183             "@AssistedFactory",
184             "interface FooFactory {",
185             "  Foo create(String factoryStr);",
186             "}");
187     JavaFileObject bar =
188         JavaFileObjects.forSourceLines(
189             "test.Bar",
190             "package test;",
191             "",
192             "import javax.inject.Inject;",
193             "",
194             "class Bar {",
195             "  @Inject Bar(FooFactory fooFactory) {}",
196             "}");
197     JavaFileObject component =
198         JavaFileObjects.forSourceLines(
199             "test.TestComponent",
200             "package test;",
201             "",
202             "import dagger.Component;",
203             "",
204             "@Component",
205             "interface TestComponent {",
206             "  FooFactory fooFactory();",
207             "}");
208     Compilation compilation =
209         compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component);
210     assertThat(compilation).succeeded();
211     JavaFileObject generatedComponent =
212         compilerMode
213             .javaFileBuilder("test.DaggerTestComponent")
214             .addLines("package test;", "", GeneratedLines.generatedAnnotations())
215             .addLinesIn(
216                 FAST_INIT_MODE,
217                 "final class DaggerTestComponent implements TestComponent {",
218                 "  private final DaggerTestComponent testComponent = this;",
219                 "  private Provider<FooFactory> fooFactoryProvider;",
220                 "",
221                 "  private Bar bar() {",
222                 "    return new Bar(fooFactoryProvider.get());",
223                 "  }",
224                 "",
225                 "  @SuppressWarnings(\"unchecked\")",
226                 "  private void initialize() {",
227                 "    this.fooFactoryProvider = SingleCheck.provider(new"
228                     + " SwitchingProvider<FooFactory>(testComponent, 0));",
229                 "  }",
230                 "",
231                 "  @Override",
232                 "  public FooFactory fooFactory() {",
233                 "    return fooFactoryProvider.get();",
234                 "  }",
235                 "",
236                 "  private static final class SwitchingProvider<T> implements Provider<T> {",
237                 "    private final DaggerTestComponent testComponent;",
238                 "    private final int id;",
239                 "",
240                 "    @SuppressWarnings(\"unchecked\")",
241                 "    @Override",
242                 "    public T get() {",
243                 "      switch (id) {",
244                 "        case 0:",
245                 "        return (T) new FooFactory() {",
246                 "          @Override",
247                 "          public Foo create(String str) {",
248                 "            return new Foo(str, testComponent.bar())",
249                 "          }",
250                 "        };",
251                 "",
252                 "        default: throw new AssertionError(id);",
253                 "      }",
254                 "    }",
255                 "  }",
256                 "}")
257             .addLinesIn(
258                 DEFAULT_MODE,
259                 "final class DaggerTestComponent implements TestComponent {",
260                 "",
261                 "  private Provider<FooFactory> fooFactoryProvider;",
262                 "",
263                 "  private Provider<Bar> barProvider;",
264                 "",
265                 "  private Foo_Factory fooProvider;",
266                 "",
267                 "  @SuppressWarnings(\"unchecked\")",
268                 "  private void initialize() {",
269                 "    this.fooFactoryProvider = new DelegateFactory<>();",
270                 "    this.barProvider = Bar_Factory.create(fooFactoryProvider);",
271                 "    this.fooProvider = Foo_Factory.create(barProvider);",
272                 "    DelegateFactory.setDelegate(",
273                 "        fooFactoryProvider, FooFactory_Impl.create(fooProvider));",
274                 "  }",
275                 "",
276                 "  @Override",
277                 "  public FooFactory fooFactory() {",
278                 "    return fooFactoryProvider.get();",
279                 "  }",
280                 "}")
281             .build();
282     assertThat(compilation)
283         .generatedSourceFile("test.DaggerTestComponent")
284         .containsElementsIn(generatedComponent);
285   }
286 
287   @Test
assistedParamConflictsWithComponentFieldName_successfulyDeduped()288   public void assistedParamConflictsWithComponentFieldName_successfulyDeduped() {
289     JavaFileObject foo =
290         JavaFileObjects.forSourceLines(
291             "test.Foo",
292             "package test;",
293             "",
294             "import dagger.assisted.Assisted;",
295             "import dagger.assisted.AssistedInject;",
296             "import javax.inject.Provider;",
297             "",
298             "class Foo {",
299             "  @AssistedInject",
300             "  Foo(@Assisted String testComponent, Provider<Bar> bar) {}",
301             "}");
302     JavaFileObject fooFactory =
303         JavaFileObjects.forSourceLines(
304             "test.FooFactory",
305             "package test;",
306             "",
307             "import dagger.assisted.AssistedFactory;",
308             "",
309             "@AssistedFactory",
310             "interface FooFactory {",
311             "  Foo create(String factoryStr);",
312             "}");
313     JavaFileObject bar =
314         JavaFileObjects.forSourceLines(
315             "test.Bar",
316             "package test;",
317             "",
318             "import javax.inject.Inject;",
319             "",
320             "class Bar {",
321             "  @Inject Bar() {}",
322             "}");
323     JavaFileObject component =
324         JavaFileObjects.forSourceLines(
325             "test.TestComponent",
326             "package test;",
327             "",
328             "import dagger.Component;",
329             "",
330             "@Component",
331             "interface TestComponent {",
332             "  FooFactory fooFactory();",
333             "}");
334     JavaFileObject generatedComponent =
335         compilerMode
336             .javaFileBuilder("test.DaggerTestComponent")
337             .addLines("package test;", "", GeneratedLines.generatedAnnotations())
338             .addLinesIn(
339                 FAST_INIT_MODE,
340                 "final class DaggerTestComponent implements TestComponent {",
341                 "  private final DaggerTestComponent testComponent = this;",
342                 "  private Provider<FooFactory> fooFactoryProvider;",
343                 "",
344                 "  @SuppressWarnings(\"unchecked\")",
345                 "  private void initialize() {",
346                 "    this.barProvider = new SwitchingProvider<>(testComponent, 1);",
347                 "    this.fooFactoryProvider = SingleCheck.provider(",
348                 "      new SwitchingProvider<FooFactory>(testComponent, 0));",
349                 "  }",
350                 "",
351                 "  @Override",
352                 "  public FooFactory fooFactory() {",
353                 "    return fooFactoryProvider.get();",
354                 "  }",
355                 "",
356                 "  private static final class SwitchingProvider<T> implements Provider<T> {",
357                 "    private final DaggerTestComponent testComponent;",
358                 "    private final int id;",
359                 "",
360                 "    @SuppressWarnings(\"unchecked\")",
361                 "    @Override",
362                 "    public T get() {",
363                 "      switch (id) {",
364                 "        case 0:",
365                 "        return (T) new FooFactory() {",
366                 "          @Override",
367                 "          public Foo create(String testComponent2) {",
368                 "            return new Foo(testComponent2, testComponent.barProvider);",
369                 "          }",
370                 "        };",
371                 "        case 1: return (T) new Bar();",
372                 "        default: throw new AssertionError(id);",
373                 "      }",
374                 "    }",
375                 "  }",
376                 "}")
377             .addLinesIn(
378                 DEFAULT_MODE,
379                 "final class DaggerTestComponent implements TestComponent {",
380                 "",
381                 "  private Foo_Factory fooProvider;",
382                 "",
383                 "  private Provider<FooFactory> fooFactoryProvider;",
384                 "",
385                 "  @SuppressWarnings(\"unchecked\")",
386                 "  private void initialize() {",
387                 "    this.fooProvider = Foo_Factory.create(Bar_Factory.create());",
388                 "    this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);",
389                 "  }",
390                 "",
391                 "  @Override",
392                 "  public FooFactory fooFactory() {",
393                 "    return fooFactoryProvider.get();",
394                 "  }",
395                 "}")
396             .build();
397 
398     Compilation compilation =
399         compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component);
400 
401     assertThat(compilation).succeeded();
402     assertThat(compilation)
403         .generatedSourceFile("test.DaggerTestComponent")
404         .containsElementsIn(generatedComponent);
405   }
406 
407   @Test
testFactoryGeneratorDuplicatedParamNames()408   public void testFactoryGeneratorDuplicatedParamNames() {
409     JavaFileObject componentSrc =
410         JavaFileObjects.forSourceLines(
411             "test.TestComponent",
412             "package test;",
413             "",
414             "import dagger.BindsInstance;",
415             "import dagger.Component;",
416             "",
417             "@Component",
418             "interface TestComponent {",
419             "  @Component.Factory",
420             "  interface Factory {",
421             "    TestComponent create(@BindsInstance Bar arg);",
422             "}",
423             "  FooFactory getFooFactory();",
424             "}");
425     JavaFileObject factorySrc =
426         JavaFileObjects.forSourceLines(
427             "test.FooFactory",
428             "package test;",
429             "",
430             "import dagger.assisted.AssistedFactory;",
431             "",
432             "@AssistedFactory",
433             "public interface FooFactory {",
434             "  Foo create(Integer arg);",
435             "}");
436     JavaFileObject barSrc =
437         JavaFileObjects.forSourceLines("test.Bar", "package test;", "", "interface Bar {}");
438     JavaFileObject injectSrc =
439         JavaFileObjects.forSourceLines(
440             "test.Foo",
441             "package test;",
442             "",
443             "import dagger.assisted.Assisted;",
444             "import dagger.assisted.AssistedInject;",
445             "",
446             "class Foo {",
447             "  @AssistedInject",
448             "  Foo(Bar arg, @Assisted Integer argProvider) {}",
449             "}");
450     JavaFileObject generatedSrc =
451         compilerMode
452             .javaFileBuilder("test.DaggerTestComponent")
453             .addLines(
454                 "package test;",
455                 "",
456                 "@ScopeMetadata",
457                 "@QualifierMetadata",
458                 GeneratedLines.generatedAnnotations())
459             .addLinesIn(
460                 FAST_INIT_MODE,
461                 "public final class Foo_Factory {",
462                 "  private final Provider<Bar> argProvider;",
463                 "",
464                 "  public Foo_Factory(Provider<Bar> argProvider) {",
465                 "    this.argProvider = argProvider;",
466                 "  }",
467                 "",
468                 "  public Foo get(Integer argProvider2) {",
469                 "    return newInstance(argProvider.get(), argProvider2);",
470                 "  }",
471                 "",
472                 "  public static Foo_Factory create(Provider<Bar> argProvider) {",
473                 "    return new Foo_Factory(argProvider);",
474                 "  }",
475                 "",
476                 "  public static Foo newInstance(Object arg, Integer argProvider) {",
477                 "    return new Foo((Bar) arg, argProvider);",
478                 "  }",
479                 "}")
480             .addLinesIn(
481                 DEFAULT_MODE,
482                 "public final class Foo_Factory {",
483                 "  private final Provider<Bar> argProvider;",
484                 "",
485                 "  public Foo_Factory(Provider<Bar> argProvider) {",
486                 "    this.argProvider = argProvider;",
487                 "  }",
488                 "",
489                 "  public Foo get(Integer argProvider2) {",
490                 "    return newInstance(argProvider.get(), argProvider2);",
491                 "  }",
492                 "",
493                 "  public static Foo_Factory create(Provider<Bar> argProvider) {",
494                 "    return new Foo_Factory(argProvider);",
495                 "  }",
496                 "",
497                 "  public static Foo newInstance(Object arg, Integer argProvider) {",
498                 "    return new Foo((Bar) arg, argProvider);",
499                 "  }",
500                 "}")
501             .build();
502     Compilation compilation =
503         compilerWithOptions(compilerMode.javacopts())
504             .compile(componentSrc, factorySrc, barSrc, injectSrc);
505     assertThat(compilation).succeeded();
506     assertThat(compilation)
507         .generatedSourceFile("test.Foo_Factory")
508         .containsElementsIn(generatedSrc);
509   }
510 
511   @Test
testParameterizedAssistParam()512   public void testParameterizedAssistParam() {
513     JavaFileObject componentSrc =
514         JavaFileObjects.forSourceLines(
515             "test.TestComponent",
516             "package test;",
517             "",
518             "import dagger.Component;",
519             "",
520             "@Component",
521             "interface TestComponent {",
522             "  FooFactory<String> getFooFactory();",
523             "}");
524     JavaFileObject factorySrc =
525         JavaFileObjects.forSourceLines(
526             "test.FooFactory",
527             "package test;",
528             "",
529             "import dagger.assisted.AssistedFactory;",
530             "",
531             "@AssistedFactory",
532             "public interface FooFactory<T> {",
533             "  Foo<T> create(T arg);",
534             "}");
535     JavaFileObject injectSrc =
536         JavaFileObjects.forSourceLines(
537             "test.Foo",
538             "package test;",
539             "",
540             "import dagger.assisted.Assisted;",
541             "import dagger.assisted.AssistedInject;",
542             "",
543             "class Foo<T> {",
544             "  @AssistedInject",
545             "  Foo(@Assisted T arg) {}",
546             "}");
547     JavaFileObject generatedSrc =
548         compilerMode
549             .javaFileBuilder("test.DaggerTestComponent")
550             .addLines("package test;", "", GeneratedLines.generatedAnnotations())
551             .addLinesIn(
552                 FAST_INIT_MODE,
553                 "final class DaggerTestComponent implements TestComponent {",
554                 "  private final DaggerTestComponent testComponent = this;",
555                 "  private Provider<FooFactory<String>> fooFactoryProvider;",
556                 "",
557                 "  @SuppressWarnings(\"unchecked\")",
558                 "  private void initialize() {",
559                 "    this.fooFactoryProvider = SingleCheck.provider(new"
560                     + " SwitchingProvider<FooFactory<String>>(testComponent, 0));",
561                 "  }",
562                 "",
563                 "  @Override",
564                 "  public FooFactory<String> getFooFactory() {",
565                 "    return fooFactoryProvider.get();",
566                 "  }",
567                 "  ",
568                 "  private static final class SwitchingProvider<T> implements Provider<T> {",
569                 "    private final DaggerTestComponent testComponent;",
570                 "    private final int id;",
571                 "",
572                 "    @SuppressWarnings(\"unchecked\")",
573                 "    @Override",
574                 "    public T get() {",
575                 "      switch (id) {",
576                 "        case 0: return (T) new FooFactory<String>() {",
577                 "          @Override",
578                 "          public Foo<String> create(String arg) {",
579                 "            return new Foo<String>(arg)",
580                 "          }",
581                 "        };",
582                 "",
583                 "        default: throw new AssertionError(id);",
584                 "      }",
585                 "    }",
586                 "  }",
587                 "}")
588             .addLinesIn(
589                 DEFAULT_MODE,
590                 "final class DaggerTestComponent implements TestComponent {",
591                 "  private final DaggerTestComponent testComponent = this;",
592                 "  private Foo_Factory<String> fooProvider;",
593                 "  private Provider<FooFactory<String>> fooFactoryProvider;",
594                 "",
595                 "  private DaggerTestComponent() {",
596                 "    initialize();",
597                 "  }",
598                 "",
599                 "  public static Builder builder() {",
600                 "    return new Builder();",
601                 "  }",
602                 "",
603                 "  public static TestComponent create() {",
604                 "    return new Builder().build();",
605                 "  }",
606                 "",
607                 "  @SuppressWarnings(\"unchecked\")",
608                 "  private void initialize() {",
609                 "    this.fooProvider = Foo_Factory.create();",
610                 "    this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);",
611                 "  }",
612                 "",
613                 "  @Override",
614                 "  public FooFactory<String> getFooFactory() {",
615                 "    return fooFactoryProvider.get();",
616                 "  }",
617                 "}")
618             .build();
619     Compilation compilation =
620         compilerWithOptions(compilerMode.javacopts()).compile(componentSrc, factorySrc, injectSrc);
621     assertThat(compilation).succeeded();
622     assertThat(compilation)
623         .generatedSourceFile("test.DaggerTestComponent")
624         .containsElementsIn(generatedSrc);
625   }
626 }
627