• 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 dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
20 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
21 
22 import androidx.room.compiler.processing.util.Source;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25 import dagger.testing.compile.CompilerTests;
26 import dagger.testing.golden.GoldenFileRule;
27 import org.junit.Rule;
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 ModuleFactoryGeneratorTest {
34 
35   private static final Source NULLABLE =
36         CompilerTests.javaSource(
37           "test.Nullable", "package test;", "public @interface Nullable {}");
38 
39   @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule();
40 
41   // TODO(gak): add tests for invalid combinations of scope and qualifier annotations like we have
42   // for @Inject
43 
providesMethodNotInModule()44   @Test public void providesMethodNotInModule() {
45     assertThatMethodInUnannotatedClass("@Provides String provideString() { return null; }")
46         .hasError("@Provides methods can only be present within a @Module or @ProducerModule");
47   }
48 
providesMethodAbstract()49   @Test public void providesMethodAbstract() {
50     assertThatModuleMethod("@Provides abstract String abstractMethod();")
51         .hasError("@Provides methods cannot be abstract");
52   }
53 
providesMethodPrivate()54   @Test public void providesMethodPrivate() {
55     assertThatModuleMethod("@Provides private String privateMethod() { return null; }")
56         .hasError("@Provides methods cannot be private");
57   }
58 
providesMethodReturnVoid()59   @Test public void providesMethodReturnVoid() {
60     assertThatModuleMethod("@Provides void voidMethod() {}")
61         .hasError("@Provides methods must return a value (not void)");
62   }
63 
64   @Test
providesMethodReturnsProvider()65   public void providesMethodReturnsProvider() {
66     assertThatModuleMethod("@Provides Provider<String> provideProvider() {}")
67         .hasError("@Provides methods must not return framework types");
68   }
69 
70   @Test
providesMethodReturnsLazy()71   public void providesMethodReturnsLazy() {
72     assertThatModuleMethod("@Provides Lazy<String> provideLazy() {}")
73         .hasError("@Provides methods must not return framework types");
74   }
75 
76   @Test
providesMethodReturnsMembersInjector()77   public void providesMethodReturnsMembersInjector() {
78     assertThatModuleMethod("@Provides MembersInjector<String> provideMembersInjector() {}")
79         .hasError("@Provides methods must not return framework types");
80   }
81 
82   @Test
providesMethodReturnsProducer()83   public void providesMethodReturnsProducer() {
84     assertThatModuleMethod("@Provides Producer<String> provideProducer() {}")
85         .hasError("@Provides methods must not return framework types");
86   }
87 
88   @Test
providesMethodReturnsProduced()89   public void providesMethodReturnsProduced() {
90     assertThatModuleMethod("@Provides Produced<String> provideProduced() {}")
91         .hasError("@Provides methods must not return framework types");
92   }
93 
providesMethodWithTypeParameter()94   @Test public void providesMethodWithTypeParameter() {
95     assertThatModuleMethod("@Provides <T> String typeParameter() { return null; }")
96         .hasError("@Provides methods may not have type parameters");
97   }
98 
providesMethodSetValuesWildcard()99   @Test public void providesMethodSetValuesWildcard() {
100     assertThatModuleMethod("@Provides @ElementsIntoSet Set<?> provideWildcard() { return null; }")
101         .hasError(
102             "@Provides methods must return a primitive, an array, a type variable, "
103                 + "or a declared type");
104   }
105 
providesMethodSetValuesRawSet()106   @Test public void providesMethodSetValuesRawSet() {
107     assertThatModuleMethod("@Provides @ElementsIntoSet Set provideSomething() { return null; }")
108         .hasError("@Provides methods annotated with @ElementsIntoSet cannot return a raw Set");
109   }
110 
providesMethodSetValuesNotASet()111   @Test public void providesMethodSetValuesNotASet() {
112     assertThatModuleMethod(
113             "@Provides @ElementsIntoSet List<String> provideStrings() { return null; }")
114         .hasError("@Provides methods annotated with @ElementsIntoSet must return a Set");
115   }
116 
modulesWithTypeParamsMustBeAbstract()117   @Test public void modulesWithTypeParamsMustBeAbstract() {
118     Source moduleFile =
119         CompilerTests.javaSource(
120             "test.TestModule",
121             "package test;",
122             "",
123             "import dagger.Module;",
124             "",
125             "@Module",
126             "final class TestModule<A> {}");
127     CompilerTests.daggerCompiler(moduleFile)
128         .compile(
129             subject -> {
130               subject.hasErrorCount(1);
131               subject.hasErrorContaining("Modules with type parameters must be abstract")
132                   .onSource(moduleFile)
133                   .onLine(6);
134             });
135   }
136 
provideOverriddenByNoProvide()137   @Test public void provideOverriddenByNoProvide() {
138     Source parent =
139         CompilerTests.javaSource(
140             "test.Parent",
141             "package test;",
142             "",
143             "import dagger.Module;",
144             "import dagger.Provides;",
145             "",
146             "@Module",
147             "class Parent {",
148             "  @Provides String foo() { return null; }",
149             "}");
150     assertThatModuleMethod("String foo() { return null; }")
151         .withDeclaration("@Module class %s extends Parent { %s }")
152         .withAdditionalSources(parent)
153         .hasError(
154             "Binding methods may not be overridden in modules. Overrides: "
155                 + "@Provides String test.Parent.foo()");
156   }
157 
provideOverriddenByProvide()158   @Test public void provideOverriddenByProvide() {
159     Source parent =
160         CompilerTests.javaSource(
161             "test.Parent",
162             "package test;",
163             "",
164             "import dagger.Module;",
165             "import dagger.Provides;",
166             "",
167             "@Module",
168             "class Parent {",
169             "  @Provides String foo() { return null; }",
170             "}");
171     assertThatModuleMethod("@Provides String foo() { return null; }")
172         .withDeclaration("@Module class %s extends Parent { %s }")
173         .withAdditionalSources(parent)
174         .hasError(
175             "Binding methods may not override another method. Overrides: "
176                 + "@Provides String test.Parent.foo()");
177   }
178 
providesOverridesNonProvides()179   @Test public void providesOverridesNonProvides() {
180     Source parent =
181         CompilerTests.javaSource(
182             "test.Parent",
183             "package test;",
184             "",
185             "import dagger.Module;",
186             "",
187             "@Module",
188             "class Parent {",
189             "  String foo() { return null; }",
190             "}");
191     assertThatModuleMethod("@Provides String foo() { return null; }")
192         .withDeclaration("@Module class %s extends Parent { %s }")
193         .withAdditionalSources(parent)
194         .hasError(
195             "Binding methods may not override another method. Overrides: "
196                 + "String test.Parent.foo()");
197   }
198 
validatesIncludedModules()199   @Test public void validatesIncludedModules() {
200     Source module =
201         CompilerTests.javaSource(
202             "test.Parent",
203             "package test;",
204             "",
205             "import dagger.Module;",
206             "",
207             "@Module(",
208             "    includes = {",
209             "        Void.class,",
210             "        String.class,",
211             "    }",
212             ")",
213             "class TestModule {}");
214 
215     CompilerTests.daggerCompiler(module)
216         .compile(
217             subject -> {
218               subject.hasErrorCount(2);
219               // We avoid asserting on the line number because ksp and javac report different lines.
220               // The main issue here is that ksp doesn't allow reporting errors on individual
221               // annotation values, it only allows reporting errors on annotations themselves.
222               subject.hasErrorContaining(
223                       "java.lang.Void is listed as a module, but is not annotated with @Module")
224                   .onSource(module);
225               subject.hasErrorContaining(
226                       "java.lang.String is listed as a module, but is not annotated with @Module")
227                   .onSource(module);
228             });
229   }
230 
singleProvidesMethodNoArgs()231   @Test public void singleProvidesMethodNoArgs() {
232     Source moduleFile =
233         CompilerTests.javaSource(
234             "test.TestModule",
235             "package test;",
236             "",
237             "import dagger.Module;",
238             "import dagger.Provides;",
239             "",
240             "@Module",
241             "final class TestModule {",
242             "  @Provides String provideString() {",
243             "    return \"\";",
244             "  }",
245             "}");
246     CompilerTests.daggerCompiler(moduleFile)
247         .compile(
248             subject -> {
249               subject.hasErrorCount(0);
250               subject.generatedSource(
251                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
252             });
253   }
254 
singleProvidesMethodNoArgs_disableNullable()255   @Test public void singleProvidesMethodNoArgs_disableNullable() {
256     Source moduleFile =
257         CompilerTests.javaSource(
258             "test.TestModule",
259             "package test;",
260             "",
261             "import dagger.Module;",
262             "import dagger.Provides;",
263             "",
264             "@Module",
265             "final class TestModule {",
266             "  @Provides String provideString() {",
267             "    return \"\";",
268             "  }",
269             "}");
270     CompilerTests.daggerCompiler(moduleFile)
271         .withProcessingOptions(ImmutableMap.of("dagger.nullableValidation", "WARNING"))
272         .compile(
273             subject -> {
274               subject.hasErrorCount(0);
275               subject.generatedSource(
276                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
277             });
278   }
279 
nullableProvides()280   @Test public void nullableProvides() {
281     Source moduleFile =
282         CompilerTests.javaSource(
283             "test.TestModule",
284             "package test;",
285             "",
286             "import dagger.Module;",
287             "import dagger.Provides;",
288             "",
289             "@Module",
290             "final class TestModule {",
291             "  @Provides @Nullable String provideString() { return null; }",
292             "}");
293     CompilerTests.daggerCompiler(moduleFile, NULLABLE)
294         .compile(
295             subject -> {
296               subject.hasErrorCount(0);
297               subject.generatedSource(
298                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
299             });
300   }
301 
multipleProvidesMethods()302   @Test public void multipleProvidesMethods() {
303     Source classXFile =
304         CompilerTests.javaSource("test.X",
305         "package test;",
306         "",
307         "import javax.inject.Inject;",
308         "",
309         "class X {",
310         "  @Inject public String s;",
311         "}");
312     Source moduleFile =
313         CompilerTests.javaSource(
314             "test.TestModule",
315             "package test;",
316             "",
317             "import dagger.MembersInjector;",
318             "import dagger.Module;",
319             "import dagger.Provides;",
320             "import java.util.Arrays;",
321             "import java.util.List;",
322             "",
323             "@Module",
324             "final class TestModule {",
325             "  @Provides List<Object> provideObjects(",
326             "      @QualifierA Object a, @QualifierB Object b, MembersInjector<X> xInjector) {",
327             "    return Arrays.asList(a, b);",
328             "  }",
329             "",
330             "  @Provides @QualifierA Object provideAObject() {",
331             "    return new Object();",
332             "  }",
333             "",
334             "  @Provides @QualifierB Object provideBObject() {",
335             "    return new Object();",
336             "  }",
337             "}");
338     CompilerTests.daggerCompiler(classXFile, moduleFile, QUALIFIER_A, QUALIFIER_B)
339         .compile(
340             subject -> {
341               subject.hasErrorCount(0);
342               subject.generatedSource(
343                   goldenFileRule.goldenSource("test/TestModule_ProvideObjectsFactory"));
344             });
345   }
346 
providesSetElement()347   @Test public void providesSetElement() {
348     Source moduleFile =
349         CompilerTests.javaSource(
350             "test.TestModule",
351             "package test;",
352             "",
353             "import java.util.logging.Logger;",
354             "import dagger.Module;",
355             "import dagger.Provides;",
356             "import dagger.multibindings.IntoSet;",
357             "",
358             "@Module",
359             "final class TestModule {",
360             "  @Provides @IntoSet String provideString() {",
361             "    return \"\";",
362             "  }",
363             "}");
364     CompilerTests.daggerCompiler(moduleFile)
365         .compile(
366             subject -> {
367               subject.hasErrorCount(0);
368               subject.generatedSource(
369                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
370             });
371   }
372 
providesSetElementWildcard()373   @Test public void providesSetElementWildcard() {
374     Source moduleFile =
375         CompilerTests.javaSource(
376             "test.TestModule",
377             "package test;",
378             "",
379             "import java.util.logging.Logger;",
380             "import dagger.Module;",
381             "import dagger.Provides;",
382             "import dagger.multibindings.IntoSet;",
383             "import java.util.ArrayList;",
384             "import java.util.List;",
385             "",
386             "@Module",
387             "final class TestModule {",
388             "  @Provides @IntoSet List<List<?>> provideWildcardList() {",
389             "    return new ArrayList<>();",
390             "  }",
391             "}");
392     CompilerTests.daggerCompiler(moduleFile)
393         .compile(
394             subject -> {
395               subject.hasErrorCount(0);
396               subject.generatedSource(
397                   goldenFileRule.goldenSource("test/TestModule_ProvideWildcardListFactory"));
398             });
399   }
400 
providesSetValues()401   @Test public void providesSetValues() {
402     Source moduleFile =
403         CompilerTests.javaSource(
404             "test.TestModule",
405             "package test;",
406             "",
407             "import dagger.Module;",
408             "import dagger.Provides;",
409             "import dagger.multibindings.ElementsIntoSet;",
410             "import java.util.Set;",
411             "",
412             "@Module",
413             "final class TestModule {",
414             "  @Provides @ElementsIntoSet Set<String> provideStrings() {",
415             "    return null;",
416             "  }",
417             "}");
418     CompilerTests.daggerCompiler(moduleFile)
419         .compile(
420             subject -> {
421               subject.hasErrorCount(0);
422               subject.generatedSource(
423                   goldenFileRule.goldenSource("test/TestModule_ProvideStringsFactory"));
424             });
425   }
426 
multipleProvidesMethodsWithSameName()427   @Test public void multipleProvidesMethodsWithSameName() {
428     Source moduleFile =
429         CompilerTests.javaSource("test.TestModule",
430         "package test;",
431         "",
432         "import dagger.Module;",
433         "import dagger.Provides;",
434         "",
435         "@Module",
436         "final class TestModule {",
437         "  @Provides Object provide(int i) {",
438         "    return i;",
439         "  }",
440         "",
441         "  @Provides String provide() {",
442         "    return \"\";",
443         "  }",
444         "}");
445     CompilerTests.daggerCompiler(moduleFile)
446         .compile(
447             subject -> {
448               subject.hasErrorCount(2);
449               subject.hasErrorContaining(
450                       "Cannot have more than one binding method with the same name in a single "
451                           + "module")
452                   .onSource(moduleFile)
453                   .onLine(8);
454               subject.hasErrorContaining(
455                       "Cannot have more than one binding method with the same name in a single "
456                           + "module")
457                   .onSource(moduleFile)
458                   .onLine(12);
459             });
460   }
461 
462   @Test
providesMethodThrowsChecked()463   public void providesMethodThrowsChecked() {
464     Source moduleFile =
465         CompilerTests.javaSource(
466             "test.TestModule",
467             "package test;",
468             "",
469             "import dagger.Module;",
470             "import dagger.Provides;",
471             "",
472             "@Module",
473             "final class TestModule {",
474             "  @Provides int i() throws Exception {",
475             "    return 0;",
476             "  }",
477             "",
478             "  @Provides String s() throws Throwable {",
479             "    return \"\";",
480             "  }",
481             "}");
482     CompilerTests.daggerCompiler(moduleFile)
483         .compile(
484             subject -> {
485               subject.hasErrorCount(2);
486               subject.hasErrorContaining("@Provides methods may only throw unchecked exceptions")
487                   .onSource(moduleFile)
488                   .onLine(8);
489               subject.hasErrorContaining("@Provides methods may only throw unchecked exceptions")
490                   .onSource(moduleFile)
491                   .onLine(12);
492             });
493   }
494 
495   @Test
providedTypes()496   public void providedTypes() {
497     Source moduleFile =
498         CompilerTests.javaSource(
499             "test.TestModule",
500             "package test;",
501             "",
502             "import dagger.Module;",
503             "import dagger.Provides;",
504             "import java.io.Closeable;",
505             "import java.util.Set;",
506             "",
507             "@Module",
508             "final class TestModule {",
509             "  @Provides String string() {",
510             "    return null;",
511             "  }",
512             "",
513             "  @Provides Set<String> strings() {",
514             "    return null;",
515             "  }",
516             "",
517             "  @Provides Set<? extends Closeable> closeables() {",
518             "    return null;",
519             "  }",
520             "",
521             "  @Provides String[] stringArray() {",
522             "    return null;",
523             "  }",
524             "",
525             "  @Provides int integer() {",
526             "    return 0;",
527             "  }",
528             "",
529             "  @Provides int[] integers() {",
530             "    return null;",
531             "  }",
532             "}");
533     CompilerTests.daggerCompiler(moduleFile).compile(subject -> subject.hasErrorCount(0));
534   }
535 
536   @Test
privateModule()537   public void privateModule() {
538     Source moduleFile =
539         CompilerTests.javaSource(
540             "test.Enclosing",
541             "package test;",
542             "",
543             "import dagger.Module;",
544             "",
545             "final class Enclosing {",
546             "  @Module private static final class PrivateModule {",
547             "  }",
548             "}");
549     CompilerTests.daggerCompiler(moduleFile)
550         .compile(
551             subject -> {
552               subject.hasErrorCount(1);
553               subject.hasErrorContaining("Modules cannot be private")
554                   .onSource(moduleFile)
555                   .onLine(6);
556             });
557   }
558 
559   @Test
privateModule_kotlin()560   public void privateModule_kotlin() {
561     Source moduleFile =
562         CompilerTests.kotlinSource(
563             "test.TestModule.kt",
564             "package test",
565             "",
566             "import dagger.Component",
567             "import dagger.Module",
568             "import dagger.Provides",
569             "",
570             "@Module",
571             "private class TestModule {",
572             "  @Provides fun provideInt(): Int = 1",
573             "}");
574 
575     CompilerTests.daggerCompiler(moduleFile)
576         .compile(
577             subject -> {
578               subject.hasErrorCount(1);
579               subject
580                   .hasErrorContaining("Modules cannot be private")
581                   .onSource(moduleFile);
582             });
583   }
584 
585   @Test
enclosedInPrivateModule()586   public void enclosedInPrivateModule() {
587     Source moduleFile =
588         CompilerTests.javaSource("test.Enclosing",
589         "package test;",
590         "",
591         "import dagger.Module;",
592         "",
593         "final class Enclosing {",
594         "  private static final class PrivateEnclosing {",
595         "    @Module static final class TestModule {",
596         "    }",
597         "  }",
598         "}");
599     CompilerTests.daggerCompiler(moduleFile)
600         .compile(
601             subject -> {
602               subject.hasErrorCount(1);
603               subject.hasErrorContaining("Modules cannot be enclosed in private types")
604                   .onSource(moduleFile)
605                   .onLine(7);
606             });
607   }
608 
609   @Test
publicModuleNonPublicIncludes()610   public void publicModuleNonPublicIncludes() {
611     Source publicModuleFile =
612         CompilerTests.javaSource("test.PublicModule",
613         "package test;",
614         "",
615         "import dagger.Module;",
616         "",
617         "@Module(includes = {",
618         "    BadNonPublicModule.class, OtherPublicModule.class, OkNonPublicModule.class",
619         "})",
620         "public final class PublicModule {",
621         "}");
622     Source badNonPublicModuleFile =
623         CompilerTests.javaSource(
624             "test.BadNonPublicModule",
625             "package test;",
626             "",
627             "import dagger.Module;",
628             "import dagger.Provides;",
629             "",
630             "@Module",
631             "final class BadNonPublicModule {",
632             "  @Provides",
633             "  int provideInt() {",
634             "    return 42;",
635             "  }",
636             "}");
637     Source okNonPublicModuleFile =
638         CompilerTests.javaSource("test.OkNonPublicModule",
639         "package test;",
640         "",
641         "import dagger.Module;",
642         "import dagger.Provides;",
643         "",
644         "@Module",
645         "final class OkNonPublicModule {",
646         "  @Provides",
647         "  static String provideString() {",
648         "    return \"foo\";",
649         "  }",
650         "}");
651     Source otherPublicModuleFile =
652         CompilerTests.javaSource("test.OtherPublicModule",
653         "package test;",
654         "",
655         "import dagger.Module;",
656         "",
657         "@Module",
658         "public final class OtherPublicModule {",
659         "}");
660     CompilerTests.daggerCompiler(
661             publicModuleFile,
662             badNonPublicModuleFile,
663             okNonPublicModuleFile,
664             otherPublicModuleFile)
665         .compile(
666             subject -> {
667               subject.hasErrorCount(1);
668               subject.hasErrorContaining(
669                       "This module is public, but it includes non-public (or effectively non-"
670                           + "public) modules (test.BadNonPublicModule) that have non-static, non-"
671                           + "abstract binding methods. Either reduce the visibility of this module"
672                           + ", make the included modules public, or make all of the binding "
673                           + "methods on the included modules abstract or static.")
674                   .onSource(publicModuleFile)
675                   .onLine(8);
676             });
677   }
678 
679   @Test
genericSubclassedModule()680   public void genericSubclassedModule() {
681     Source parent =
682         CompilerTests.javaSource(
683             "test.ParentModule",
684             "package test;",
685             "",
686             "import dagger.Module;",
687             "import dagger.Provides;",
688             "import dagger.multibindings.IntoSet;",
689             "import dagger.multibindings.IntoMap;",
690             "import dagger.multibindings.StringKey;",
691             "import java.util.List;",
692             "import java.util.ArrayList;",
693             "",
694             "@Module",
695             "abstract class ParentModule<A extends CharSequence,",
696             "                            B,",
697             "                            C extends Number & Comparable<C>> {",
698             "  @Provides List<B> provideListB(B b) {",
699             "    List<B> list = new ArrayList<B>();",
700             "    list.add(b);",
701             "    return list;",
702             "  }",
703             "",
704             "  @Provides @IntoSet B provideBElement(B b) {",
705             "    return b;",
706             "  }",
707             "",
708             "  @Provides @IntoMap @StringKey(\"b\") B provideBEntry(B b) {",
709             "    return b;",
710             "  }",
711             "}");
712     Source numberChild =
713         CompilerTests.javaSource("test.ChildNumberModule",
714         "package test;",
715         "",
716         "import dagger.Module;",
717         "import dagger.Provides;",
718         "",
719         "@Module",
720         "class ChildNumberModule extends ParentModule<String, Number, Double> {",
721         "  @Provides Number provideNumber() { return 1; }",
722         "}");
723     Source integerChild =
724         CompilerTests.javaSource("test.ChildIntegerModule",
725         "package test;",
726         "",
727         "import dagger.Module;",
728         "import dagger.Provides;",
729         "",
730         "@Module",
731         "class ChildIntegerModule extends ParentModule<StringBuilder, Integer, Float> {",
732         "  @Provides Integer provideInteger() { return 2; }",
733         "}");
734     Source component =
735         CompilerTests.javaSource("test.C",
736         "package test;",
737         "",
738         "import dagger.Component;",
739         "import java.util.List;",
740         "",
741         "@Component(modules={ChildNumberModule.class, ChildIntegerModule.class})",
742         "interface C {",
743         "  List<Number> numberList();",
744         "  List<Integer> integerList();",
745         "}");
746     CompilerTests.daggerCompiler(parent, numberChild, integerChild, component)
747         .compile(
748             subject -> {
749               subject.hasErrorCount(0);
750               subject.generatedSource(
751                   goldenFileRule.goldenSource("test/ParentModule_ProvideListBFactory"));
752               subject.generatedSource(
753                   goldenFileRule.goldenSource("test/ParentModule_ProvideBElementFactory"));
754               subject.generatedSource(
755                   goldenFileRule.goldenSource("test/ParentModule_ProvideBEntryFactory"));
756               subject.generatedSource(
757                   goldenFileRule.goldenSource("test/ChildNumberModule_ProvideNumberFactory"));
758               subject.generatedSource(
759                   goldenFileRule.goldenSource("test/ChildIntegerModule_ProvideIntegerFactory"));
760             });
761   }
762 
parameterizedModuleWithStaticProvidesMethodOfGenericType()763   @Test public void parameterizedModuleWithStaticProvidesMethodOfGenericType() {
764     Source moduleFile =
765         CompilerTests.javaSource(
766             "test.ParameterizedModule",
767             "package test;",
768             "",
769             "import dagger.Module;",
770             "import dagger.Provides;",
771             "import java.util.List;",
772             "import java.util.ArrayList;",
773             "import java.util.Map;",
774             "import java.util.HashMap;",
775             "",
776             "@Module abstract class ParameterizedModule<T> {",
777             "  @Provides List<T> provideListT() {",
778             "    return new ArrayList<>();",
779             "  }",
780             "",
781             "  @Provides static Map<String, Number> provideMapStringNumber() {",
782             "    return new HashMap<>();",
783             "  }",
784             "",
785             "  @Provides static Object provideNonGenericType() {",
786             "    return new Object();",
787             "  }",
788             "",
789             "  @Provides static String provideNonGenericTypeWithDeps(Object o) {",
790             "    return o.toString();",
791             "  }",
792             "}");
793     CompilerTests.daggerCompiler(moduleFile)
794         .compile(
795             subject -> {
796               subject.hasErrorCount(0);
797               subject.generatedSource(
798                   goldenFileRule.goldenSource(
799                       "test/ParameterizedModule_ProvideMapStringNumberFactory"));
800               subject.generatedSource(
801                   goldenFileRule.goldenSource(
802                       "test/ParameterizedModule_ProvideNonGenericTypeFactory"));
803               subject.generatedSource(
804                   goldenFileRule.goldenSource(
805                       "test/ParameterizedModule_ProvideNonGenericTypeWithDepsFactory"));
806             });
807   }
808 
809   private static final Source QUALIFIER_A =
810         CompilerTests.javaSource(
811           "test.QualifierA",
812           "package test;",
813           "",
814           "import javax.inject.Qualifier;",
815           "",
816           "@Qualifier @interface QualifierA {}");
817 
818   private static final Source QUALIFIER_B =
819         CompilerTests.javaSource(
820           "test.QualifierB",
821           "package test;",
822           "",
823           "import javax.inject.Qualifier;",
824           "",
825           "@Qualifier @interface QualifierB {}");
826 
827   @Test
providesMethodMultipleQualifiersOnMethod()828   public void providesMethodMultipleQualifiersOnMethod() {
829     Source moduleFile =
830         CompilerTests.javaSource("test.TestModule",
831         "package test;",
832         "",
833         "import dagger.Module;",
834         "import dagger.Provides;",
835         "",
836         "@Module",
837         "final class TestModule {",
838         "  @Provides",
839         "  @QualifierA",
840         "  @QualifierB",
841         "  String provideString() {",
842         "    return \"foo\";",
843         "  }",
844         "}");
845     CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B)
846         .compile(
847             subject -> {
848               // There are 2 errors -- 1 per qualifier.
849               subject.hasErrorCount(2);
850               subject.hasErrorContaining("may not use more than one @Qualifier")
851                   .onSource(moduleFile)
852                   .onLine(9);
853               subject.hasErrorContaining("may not use more than one @Qualifier")
854                   .onSource(moduleFile)
855                   .onLine(10);
856             });
857   }
858 
859   @Test
providesMethodMultipleQualifiersOnParameter()860   public void providesMethodMultipleQualifiersOnParameter() {
861     Source moduleFile =
862         CompilerTests.javaSource(
863             "test.TestModule",
864             "package test;",
865             "",
866             "import dagger.Module;",
867             "import dagger.Provides;",
868             "",
869             "@Module",
870             "final class TestModule {",
871             "  @Provides",
872             "  static String provideString(",
873             "      @QualifierA",
874             "      @QualifierB",
875             "      Object object) {",
876             "    return \"foo\";",
877             "  }",
878             "}");
879     CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B)
880         .compile(
881             subject -> {
882               // There are two errors -- 1 per qualifier.
883               subject.hasErrorCount(2);
884               subject.hasErrorContaining("may not use more than one @Qualifier")
885                   .onSource(moduleFile)
886                   .onLine(10);
887               subject.hasErrorContaining("may not use more than one @Qualifier")
888                   .onSource(moduleFile)
889                   .onLine(11);
890             });
891   }
892 
893   @Test
providesMethodWildcardDependency()894   public void providesMethodWildcardDependency() {
895     Source moduleFile =
896         CompilerTests.javaSource(
897             "test.TestModule",
898             "package test;",
899             "",
900             "import dagger.Module;",
901             "import dagger.Provides;",
902             "import javax.inject.Provider;",
903             "",
904             "@Module",
905             "final class TestModule {",
906             "  @Provides static String provideString(Provider<? extends Number> numberProvider) {",
907             "    return \"foo\";",
908             "  }",
909             "}");
910     CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B)
911         .compile(
912             subject -> {
913               subject.hasErrorCount(1);
914               subject.hasErrorContaining(
915                   "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or "
916                       + "Produced<T> when T is a wildcard type such as ? extends java.lang.Number");
917             });
918   }
919 
920   private static final Source SCOPE_A =
921         CompilerTests.javaSource(
922           "test.ScopeA",
923           "package test;",
924           "",
925           "import javax.inject.Scope;",
926           "",
927           "@Scope @interface ScopeA {}");
928 
929   private static final Source SCOPE_B =
930         CompilerTests.javaSource(
931           "test.ScopeB",
932           "package test;",
933           "",
934           "import javax.inject.Scope;",
935           "",
936           "@Scope @interface ScopeB {}");
937 
938   @Test
providesMethodMultipleScopes()939   public void providesMethodMultipleScopes() {
940     Source moduleFile =
941         CompilerTests.javaSource(
942             "test.TestModule",
943             "package test;",
944             "",
945             "import dagger.Module;",
946             "import dagger.Provides;",
947             "",
948             "@Module",
949             "final class TestModule {",
950             "  @Provides",
951             "  @ScopeA",
952             "  @ScopeB",
953             "  String provideString() {",
954             "    return \"foo\";",
955             "  }",
956             "}");
957     CompilerTests.daggerCompiler(moduleFile, SCOPE_A, SCOPE_B)
958         .compile(
959             subject -> {
960               subject.hasErrorCount(2);
961               subject.hasErrorContaining("cannot use more than one @Scope")
962                   .onSource(moduleFile)
963                   .onLineContaining("@ScopeA");
964               subject.hasErrorContaining("cannot use more than one @Scope")
965                   .onSource(moduleFile)
966                   .onLineContaining("@ScopeB");
967             });
968   }
969 
providerDependsOnProduced()970   @Test public void providerDependsOnProduced() {
971     Source moduleFile =
972         CompilerTests.javaSource(
973             "test.TestModule",
974             "package test;",
975             "",
976             "import dagger.Module;",
977             "import dagger.Provides;",
978             "import dagger.producers.Producer;",
979             "",
980             "@Module",
981             "final class TestModule {",
982             "  @Provides String provideString(Producer<Integer> producer) {",
983             "    return \"foo\";",
984             "  }",
985             "}");
986     CompilerTests.daggerCompiler(moduleFile)
987         .compile(
988             subject -> {
989               subject.hasErrorCount(1);
990               subject.hasErrorContaining("Producer may only be injected in @Produces methods");
991             });
992   }
993 
providerDependsOnProducer()994   @Test public void providerDependsOnProducer() {
995     Source moduleFile =
996         CompilerTests.javaSource(
997             "test.TestModule",
998             "package test;",
999             "",
1000             "import dagger.Module;",
1001             "import dagger.Provides;",
1002             "import dagger.producers.Produced;",
1003             "",
1004             "@Module",
1005             "final class TestModule {",
1006             "  @Provides String provideString(Produced<Integer> produced) {",
1007             "    return \"foo\";",
1008             "  }",
1009             "}");
1010     CompilerTests.daggerCompiler(moduleFile)
1011         .compile(
1012             subject -> {
1013               subject.hasErrorCount(1);
1014               subject.hasErrorContaining("Produced may only be injected in @Produces methods");
1015             });
1016   }
1017 
1018   @Test
proxyMethodsConflictWithOtherFactoryMethods()1019   public void proxyMethodsConflictWithOtherFactoryMethods() throws Exception {
1020     Source module =
1021         CompilerTests.javaSource(
1022             "test.TestModule",
1023             "package test;",
1024             "",
1025             "import dagger.Module;",
1026             "import dagger.Provides;",
1027             "",
1028             "@Module",
1029             "interface TestModule {",
1030             "  @Provides",
1031             "  static int get() { return 1; }",
1032             "",
1033             "  @Provides",
1034             "  static boolean create() { return true; }",
1035             "}");
1036 
1037     CompilerTests.daggerCompiler(module)
1038         .compile(
1039             subject -> {
1040               subject.hasErrorCount(0);
1041               subject.generatedSource(goldenFileRule.goldenSource("test/TestModule_GetFactory"));
1042               subject.generatedSource(goldenFileRule.goldenSource("test/TestModule_CreateFactory"));
1043             });
1044   }
1045 
1046   @Test
testScopedMetadataOnStaticProvides()1047   public void testScopedMetadataOnStaticProvides() throws Exception {
1048     Source module =
1049         CompilerTests.javaSource(
1050             "test.ScopedBinding",
1051             "package test;",
1052             "",
1053             "import dagger.Module;",
1054             "import dagger.Provides;",
1055             "import javax.inject.Singleton;",
1056             "",
1057             "@Module",
1058             "interface MyModule {",
1059             "  @NonScope",
1060             "  @Singleton",
1061             "  @Provides",
1062             "  static String provideString() {",
1063             "    return \"\";",
1064             "  }",
1065             "}");
1066     Source nonScope =
1067         CompilerTests.javaSource(
1068             "test.NonScope",
1069             "package test;",
1070             "",
1071             "@interface NonScope {}");
1072 
1073     CompilerTests.daggerCompiler(module, nonScope)
1074         .compile(
1075             subject -> {
1076               subject.hasErrorCount(0);
1077               subject.generatedSource(
1078                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1079             });
1080   }
1081 
1082   @Test
testScopedMetadataOnNonStaticProvides()1083   public void testScopedMetadataOnNonStaticProvides() throws Exception {
1084     Source module =
1085         CompilerTests.javaSource(
1086             "test.ScopedBinding",
1087             "package test;",
1088             "",
1089             "import dagger.Module;",
1090             "import dagger.Provides;",
1091             "import javax.inject.Singleton;",
1092             "",
1093             "@Module",
1094             "class MyModule {",
1095             "  @NonScope",
1096             "  @Singleton",
1097             "  @Provides",
1098             "  String provideString() {",
1099             "    return \"\";",
1100             "  }",
1101             "}");
1102     Source nonScope =
1103         CompilerTests.javaSource(
1104             "test.NonScope",
1105             "package test;",
1106             "",
1107             "@interface NonScope {}");
1108 
1109     CompilerTests.daggerCompiler(module, nonScope)
1110         .compile(
1111             subject -> {
1112               subject.hasErrorCount(0);
1113               subject.generatedSource(
1114                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1115             });
1116   }
1117 
1118   @Test
testScopeMetadataWithCustomScope()1119   public void testScopeMetadataWithCustomScope() throws Exception {
1120     Source module =
1121         CompilerTests.javaSource(
1122             "test.ScopedBinding",
1123             "package test;",
1124             "",
1125             "import dagger.Module;",
1126             "import dagger.Provides;",
1127             "import javax.inject.Singleton;",
1128             "",
1129             "@Module",
1130             "interface MyModule {",
1131             "  @NonScope(\"someValue\")",
1132             "  @CustomScope(\"someOtherValue\")",
1133             "  @Provides",
1134             "  static String provideString() {",
1135             "    return \"\";",
1136             "  }",
1137             "}");
1138     Source customScope =
1139         CompilerTests.javaSource(
1140             "test.CustomScope",
1141             "package test;",
1142             "",
1143             "import javax.inject.Scope;",
1144             "",
1145             "@Scope",
1146             "@interface CustomScope {",
1147             "  String value();",
1148             "}");
1149     Source nonScope =
1150         CompilerTests.javaSource(
1151             "test.NonScope",
1152             "package test;",
1153             "",
1154             "@interface NonScope {",
1155             "  String value();",
1156             "}");
1157 
1158     CompilerTests.daggerCompiler(module, customScope, nonScope)
1159         .compile(
1160             subject -> {
1161               subject.hasErrorCount(0);
1162               subject.generatedSource(
1163                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1164             });
1165   }
1166 
1167   @Test
testQualifierMetadataOnProvides()1168   public void testQualifierMetadataOnProvides() throws Exception {
1169     Source module =
1170         CompilerTests.javaSource(
1171             "test.ScopedBinding",
1172             "package test;",
1173             "",
1174             "import dagger.Module;",
1175             "import dagger.Provides;",
1176             "import javax.inject.Singleton;",
1177             "",
1178             "@Module",
1179             "interface MyModule {",
1180             "  @Provides",
1181             "  @NonQualifier",
1182             "  @MethodQualifier",
1183             "  static String provideString(@NonQualifier @ParamQualifier int i) {",
1184             "    return \"\";",
1185             "  }",
1186             "}");
1187     Source methodQualifier =
1188         CompilerTests.javaSource(
1189             "test.MethodQualifier",
1190             "package test;",
1191             "",
1192             "import javax.inject.Qualifier;",
1193             "",
1194             "@Qualifier",
1195             "@interface MethodQualifier {}");
1196     Source paramQualifier =
1197         CompilerTests.javaSource(
1198             "test.ParamQualifier",
1199             "package test;",
1200             "",
1201             "import javax.inject.Qualifier;",
1202             "",
1203             "@Qualifier",
1204             "@interface ParamQualifier {}");
1205     Source nonQualifier =
1206         CompilerTests.javaSource(
1207             "test.NonQualifier",
1208             "package test;",
1209             "",
1210             "@interface NonQualifier {}");
1211 
1212     CompilerTests.daggerCompiler(module, methodQualifier, paramQualifier, nonQualifier)
1213         .compile(
1214             subject -> {
1215               subject.hasErrorCount(0);
1216               subject.generatedSource(
1217                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1218             });
1219   }
1220 
1221   private static final String BINDS_METHOD = "@Binds abstract Foo bindFoo(FooImpl impl);";
1222   private static final String MULTIBINDS_METHOD = "@Multibinds abstract Set<Foo> foos();";
1223   private static final String STATIC_PROVIDES_METHOD =
1224       "@Provides static Bar provideBar() { return new Bar(); }";
1225   private static final String INSTANCE_PROVIDES_METHOD =
1226       "@Provides Baz provideBaz() { return new Baz(); }";
1227   private static final String SOME_ABSTRACT_METHOD = "abstract void blah();";
1228 
1229   @Test
bindsWithInstanceProvides()1230   public void bindsWithInstanceProvides() {
1231     compileMethodCombination(BINDS_METHOD, INSTANCE_PROVIDES_METHOD)
1232         .compile(
1233             subject -> {
1234               subject.hasErrorCount(1);
1235               subject.hasErrorContaining(
1236                   "A @Module may not contain both non-static and abstract binding methods");
1237             });
1238   }
1239 
1240   @Test
multibindsWithInstanceProvides()1241   public void multibindsWithInstanceProvides() {
1242     compileMethodCombination(MULTIBINDS_METHOD, INSTANCE_PROVIDES_METHOD)
1243         .compile(
1244             subject -> {
1245               subject.hasErrorCount(1);
1246               subject.hasErrorContaining(
1247                   "A @Module may not contain both non-static and abstract binding methods");
1248             });
1249   }
1250 
1251   @Test
bindsWithStaticProvides()1252   public void bindsWithStaticProvides() {
1253     compileMethodCombination(BINDS_METHOD, STATIC_PROVIDES_METHOD)
1254         .compile(subject -> subject.hasErrorCount(0));
1255   }
1256 
1257   @Test
bindsWithMultibinds()1258   public void bindsWithMultibinds() {
1259     compileMethodCombination(BINDS_METHOD, MULTIBINDS_METHOD)
1260         .compile(subject -> subject.hasErrorCount(0));
1261   }
1262 
1263   @Test
multibindsWithStaticProvides()1264   public void multibindsWithStaticProvides() {
1265     compileMethodCombination(MULTIBINDS_METHOD, STATIC_PROVIDES_METHOD)
1266         .compile(subject -> subject.hasErrorCount(0));
1267   }
1268 
1269   @Test
instanceProvidesWithAbstractMethod()1270   public void instanceProvidesWithAbstractMethod() {
1271     compileMethodCombination(INSTANCE_PROVIDES_METHOD, SOME_ABSTRACT_METHOD)
1272         .compile(subject -> subject.hasErrorCount(0));
1273   }
1274 
compileMethodCombination(String... methodLines)1275   private CompilerTests.DaggerCompiler compileMethodCombination(String... methodLines) {
1276     Source fooFile =
1277         CompilerTests.javaSource(
1278             "test.Foo",
1279             "package test;",
1280             "",
1281             "interface Foo {}");
1282     Source fooImplFile =
1283         CompilerTests.javaSource(
1284             "test.FooImpl",
1285             "package test;",
1286             "",
1287             "import javax.inject.Inject;",
1288             "",
1289             "final class FooImpl implements Foo {",
1290             "  @Inject FooImpl() {}",
1291             "}");
1292     Source barFile =
1293         CompilerTests.javaSource(
1294             "test.Bar",
1295             "package test;",
1296             "",
1297             "final class Bar {}");
1298     Source bazFile =
1299         CompilerTests.javaSource(
1300             "test.Baz",
1301             "package test;",
1302             "",
1303             "final class Baz {}");
1304 
1305     ImmutableList<String> moduleLines =
1306         new ImmutableList.Builder<String>()
1307             .add(
1308                 "package test;",
1309                 "",
1310                 "import dagger.Binds;",
1311                 "import dagger.Module;",
1312                 "import dagger.Provides;",
1313                 "import dagger.multibindings.Multibinds;",
1314                 "import java.util.Set;",
1315                 "",
1316                 "@Module abstract class TestModule {")
1317             .add(methodLines)
1318             .add("}")
1319             .build();
1320 
1321     Source bindsMethodAndInstanceProvidesMethodModuleFile =
1322         CompilerTests.javaSource("test.TestModule", moduleLines);
1323     return CompilerTests.daggerCompiler(
1324         fooFile, fooImplFile, barFile, bazFile, bindsMethodAndInstanceProvidesMethodModuleFile);
1325   }
1326 }
1327