• 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 androidx.room.compiler.processing.util.Source;
20 import com.google.common.collect.ImmutableMap;
21 import dagger.testing.compile.CompilerTests;
22 import dagger.testing.golden.GoldenFileRule;
23 import org.junit.Rule;
24 import org.junit.Test;
25 import org.junit.runner.RunWith;
26 import org.junit.runners.JUnit4;
27 
28 @RunWith(JUnit4.class)
29 // TODO(gak): add tests for generation in the default package.
30 public final class InjectConstructorFactoryGeneratorTest {
31   private static final Source QUALIFIER_A =
32         CompilerTests.javaSource("test.QualifierA",
33           "package test;",
34           "",
35           "import javax.inject.Qualifier;",
36           "",
37           "@Qualifier @interface QualifierA {}");
38   private static final Source QUALIFIER_B =
39         CompilerTests.javaSource("test.QualifierB",
40           "package test;",
41           "",
42           "import javax.inject.Qualifier;",
43           "",
44           "@Qualifier @interface QualifierB {}");
45   private static final Source SCOPE_A =
46         CompilerTests.javaSource("test.ScopeA",
47           "package test;",
48           "",
49           "import javax.inject.Scope;",
50           "",
51           "@Scope @interface ScopeA {}");
52   private static final Source SCOPE_B =
53         CompilerTests.javaSource("test.ScopeB",
54           "package test;",
55           "",
56           "import javax.inject.Scope;",
57           "",
58           "@Scope @interface ScopeB {}");
59 
60   @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule();
61 
injectOnPrivateConstructor()62   @Test public void injectOnPrivateConstructor() {
63     Source file =
64         CompilerTests.javaSource(
65             "test.PrivateConstructor",
66             "package test;",
67             "",
68             "import javax.inject.Inject;",
69             "",
70             "class PrivateConstructor {",
71             "  @Inject private PrivateConstructor() {}",
72             "}");
73     CompilerTests.daggerCompiler(file)
74         .compile(
75             subject -> {
76               subject.hasErrorCount(1);
77               subject.hasErrorContaining(
78                       "Dagger does not support injection into private constructors")
79                   .onSource(file)
80                   .onLine(6);
81             });
82   }
83 
injectConstructorOnInnerClass()84   @Test public void injectConstructorOnInnerClass() {
85     Source file =
86         CompilerTests.javaSource(
87             "test.OuterClass",
88             "package test;",
89             "",
90             "import javax.inject.Inject;",
91             "",
92             "class OuterClass {",
93             "  class InnerClass {",
94             "    @Inject InnerClass() {}",
95             "  }",
96             "}");
97     CompilerTests.daggerCompiler(file)
98         .compile(
99             subject -> {
100               subject.hasErrorCount(1);
101               subject.hasErrorContaining(
102                       "@Inject constructors are invalid on inner classes. "
103                           + "Did you mean to make the class static?")
104                   .onSource(file)
105                   .onLine(7);
106             });
107   }
108 
injectConstructorOnAbstractClass()109   @Test public void injectConstructorOnAbstractClass() {
110     Source file =
111         CompilerTests.javaSource(
112             "test.AbstractClass",
113             "package test;",
114             "",
115             "import javax.inject.Inject;",
116             "",
117             "abstract class AbstractClass {",
118             "  @Inject AbstractClass() {}",
119             "}");
120     CompilerTests.daggerCompiler(file)
121         .compile(
122             subject -> {
123               subject.hasErrorCount(1);
124               subject.hasErrorContaining(
125                       "@Inject is nonsense on the constructor of an abstract class")
126                   .onSource(file)
127                   .onLine(6);
128             });
129   }
130 
injectConstructorOnGenericClass()131   @Test public void injectConstructorOnGenericClass() {
132     Source file =
133         CompilerTests.javaSource(
134             "test.GenericClass",
135             "package test;",
136             "",
137             "import javax.inject.Inject;",
138             "",
139             "class GenericClass<T> {",
140             "  @Inject GenericClass(T t) {}",
141             "}");
142     CompilerTests.daggerCompiler(file)
143         .compile(
144             subject -> {
145               subject.hasErrorCount(0);
146               subject.generatedSource(goldenFileRule.goldenSource("test/GenericClass_Factory"));
147             });
148   }
149 
fieldAndMethodGenerics()150   @Test public void fieldAndMethodGenerics() {
151     Source file =
152         CompilerTests.javaSource(
153             "test.GenericClass",
154             "package test;",
155             "",
156             "import javax.inject.Inject;",
157             "",
158             "class GenericClass<A, B> {",
159             "  @Inject A a;",
160             "",
161             "  @Inject GenericClass() {}",
162             "",
163             "  @Inject void register(B b) {}",
164             "}");
165     CompilerTests.daggerCompiler(file)
166         .compile(
167             subject -> {
168               subject.hasErrorCount(0);
169               subject.generatedSource(goldenFileRule.goldenSource("test/GenericClass_Factory"));
170             });
171   }
172 
genericClassWithNoDependencies()173   @Test public void genericClassWithNoDependencies() {
174     Source file =
175         CompilerTests.javaSource(
176             "test.GenericClass",
177             "package test;",
178             "",
179             "import javax.inject.Inject;",
180             "",
181             "class GenericClass<T> {",
182             "  @Inject GenericClass() {}",
183             "}");
184     CompilerTests.daggerCompiler(file)
185         .compile(
186             subject -> {
187               subject.hasErrorCount(0);
188               subject.generatedSource(goldenFileRule.goldenSource("test/GenericClass_Factory"));
189             });
190   }
191 
twoGenericTypes()192   @Test public void twoGenericTypes() {
193     Source file =
194         CompilerTests.javaSource(
195             "test.GenericClass",
196             "package test;",
197             "",
198             "import javax.inject.Inject;",
199             "",
200             "class GenericClass<A, B> {",
201             "  @Inject GenericClass(A a, B b) {}",
202             "}");
203     CompilerTests.daggerCompiler(file)
204         .compile(
205             subject -> {
206               subject.hasErrorCount(0);
207               subject.generatedSource(goldenFileRule.goldenSource("test/GenericClass_Factory"));
208             });
209   }
210 
boundedGenerics()211   @Test public void boundedGenerics() {
212     Source file =
213         CompilerTests.javaSource(
214             "test.GenericClass",
215             "package test;",
216             "",
217             "import javax.inject.Inject;",
218             "import java.util.List;",
219             "",
220             "class GenericClass<A extends Number & Comparable<A>,",
221             "    B extends List<? extends String>,",
222             "    C extends List<? super String>> {",
223             "  @Inject GenericClass(A a, B b, C c) {}",
224             "}");
225     CompilerTests.daggerCompiler(file)
226         .compile(
227             subject -> {
228               subject.hasErrorCount(0);
229               subject.generatedSource(goldenFileRule.goldenSource("test/GenericClass_Factory"));
230             });
231   }
232 
multipleSameTypesWithGenericsAndQualifiersAndLazies()233   @Test public void multipleSameTypesWithGenericsAndQualifiersAndLazies() {
234     Source file =
235         CompilerTests.javaSource(
236             "test.GenericClass",
237             "package test;",
238             "",
239             "import javax.inject.Inject;",
240             "import javax.inject.Provider;",
241             "import dagger.Lazy;",
242             "",
243             "class GenericClass<A, B> {",
244             "  @Inject GenericClass(A a, A a2, Provider<A> pa, @QualifierA A qa, Lazy<A> la, ",
245             "                       String s, String s2, Provider<String> ps, ",
246             "                       @QualifierA String qs, Lazy<String> ls,",
247             "                       B b, B b2, Provider<B> pb, @QualifierA B qb, Lazy<B> lb) {}",
248             "}");
249     CompilerTests.daggerCompiler(file, QUALIFIER_A)
250         .compile(
251             subject -> {
252               subject.hasErrorCount(0);
253               subject.generatedSource(goldenFileRule.goldenSource("test/GenericClass_Factory"));
254             });
255   }
256 
multipleInjectConstructors()257   @Test public void multipleInjectConstructors() {
258     Source file =
259         CompilerTests.javaSource(
260             "test.TooManyInjectConstructors",
261             "package test;",
262             "",
263             "import javax.inject.Inject;",
264             "",
265             "class TooManyInjectConstructors {",
266             "  @Inject TooManyInjectConstructors() {}",
267             "  TooManyInjectConstructors(int i) {}",
268             "  @Inject TooManyInjectConstructors(String s) {}",
269             "}");
270     CompilerTests.daggerCompiler(file)
271         .compile(
272             subject -> {
273               subject.hasErrorCount(1);
274               subject.hasErrorContaining(
275                       "Type test.TooManyInjectConstructors may only contain one injected "
276                           + "constructor. Found: ["
277                           + "@Inject test.TooManyInjectConstructors(), "
278                           + "@Inject test.TooManyInjectConstructors(String)"
279                           + "]")
280                   .onSource(file)
281                   .onLine(5);
282             });
283   }
284 
multipleQualifiersOnInjectConstructorParameter()285   @Test public void multipleQualifiersOnInjectConstructorParameter() {
286     Source file =
287         CompilerTests.javaSource(
288             "test.MultipleQualifierConstructorParam",
289             "package test;",
290             "",
291             "import javax.inject.Inject;",
292             "",
293             "class MultipleQualifierConstructorParam {",
294             "  @Inject MultipleQualifierConstructorParam(",
295             "      @QualifierA",
296             "      @QualifierB",
297             "      String s) {}",
298             "}");
299     CompilerTests.daggerCompiler(file, QUALIFIER_A, QUALIFIER_B)
300         .compile(
301             subject -> {
302               subject.hasErrorCount(2);
303               subject.hasErrorContaining(
304                       "A single dependency request may not use more than one @Qualifier")
305                   .onSource(file)
306                   .onLine(7);
307               subject.hasErrorContaining(
308                       "A single dependency request may not use more than one @Qualifier")
309                   .onSource(file)
310                   .onLine(8);
311             });
312   }
313 
injectConstructorOnClassWithMultipleScopes()314   @Test public void injectConstructorOnClassWithMultipleScopes() {
315     Source file =
316         CompilerTests.javaSource(
317             "test.MultipleScopeClass",
318             "package test;",
319             "",
320             "import javax.inject.Inject;",
321             "",
322             "@ScopeA",
323             "@ScopeB",
324             "class MultipleScopeClass {",
325             "  @Inject MultipleScopeClass() {}",
326             "}");
327     CompilerTests.daggerCompiler(file, SCOPE_A, SCOPE_B)
328         .compile(
329             subject -> {
330               subject.hasErrorCount(2);
331               subject.hasErrorContaining("A single binding may not declare more than one @Scope")
332                   .onSource(file)
333                   .onLine(5);
334               subject.hasErrorContaining("A single binding may not declare more than one @Scope")
335                   .onSource(file)
336                   .onLine(6);
337             });
338   }
339 
injectConstructorWithQualifier()340   @Test public void injectConstructorWithQualifier() {
341     Source file =
342         CompilerTests.javaSource(
343             "test.MultipleScopeClass",
344             "package test;",
345             "",
346             "import javax.inject.Inject;",
347             "",
348             "class MultipleScopeClass {",
349             "  @Inject",
350             "  @QualifierA",
351             "  @QualifierB",
352             "  MultipleScopeClass() {}",
353             "}");
354     CompilerTests.daggerCompiler(file, QUALIFIER_A, QUALIFIER_B)
355         .compile(
356             subject -> {
357               subject.hasErrorCount(2);
358               subject.hasErrorContaining(
359                       "@Qualifier annotations are not allowed on @Inject constructors")
360                   .onSource(file)
361                   .onLine(7);
362               subject.hasErrorContaining(
363                       "@Qualifier annotations are not allowed on @Inject constructors")
364                   .onSource(file)
365                   .onLine(8);
366             });
367   }
368 
injectConstructorWithCheckedExceptionsError()369   @Test public void injectConstructorWithCheckedExceptionsError() {
370     Source file =
371         CompilerTests.javaSource(
372             "test.CheckedExceptionClass",
373             "package test;",
374             "",
375             "import javax.inject.Inject;",
376             "",
377             "class CheckedExceptionClass {",
378             "  @Inject CheckedExceptionClass() throws Exception {}",
379             "}");
380     CompilerTests.daggerCompiler(file)
381         .compile(
382             subject -> {
383               subject.hasErrorCount(1);
384               subject.hasErrorContaining(
385                       "Dagger does not support checked exceptions on @Inject constructors")
386                   .onSource(file)
387                   .onLine(6);
388             });
389   }
390 
injectConstructorWithCheckedExceptionsWarning()391   @Test public void injectConstructorWithCheckedExceptionsWarning() {
392     Source file =
393         CompilerTests.javaSource(
394             "test.CheckedExceptionClass",
395             "package test;",
396             "",
397             "import javax.inject.Inject;",
398             "",
399             "class CheckedExceptionClass {",
400             "  @Inject CheckedExceptionClass() throws Exception {}",
401             "}");
402     CompilerTests.daggerCompiler(file)
403         .withProcessingOptions(ImmutableMap.of("dagger.privateMemberValidation", "WARNING"))
404         .compile(
405             subject -> {
406               subject.hasErrorCount(0);
407               subject.hasWarningCount(1);
408               subject.hasWarningContaining(
409                       "Dagger does not support checked exceptions on @Inject constructors")
410                   .onSource(file)
411                   .onLine(6);
412             });
413   }
414 
privateInjectClassError()415   @Test public void privateInjectClassError() {
416     Source file =
417         CompilerTests.javaSource(
418             "test.OuterClass",
419             "package test;",
420             "",
421             "import javax.inject.Inject;",
422             "",
423             "final class OuterClass {",
424             "  private static final class InnerClass {",
425             "    @Inject InnerClass() {}",
426             "  }",
427             "}");
428     CompilerTests.daggerCompiler(file)
429         .compile(
430             subject -> {
431               subject.hasErrorCount(1);
432               subject.hasErrorContaining("Dagger does not support injection into private classes")
433                   .onSource(file)
434                   .onLine(7);
435             });
436   }
437 
privateInjectClassWarning()438   @Test public void privateInjectClassWarning() {
439     Source file =
440         CompilerTests.javaSource(
441             "test.OuterClass",
442             "package test;",
443             "",
444             "import javax.inject.Inject;",
445             "",
446             "final class OuterClass {",
447             "  private static final class InnerClass {",
448             "    @Inject InnerClass() {}",
449             "  }",
450             "}");
451     CompilerTests.daggerCompiler(file)
452         .withProcessingOptions(ImmutableMap.of("dagger.privateMemberValidation", "WARNING"))
453         .compile(
454             subject -> {
455               subject.hasErrorCount(0);
456               subject.hasWarningCount(1);
457               subject.hasWarningContaining("Dagger does not support injection into private classes")
458                   .onSource(file)
459                   .onLine(7);
460             });
461   }
462 
nestedInPrivateInjectClassError()463   @Test public void nestedInPrivateInjectClassError() {
464     Source file =
465         CompilerTests.javaSource(
466             "test.OuterClass",
467             "package test;",
468             "",
469             "import javax.inject.Inject;",
470             "",
471             "final class OuterClass {",
472             "  private static final class MiddleClass {",
473             "    static final class InnerClass {",
474             "      @Inject InnerClass() {}",
475             "    }",
476             "  }",
477             "}");
478     CompilerTests.daggerCompiler(file)
479         .compile(
480             subject -> {
481               subject.hasErrorCount(1);
482               subject.hasErrorContaining("Dagger does not support injection into private classes")
483                   .onSource(file)
484                   .onLine(8);
485             });
486   }
487 
nestedInPrivateInjectClassWarning()488   @Test public void nestedInPrivateInjectClassWarning() {
489     Source file =
490         CompilerTests.javaSource(
491             "test.OuterClass",
492             "package test;",
493             "",
494             "import javax.inject.Inject;",
495             "",
496             "final class OuterClass {",
497             "  private static final class MiddleClass {",
498             "    static final class InnerClass {",
499             "      @Inject InnerClass() {}",
500             "    }",
501             "  }",
502             "}");
503     CompilerTests.daggerCompiler(file)
504         .withProcessingOptions(ImmutableMap.of("dagger.privateMemberValidation", "WARNING"))
505         .compile(
506             subject -> {
507               subject.hasErrorCount(0);
508               subject.hasWarningCount(1);
509               subject.hasWarningContaining("Dagger does not support injection into private classes")
510                   .onSource(file)
511                   .onLine(8);
512             });
513   }
514 
finalInjectField()515   @Test public void finalInjectField() {
516     Source file =
517         CompilerTests.javaSource(
518             "test.FinalInjectField",
519             "package test;",
520             "",
521             "import javax.inject.Inject;",
522             "",
523             "class FinalInjectField {",
524             "  @Inject final String s;",
525             "}");
526     CompilerTests.daggerCompiler(file)
527         .compile(
528             subject -> {
529               subject.hasErrorCount(1);
530               subject.hasErrorContaining("@Inject fields may not be final")
531                   .onSource(file)
532                   .onLine(6);
533             });
534   }
535 
privateInjectFieldError()536   @Test public void privateInjectFieldError() {
537     Source file =
538         CompilerTests.javaSource(
539             "test.PrivateInjectField",
540             "package test;",
541             "",
542             "import javax.inject.Inject;",
543             "",
544             "class PrivateInjectField {",
545             "  @Inject private String s;",
546             "}");
547     CompilerTests.daggerCompiler(file)
548         .compile(
549             subject -> {
550               subject.hasErrorCount(1);
551               subject.hasErrorContaining("Dagger does not support injection into private fields")
552                   .onSource(file)
553                   .onLine(6);
554             });
555   }
556 
privateInjectFieldWarning()557   @Test public void privateInjectFieldWarning() {
558     Source file =
559         CompilerTests.javaSource(
560             "test.PrivateInjectField",
561             "package test;",
562             "",
563             "import javax.inject.Inject;",
564             "",
565             "class PrivateInjectField {",
566             "  @Inject private String s;",
567             "}");
568     CompilerTests.daggerCompiler(file)
569         .withProcessingOptions(ImmutableMap.of("dagger.privateMemberValidation", "WARNING"))
570         .compile(
571             subject -> {
572               subject.hasErrorCount(0);
573               // TODO: Verify warning message when supported
574               // subject.hasWarningCount(1);
575             });
576   }
577 
staticInjectFieldError()578   @Test public void staticInjectFieldError() {
579     Source file =
580         CompilerTests.javaSource(
581             "test.StaticInjectField",
582             "package test;",
583             "",
584             "import javax.inject.Inject;",
585             "",
586             "class StaticInjectField {",
587             "  @Inject static String s;",
588             "}");
589     CompilerTests.daggerCompiler(file)
590         .compile(
591             subject -> {
592               subject.hasErrorCount(1);
593               subject.hasErrorContaining("Dagger does not support injection into static fields")
594                   .onSource(file)
595                   .onLine(6);
596             });
597   }
598 
staticInjectFieldWarning()599   @Test public void staticInjectFieldWarning() {
600     Source file =
601         CompilerTests.javaSource(
602             "test.StaticInjectField",
603             "package test;",
604             "",
605             "import javax.inject.Inject;",
606             "",
607             "class StaticInjectField {",
608             "  @Inject static String s;",
609             "}");
610     CompilerTests.daggerCompiler(file)
611         .withProcessingOptions(ImmutableMap.of("dagger.staticMemberValidation", "WARNING"))
612         .compile(
613             subject -> {
614               subject.hasErrorCount(0);
615               // TODO: Verify warning message when supported
616               // subject.hasWarningCount(1);
617             });
618   }
619 
multipleQualifiersOnField()620   @Test public void multipleQualifiersOnField() {
621     Source file =
622         CompilerTests.javaSource(
623             "test.MultipleQualifierInjectField",
624             "package test;",
625             "",
626             "import javax.inject.Inject;",
627             "",
628             "class MultipleQualifierInjectField {",
629             "  @Inject",
630             "  @QualifierA",
631             "  @QualifierB",
632             "  String s;",
633             "}");
634     CompilerTests.daggerCompiler(file, QUALIFIER_A, QUALIFIER_B)
635         .compile(
636             subject -> {
637               subject.hasErrorCount(2);
638               subject.hasErrorContaining(
639                       "A single dependency request may not use more than one @Qualifier")
640                   .onSource(file)
641                   .onLine(7);
642               subject.hasErrorContaining(
643                       "A single dependency request may not use more than one @Qualifier")
644                   .onSource(file)
645                   .onLine(8);
646             });
647   }
648 
abstractInjectMethod()649   @Test public void abstractInjectMethod() {
650     Source file =
651         CompilerTests.javaSource(
652             "test.AbstractInjectMethod",
653             "package test;",
654             "",
655             "import javax.inject.Inject;",
656             "",
657             "abstract class AbstractInjectMethod {",
658             "  @Inject abstract void method();",
659             "}");
660     CompilerTests.daggerCompiler(file)
661         .compile(
662             subject -> {
663               subject.hasErrorCount(1);
664               subject.hasErrorContaining("Methods with @Inject may not be abstract")
665                   .onSource(file)
666                   .onLine(6);
667             });
668   }
669 
privateInjectMethodError()670   @Test public void privateInjectMethodError() {
671     Source file =
672         CompilerTests.javaSource(
673             "test.PrivateInjectMethod",
674             "package test;",
675             "",
676             "import javax.inject.Inject;",
677             "",
678             "class PrivateInjectMethod {",
679             "  @Inject private void method(){}",
680             "}");
681     CompilerTests.daggerCompiler(file)
682         .compile(
683             subject -> {
684               subject.hasErrorCount(1);
685               subject.hasErrorContaining("Dagger does not support injection into private methods")
686                   .onSource(file)
687                   .onLine(6);
688             });
689   }
690 
privateInjectMethodWarning()691   @Test public void privateInjectMethodWarning() {
692     Source file =
693         CompilerTests.javaSource(
694             "test.PrivateInjectMethod",
695             "package test;",
696             "",
697             "import javax.inject.Inject;",
698             "",
699             "class PrivateInjectMethod {",
700             "  @Inject private void method(){}",
701             "}");
702     CompilerTests.daggerCompiler(file)
703         .withProcessingOptions(ImmutableMap.of("dagger.privateMemberValidation", "WARNING"))
704         .compile(
705             subject -> {
706               subject.hasErrorCount(0);
707               // TODO: Verify warning message when supported
708               // subject.hasWarningCount(1);
709             });
710   }
711 
staticInjectMethodError()712   @Test public void staticInjectMethodError() {
713     Source file =
714         CompilerTests.javaSource(
715             "test.StaticInjectMethod",
716             "package test;",
717             "",
718             "import javax.inject.Inject;",
719             "",
720             "class StaticInjectMethod {",
721             "  @Inject static void method(){}",
722             "}");
723     CompilerTests.daggerCompiler(file)
724         .compile(
725             subject -> {
726               subject.hasErrorCount(1);
727               subject.hasErrorContaining("Dagger does not support injection into static methods")
728                   .onSource(file)
729                   .onLine(6);
730             });
731   }
732 
staticInjectMethodWarning()733   @Test public void staticInjectMethodWarning() {
734     Source file =
735         CompilerTests.javaSource(
736             "test.StaticInjectMethod",
737             "package test;",
738             "",
739             "import javax.inject.Inject;",
740             "",
741             "class StaticInjectMethod {",
742             "  @Inject static void method(){}",
743             "}");
744     CompilerTests.daggerCompiler(file)
745         .withProcessingOptions(ImmutableMap.of("dagger.staticMemberValidation", "WARNING"))
746         .compile(
747             subject -> {
748               subject.hasErrorCount(0);
749               // TODO: Verify warning message when supported
750               // subject.hasWarningCount(1);
751             });
752   }
753 
genericInjectMethod()754   @Test public void genericInjectMethod() {
755     Source file =
756         CompilerTests.javaSource(
757             "test.GenericInjectMethod",
758             "package test;",
759             "",
760             "import javax.inject.Inject;",
761             "",
762             "class AbstractInjectMethod {",
763             "  @Inject <T> void method();",
764             "}");
765     CompilerTests.daggerCompiler(file)
766         .compile(
767             subject -> {
768               subject.hasErrorCount(1);
769               subject.hasErrorContaining("Methods with @Inject may not declare type parameters")
770                   .onSource(file)
771                   .onLine(6);
772             });
773   }
774 
multipleQualifiersOnInjectMethodParameter()775   @Test public void multipleQualifiersOnInjectMethodParameter() {
776     Source file =
777         CompilerTests.javaSource(
778             "test.MultipleQualifierMethodParam",
779             "package test;",
780             "",
781             "import javax.inject.Inject;",
782             "",
783             "class MultipleQualifierMethodParam {",
784             "  @Inject void method(",
785             "      @QualifierA",
786             "      @QualifierB",
787             "      String s) {}",
788             "}");
789     CompilerTests.daggerCompiler(file, QUALIFIER_A, QUALIFIER_B)
790         .compile(
791             subject -> {
792               subject.hasErrorCount(2);
793               subject.hasErrorContaining(
794                       "A single dependency request may not use more than one @Qualifier")
795                   .onSource(file)
796                   .onLine(7);
797               subject.hasErrorContaining(
798                       "A single dependency request may not use more than one @Qualifier")
799                   .onSource(file)
800                   .onLine(8);
801             });
802   }
803 
injectConstructorDependsOnProduced()804   @Test public void injectConstructorDependsOnProduced() {
805     Source file =
806         CompilerTests.javaSource(
807             "test.A",
808             "package test;",
809             "",
810             "import dagger.producers.Produced;",
811             "import javax.inject.Inject;",
812             "",
813             "final class A {",
814             "  @Inject A(Produced<String> str) {}",
815             "}");
816     CompilerTests.daggerCompiler(file)
817         .compile(
818             subject -> {
819               subject.hasErrorCount(1);
820               subject.hasErrorContaining("Produced may only be injected in @Produces methods");
821             });
822   }
823 
injectConstructorDependsOnProducer()824   @Test public void injectConstructorDependsOnProducer() {
825     Source file =
826         CompilerTests.javaSource(
827             "test.A",
828             "package test;",
829             "",
830             "import dagger.producers.Producer;",
831             "import javax.inject.Inject;",
832             "",
833             "final class A {",
834             "  @Inject A(Producer<String> str) {}",
835             "}");
836     CompilerTests.daggerCompiler(file)
837         .compile(
838             subject -> {
839               subject.hasErrorCount(1);
840               subject.hasErrorContaining("Producer may only be injected in @Produces methods");
841             });
842   }
843 
injectFieldDependsOnProduced()844   @Test public void injectFieldDependsOnProduced() {
845     Source file =
846         CompilerTests.javaSource(
847             "test.A",
848             "package test;",
849             "",
850             "import dagger.producers.Produced;",
851             "import javax.inject.Inject;",
852             "",
853             "final class A {",
854             "  @Inject Produced<String> str;",
855             "}");
856     CompilerTests.daggerCompiler(file)
857         .compile(
858             subject -> {
859               subject.hasErrorCount(1);
860               subject.hasErrorContaining("Produced may only be injected in @Produces methods");
861             });
862   }
863 
injectFieldDependsOnProducer()864   @Test public void injectFieldDependsOnProducer() {
865     Source file =
866         CompilerTests.javaSource(
867             "test.A",
868             "package test;",
869             "",
870             "import dagger.producers.Producer;",
871             "import javax.inject.Inject;",
872             "",
873             "final class A {",
874             "  @Inject Producer<String> str;",
875             "}");
876     CompilerTests.daggerCompiler(file)
877         .compile(
878             subject -> {
879               subject.hasErrorCount(1);
880               subject.hasErrorContaining("Producer may only be injected in @Produces methods");
881             });
882   }
883 
injectMethodDependsOnProduced()884   @Test public void injectMethodDependsOnProduced() {
885     Source file =
886         CompilerTests.javaSource(
887             "test.A",
888             "package test;",
889             "",
890             "import dagger.producers.Produced;",
891             "import javax.inject.Inject;",
892             "",
893             "final class A {",
894             "  @Inject void inject(Produced<String> str) {}",
895             "}");
896     CompilerTests.daggerCompiler(file)
897         .compile(
898             subject -> {
899               subject.hasErrorCount(1);
900               subject.hasErrorContaining("Produced may only be injected in @Produces methods");
901             });
902   }
903 
injectMethodDependsOnProducer()904   @Test public void injectMethodDependsOnProducer() {
905     Source file =
906         CompilerTests.javaSource(
907             "test.A",
908             "package test;",
909             "",
910             "import dagger.producers.Producer;",
911             "import javax.inject.Inject;",
912             "",
913             "final class A {",
914             "  @Inject void inject(Producer<String> str) {}",
915             "}");
916     CompilerTests.daggerCompiler(file)
917         .compile(
918             subject -> {
919               subject.hasErrorCount(1);
920               subject.hasErrorContaining("Producer may only be injected in @Produces methods");
921             });
922   }
923 
924 
injectConstructor()925   @Test public void injectConstructor() {
926     Source file =
927         CompilerTests.javaSource("test.InjectConstructor",
928         "package test;",
929         "",
930         "import javax.inject.Inject;",
931         "",
932         "class InjectConstructor {",
933         "  @Inject InjectConstructor(String s) {}",
934         "}");
935     CompilerTests.daggerCompiler(file)
936         .compile(
937             subject -> {
938               subject.hasErrorCount(0);
939               subject.generatedSource(goldenFileRule.goldenSource("test/InjectConstructor_Factory"));
940             });
941   }
942 
injectConstructorAndMembersInjection()943   @Test public void injectConstructorAndMembersInjection() {
944     Source file =
945         CompilerTests.javaSource("test.AllInjections",
946         "package test;",
947         "",
948         "import javax.inject.Inject;",
949         "",
950         "class AllInjections {",
951         "  @Inject String s;",
952         "  @Inject AllInjections(String s) {}",
953         "  @Inject void s(String s) {}",
954         "}");
955     CompilerTests.daggerCompiler(file)
956         .compile(
957             subject -> {
958               subject.hasErrorCount(0);
959               subject.generatedSource(goldenFileRule.goldenSource("test/AllInjections_Factory"));
960             });
961   }
962 
963   @Test
wildcardDependency()964   public void wildcardDependency() {
965     Source file =
966         CompilerTests.javaSource("test.InjectConstructor",
967         "package test;",
968         "",
969         "import java.util.List;",
970         "import javax.inject.Inject;",
971         "",
972         "class InjectConstructor {",
973         "  @Inject InjectConstructor(List<?> objects) {}",
974         "}");
975     CompilerTests.daggerCompiler(file)
976         .compile(
977             subject -> {
978               subject.hasErrorCount(0);
979               subject.generatedSource(goldenFileRule.goldenSource("test/InjectConstructor_Factory"));
980             });
981   }
982 
983   @Test
basicNameCollision()984   public void basicNameCollision() {
985     Source factoryFile =
986         CompilerTests.javaSource("other.pkg.Factory",
987         "package other.pkg;",
988         "",
989         "public class Factory {}");
990     Source file =
991         CompilerTests.javaSource("test.InjectConstructor",
992         "package test;",
993         "",
994         "import javax.inject.Inject;",
995         "import other.pkg.Factory;",
996         "",
997         "class InjectConstructor {",
998         "  @Inject InjectConstructor(Factory factory) {}",
999         "}");
1000     CompilerTests.daggerCompiler(factoryFile, file)
1001         .compile(
1002             subject -> {
1003               subject.hasErrorCount(0);
1004               subject.generatedSource(goldenFileRule.goldenSource("test/InjectConstructor_Factory"));
1005             });
1006   }
1007 
1008   @Test
nestedNameCollision()1009   public void nestedNameCollision() {
1010     Source factoryFile =
1011         CompilerTests.javaSource("other.pkg.Outer",
1012         "package other.pkg;",
1013         "",
1014         "public class Outer {",
1015         "  public class Factory {}",
1016         "}");
1017     Source file =
1018         CompilerTests.javaSource("test.InjectConstructor",
1019         "package test;",
1020         "",
1021         "import javax.inject.Inject;",
1022         "import other.pkg.Outer;",
1023         "",
1024         "class InjectConstructor {",
1025         "  @Inject InjectConstructor(Outer.Factory factory) {}",
1026         "}");
1027     CompilerTests.daggerCompiler(factoryFile, file)
1028         .compile(
1029             subject -> {
1030               subject.hasErrorCount(0);
1031               subject.generatedSource(goldenFileRule.goldenSource("test/InjectConstructor_Factory"));
1032             });
1033   }
1034 
1035   @Test
samePackageNameCollision()1036   public void samePackageNameCollision() {
1037     Source samePackageInterface =
1038         CompilerTests.javaSource(
1039             "test.CommonName",
1040             "package test;",
1041             "",
1042             "public interface CommonName {}");
1043     Source differentPackageInterface =
1044         CompilerTests.javaSource(
1045             "other.pkg.CommonName",
1046             "package other.pkg;",
1047             "",
1048             "public interface CommonName {}");
1049     Source file =
1050         CompilerTests.javaSource(
1051             "test.InjectConstructor",
1052             "package test;",
1053             "",
1054             "import javax.inject.Inject;",
1055             "",
1056             "class InjectConstructor implements CommonName {",
1057             "  @Inject InjectConstructor("
1058                 + "other.pkg.CommonName otherPackage, CommonName samePackage) {}",
1059             "}");
1060     CompilerTests.daggerCompiler(samePackageInterface, differentPackageInterface, file)
1061         .compile(
1062             subject -> {
1063               subject.hasErrorCount(0);
1064               subject.generatedSource(goldenFileRule.goldenSource("test/InjectConstructor_Factory"));
1065             });
1066   }
1067 
1068   @Test
noDeps()1069   public void noDeps() {
1070     Source file =
1071         CompilerTests.javaSource(
1072             "test.SimpleType",
1073             "package test;",
1074             "",
1075             "import javax.inject.Inject;",
1076             "",
1077             "final class SimpleType {",
1078             "  @Inject SimpleType() {}",
1079             "}");
1080     CompilerTests.daggerCompiler(file)
1081         .compile(
1082             subject -> {
1083               subject.hasErrorCount(0);
1084               subject.generatedSource(goldenFileRule.goldenSource("test/SimpleType_Factory"));
1085             });
1086   }
1087 
simpleComponentWithNesting()1088   @Test public void simpleComponentWithNesting() {
1089     Source file =
1090         CompilerTests.javaSource(
1091             "test.OuterType",
1092             "package test;",
1093             "",
1094             "import dagger.Component;",
1095             "import javax.inject.Inject;",
1096             "",
1097             "final class OuterType {",
1098             "  static class A {",
1099             "    @Inject A() {}",
1100             "  }",
1101             "  static class B {",
1102             "    @Inject A a;",
1103             "  }",
1104             "}");
1105     CompilerTests.daggerCompiler(file)
1106         .compile(
1107             subject -> {
1108               subject.hasErrorCount(0);
1109               subject.generatedSource(goldenFileRule.goldenSource("test/OuterType_A_Factory"));
1110             });
1111   }
1112 
1113   @Test
testScopedMetadata()1114   public void testScopedMetadata() throws Exception {
1115     Source file =
1116         CompilerTests.javaSource(
1117             "test.ScopedBinding",
1118             "package test;",
1119             "",
1120             "import javax.inject.Inject;",
1121             "import javax.inject.Singleton;",
1122             "",
1123             "@Singleton",
1124             "class ScopedBinding {",
1125             "  @Inject",
1126             "  ScopedBinding() {}",
1127             "}");
1128     CompilerTests.daggerCompiler(file)
1129         .compile(
1130             subject -> {
1131               subject.hasErrorCount(0);
1132               subject.generatedSource(goldenFileRule.goldenSource("test/ScopedBinding_Factory"));
1133             });
1134   }
1135 
1136   @Test
testScopedMetadataWithCustomScope()1137   public void testScopedMetadataWithCustomScope() throws Exception {
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 
1150     Source customAnnotation =
1151         CompilerTests.javaSource(
1152             "test.CustomAnnotation",
1153             "package test;",
1154             "",
1155             "@interface CustomAnnotation {",
1156             "  String value();",
1157             "}");
1158 
1159     Source scopedBinding =
1160         CompilerTests.javaSource(
1161             "test.ScopedBinding",
1162             "package test;",
1163             "",
1164             "import javax.inject.Inject;",
1165             "import javax.inject.Singleton;",
1166             "",
1167             "@CustomAnnotation(\"someValue\")",
1168             "@CustomScope(\"someOtherValue\")",
1169             "class ScopedBinding {",
1170             "  @Inject",
1171             "  ScopedBinding() {}",
1172             "}");
1173     CompilerTests.daggerCompiler(scopedBinding, customScope, customAnnotation)
1174         .compile(
1175             subject -> {
1176               subject.hasErrorCount(0);
1177               subject.generatedSource(goldenFileRule.goldenSource("test/ScopedBinding_Factory"));
1178             });
1179   }
1180 
1181   @Test
testQualifierMetadata()1182   public void testQualifierMetadata() throws Exception {
1183     Source someBinding =
1184         CompilerTests.javaSource(
1185             "test.SomeBinding",
1186             "package test;",
1187             "",
1188             "import javax.inject.Inject;",
1189             "import javax.inject.Singleton;",
1190             "",
1191             "@NonQualifier",
1192             "@MisplacedQualifier",
1193             "class SomeBinding {",
1194             "  @NonQualifier @FieldQualifier @Inject String injectField;",
1195             "  @NonQualifier @MisplacedQualifier String nonDaggerField;",
1196             "",
1197             "  @NonQualifier",
1198             "  @Inject",
1199             "  SomeBinding(@NonQualifier @ConstructorParameterQualifier Double d) {}",
1200             "",
1201             "  @NonQualifier",
1202             "  @MisplacedQualifier",
1203             "  SomeBinding(@NonQualifier @MisplacedQualifier Double d, int i) {}",
1204             "",
1205             "  @NonQualifier",
1206             "  @MisplacedQualifier",
1207             "  @Inject",
1208             "  void injectMethod(@NonQualifier @MethodParameterQualifier Float f) {}",
1209             "",
1210             "  @NonQualifier",
1211             "  @MisplacedQualifier",
1212             "  void nonDaggerMethod(@NonQualifier @MisplacedQualifier Float f) {}",
1213             "}");
1214     Source fieldQualifier =
1215         CompilerTests.javaSource(
1216             "test.FieldQualifier",
1217             "package test;",
1218             "",
1219             "import javax.inject.Qualifier;",
1220             "",
1221             "@Qualifier",
1222             "@interface FieldQualifier {}");
1223     Source constructorParameterQualifier =
1224         CompilerTests.javaSource(
1225             "test.ConstructorParameterQualifier",
1226             "package test;",
1227             "",
1228             "import javax.inject.Qualifier;",
1229             "",
1230             "@Qualifier",
1231             "@interface ConstructorParameterQualifier {}");
1232     Source methodParameterQualifier =
1233         CompilerTests.javaSource(
1234             "test.MethodParameterQualifier",
1235             "package test;",
1236             "",
1237             "import javax.inject.Qualifier;",
1238             "",
1239             "@Qualifier",
1240             "@interface MethodParameterQualifier {}");
1241     Source misplacedQualifier =
1242         CompilerTests.javaSource(
1243             "test.MisplacedQualifier",
1244             "package test;",
1245             "",
1246             "import javax.inject.Qualifier;",
1247             "",
1248             "@Qualifier",
1249             "@interface MisplacedQualifier {}");
1250     Source nonQualifier =
1251         CompilerTests.javaSource(
1252             "test.NonQualifier",
1253             "package test;",
1254             "",
1255             "@interface NonQualifier {}");
1256     CompilerTests.daggerCompiler(
1257             someBinding,
1258             fieldQualifier,
1259             constructorParameterQualifier,
1260             methodParameterQualifier,
1261             misplacedQualifier,
1262             nonQualifier)
1263         .compile(
1264             subject -> {
1265               subject.hasErrorCount(0);
1266               subject.generatedSource(goldenFileRule.goldenSource("test/SomeBinding_Factory"));
1267               subject.generatedSource(
1268                   goldenFileRule.goldenSource("test/SomeBinding_MembersInjector"));
1269             });
1270   }
1271 
1272   @Test
testComplexQualifierMetadata()1273   public void testComplexQualifierMetadata() throws Exception {
1274     Source someBinding =
1275         CompilerTests.javaSource(
1276             "test.SomeBinding",
1277             "package test;",
1278             "",
1279             "import javax.inject.Inject;",
1280             "import javax.inject.Inject;",
1281             "",
1282             "class SomeBinding {",
1283             "  @QualifierWithValue(1) @Inject String injectField;",
1284             "",
1285             "  @Inject",
1286             "  SomeBinding(",
1287             "      @pkg1.SameNameQualifier String str1,",
1288             "      @pkg2.SameNameQualifier String str2) {}",
1289             "",
1290             "  @Inject",
1291             "  void injectMethod(@test.Outer.NestedQualifier Float f) {}",
1292             "}");
1293     Source qualifierWithValue =
1294         CompilerTests.javaSource(
1295             "test.QualifierWithValue",
1296             "package test;",
1297             "",
1298             "import javax.inject.Qualifier;",
1299             "",
1300             "@Qualifier",
1301             "@interface QualifierWithValue {",
1302             "  int value();",
1303             "}");
1304     Source pkg1SameNameQualifier =
1305         CompilerTests.javaSource(
1306             "pkg1.SameNameQualifier",
1307             "package pkg1;",
1308             "",
1309             "import javax.inject.Qualifier;",
1310             "",
1311             "@Qualifier",
1312             "public @interface SameNameQualifier {}");
1313     Source pkg2SameNameQualifier =
1314         CompilerTests.javaSource(
1315             "pkg2.SameNameQualifier",
1316             "package pkg2;",
1317             "",
1318             "import javax.inject.Qualifier;",
1319             "",
1320             "@Qualifier",
1321             "public @interface SameNameQualifier {}");
1322     Source nestedQualifier =
1323         CompilerTests.javaSource(
1324             "test.Outer",
1325             "package test;",
1326             "",
1327             "import javax.inject.Qualifier;",
1328             "",
1329             "interface Outer {",
1330             "  @Qualifier",
1331             "  @interface NestedQualifier {}",
1332             "}");
1333     CompilerTests.daggerCompiler(
1334             someBinding,
1335             qualifierWithValue,
1336             pkg1SameNameQualifier,
1337             pkg2SameNameQualifier,
1338             nestedQualifier)
1339         .compile(
1340             subject -> {
1341               subject.hasErrorCount(0);
1342               subject.generatedSource(goldenFileRule.goldenSource("test/SomeBinding_Factory"));
1343               subject.generatedSource(
1344                   goldenFileRule.goldenSource("test/SomeBinding_MembersInjector"));
1345             });
1346   }
1347 
1348   @Test
testBaseClassQualifierMetadata()1349   public void testBaseClassQualifierMetadata() throws Exception {
1350     Source foo =
1351         CompilerTests.javaSource(
1352             "test.Foo",
1353             "package test;",
1354             "",
1355             "import javax.inject.Inject;",
1356             "import javax.inject.Singleton;",
1357             "",
1358             "class Foo extends FooBase {",
1359             "  @FooFieldQualifier @Inject String injectField;",
1360             "",
1361             "  @Inject",
1362             "  Foo(@FooConstructorQualifier int i) { super(i); }",
1363             "",
1364             "  @Inject",
1365             "  void injectMethod(@FooMethodQualifier float f) {}",
1366             "}");
1367     Source fooFieldQualifier =
1368         CompilerTests.javaSource(
1369             "test.FooFieldQualifier",
1370             "package test;",
1371             "",
1372             "import javax.inject.Qualifier;",
1373             "",
1374             "@Qualifier",
1375             "@interface FooFieldQualifier {}");
1376     Source fooConstructorQualifier =
1377         CompilerTests.javaSource(
1378             "test.FooConstructorQualifier",
1379             "package test;",
1380             "",
1381             "import javax.inject.Qualifier;",
1382             "",
1383             "@Qualifier",
1384             "@interface FooConstructorQualifier {}");
1385     Source fooMethodQualifier =
1386         CompilerTests.javaSource(
1387             "test.FooMethodQualifier",
1388             "package test;",
1389             "",
1390             "import javax.inject.Qualifier;",
1391             "",
1392             "@Qualifier",
1393             "@interface FooMethodQualifier {}");
1394     Source fooBase =
1395         CompilerTests.javaSource(
1396             "test.FooBase",
1397             "package test;",
1398             "",
1399             "import javax.inject.Inject;",
1400             "import javax.inject.Singleton;",
1401             "",
1402             "class FooBase {",
1403             "  @FooBaseFieldQualifier @Inject String injectField;",
1404             "",
1405             "  @Inject",
1406             "  FooBase(@FooBaseConstructorQualifier int i) {}",
1407             "",
1408             "  @Inject",
1409             "  void injectMethod(@FooBaseMethodQualifier float f) {}",
1410             "}");
1411     Source fooBaseFieldQualifier =
1412         CompilerTests.javaSource(
1413             "test.FooBaseFieldQualifier",
1414             "package test;",
1415             "",
1416             "import javax.inject.Qualifier;",
1417             "",
1418             "@Qualifier",
1419             "@interface FooBaseFieldQualifier {}");
1420     Source fooBaseConstructorQualifier =
1421         CompilerTests.javaSource(
1422             "test.FooBaseConstructorQualifier",
1423             "package test;",
1424             "",
1425             "import javax.inject.Qualifier;",
1426             "",
1427             "@Qualifier",
1428             "@interface FooBaseConstructorQualifier {}");
1429     Source fooBaseMethodQualifier =
1430         CompilerTests.javaSource(
1431             "test.FooBaseMethodQualifier",
1432             "package test;",
1433             "",
1434             "import javax.inject.Qualifier;",
1435             "",
1436             "@Qualifier",
1437             "@interface FooBaseMethodQualifier {}");
1438     CompilerTests.daggerCompiler(
1439             foo,
1440             fooBase,
1441             fooFieldQualifier,
1442             fooConstructorQualifier,
1443             fooMethodQualifier,
1444             fooBaseFieldQualifier,
1445             fooBaseConstructorQualifier,
1446             fooBaseMethodQualifier)
1447         .compile(
1448             subject -> {
1449               subject.hasErrorCount(0);
1450               subject.generatedSource(goldenFileRule.goldenSource("test/Foo_Factory"));
1451               subject.generatedSource(goldenFileRule.goldenSource("test/Foo_MembersInjector"));
1452               subject.generatedSource(goldenFileRule.goldenSource("test/FooBase_Factory"));
1453               subject.generatedSource(goldenFileRule.goldenSource("test/FooBase_MembersInjector"));
1454             });
1455   }
1456 }
1457