xref: /aosp_15_r20/external/dagger2/javatests/dagger/internal/codegen/MembersInjectionTest.java (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
1 /*
2  * Copyright (C) 2015 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.ImmutableList;
21 import com.google.common.collect.ImmutableMap;
22 import com.squareup.javapoet.MethodSpec;
23 import com.squareup.javapoet.TypeSpec;
24 import dagger.internal.codegen.javapoet.TypeNames;
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.Parameterized;
31 import org.junit.runners.Parameterized.Parameters;
32 
33 @RunWith(Parameterized.class)
34 public class MembersInjectionTest {
35   @Parameters(name = "{0}")
parameters()36   public static ImmutableList<Object[]> parameters() {
37     return CompilerMode.TEST_PARAMETERS;
38   }
39 
40   @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule();
41 
42   private final CompilerMode compilerMode;
43 
MembersInjectionTest(CompilerMode compilerMode)44   public MembersInjectionTest(CompilerMode compilerMode) {
45     this.compilerMode = compilerMode;
46   }
47 
48   @Test
injectKotlinProtectField_fails()49   public void injectKotlinProtectField_fails() {
50     Source injectFieldSrc =
51         CompilerTests.kotlinSource(
52             "MyClass.kt",
53             "package test",
54             "",
55             "import javax.inject.Inject",
56             "",
57             "class MyClass @Inject constructor() {",
58             "  @Inject protected lateinit var protectedField: String",
59             "}");
60     Source moduleSrc =
61         CompilerTests.kotlinSource(
62             "MyModule.kt",
63             "package test",
64             "",
65             "import dagger.Module",
66             "import dagger.Provides",
67             "",
68             "@Module",
69             "object MyModule {",
70             "  @Provides",
71             "  fun providesString() = \"hello\"",
72             "}");
73     Source componentSrc =
74         CompilerTests.kotlinSource(
75             "MyComponent.kt",
76             "package test",
77             "",
78             "import dagger.Component",
79             "@Component(modules = [MyModule::class])",
80             "interface MyComponent {}");
81     CompilerTests.daggerCompiler(injectFieldSrc, moduleSrc, componentSrc)
82         .withProcessingOptions(compilerMode.processorOptions())
83         .compile(
84             subject -> {
85               subject.hasErrorCount(1);
86               subject.hasErrorContaining(
87                   "Dagger injector does not have access to kotlin protected fields");
88             });
89   }
90 
91   @Test
injectJavaProtectField_succeeds()92   public void injectJavaProtectField_succeeds() {
93     Source injectFieldSrc =
94         CompilerTests.javaSource(
95             "test.MyClass",
96             "package test;",
97             "",
98             "import javax.inject.Inject;",
99             "",
100             "public final class MyClass {",
101             "  @Inject MyClass() {}",
102             "  @Inject protected String protectedField;",
103             "}");
104     Source moduleSrc =
105         CompilerTests.kotlinSource(
106             "MyModule.kt",
107             "package test",
108             "",
109             "import dagger.Module",
110             "import dagger.Provides",
111             "",
112             "@Module",
113             "object MyModule {",
114             "  @Provides",
115             "  fun providesString() = \"hello\"",
116             "}");
117     Source componentSrc =
118         CompilerTests.kotlinSource(
119             "MyComponent.kt",
120             "package test",
121             "",
122             "import dagger.Component",
123             "@Component(modules = [MyModule::class])",
124             "interface MyComponent {}");
125     CompilerTests.daggerCompiler(injectFieldSrc, moduleSrc, componentSrc)
126         .withProcessingOptions(compilerMode.processorOptions())
127         .compile(subject -> subject.hasErrorCount(0));
128   }
129 
130   @Test
parentClass_noInjectedMembers()131   public void parentClass_noInjectedMembers() throws Exception {
132     Source childFile =
133         CompilerTests.javaSource(
134             "test.Child",
135             "package test;",
136             "",
137             "import javax.inject.Inject;",
138             "",
139             "public final class Child extends Parent {",
140             "  @Inject Child() {}",
141             "}");
142     Source parentFile =
143         CompilerTests.javaSource(
144             "test.Parent",
145             "package test;",
146             "",
147             "public abstract class Parent {}");
148 
149     Source componentFile =
150         CompilerTests.javaSource(
151             "test.TestComponent",
152             "package test;",
153             "",
154             "import dagger.Component;",
155             "",
156             "@Component",
157             "interface TestComponent {",
158             "  Child child();",
159             "}");
160 
161     CompilerTests.daggerCompiler(childFile, parentFile, componentFile)
162         .withProcessingOptions(compilerMode.processorOptions())
163         .compile(
164             subject -> {
165               subject.hasErrorCount(0);
166               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
167             });
168   }
169 
170   @Test
parentClass_injectedMembersInSupertype()171   public void parentClass_injectedMembersInSupertype() throws Exception {
172     Source childFile =
173         CompilerTests.javaSource(
174             "test.Child",
175             "package test;",
176             "",
177             "import javax.inject.Inject;",
178             "",
179             "public final class Child extends Parent {",
180             "  @Inject Child() {}",
181             "}");
182     Source parentFile =
183         CompilerTests.javaSource(
184             "test.Parent",
185             "package test;",
186             "",
187             "import javax.inject.Inject;",
188             "",
189             "public abstract class Parent {",
190             "  @Inject Dep dep;",
191             "}");
192     Source depFile =
193         CompilerTests.javaSource(
194             "test.Dep",
195             "package test;",
196             "",
197             "import javax.inject.Inject;",
198             "",
199             "final class Dep {",
200             "  @Inject Dep() {}",
201             "}");
202     Source componentFile =
203         CompilerTests.javaSource(
204             "test.TestComponent",
205             "package test;",
206             "",
207             "import dagger.Component;",
208             "",
209             "@Component",
210             "interface TestComponent {",
211             "  Child child();",
212             "}");
213 
214     CompilerTests.daggerCompiler(childFile, parentFile, depFile, componentFile)
215         .withProcessingOptions(compilerMode.processorOptions())
216         .compile(
217             subject -> {
218               subject.hasErrorCount(0);
219               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
220             });
221   }
222 
fieldAndMethodGenerics()223   @Test public void fieldAndMethodGenerics() {
224     Source file =
225         CompilerTests.javaSource(
226             "test.GenericClass",
227             "package test;",
228             "",
229             "import javax.inject.Inject;",
230             "",
231             "class GenericClass<A, B> {",
232             "  @Inject A a;",
233             "",
234             "  @Inject GenericClass() {}",
235             "",
236             " @Inject void register(B b) {}",
237             "}");
238 
239     CompilerTests.daggerCompiler(file)
240         .withProcessingOptions(compilerMode.processorOptions())
241         .compile(
242             subject -> {
243               subject.hasErrorCount(0);
244               subject.generatedSource(
245                   goldenFileRule.goldenSource("test/GenericClass_MembersInjector"));
246             });
247   }
248 
subclassedGenericMembersInjectors()249   @Test public void subclassedGenericMembersInjectors() {
250     Source a =
251         CompilerTests.javaSource(
252             "test.A",
253             "package test;",
254             "",
255             "import javax.inject.Inject;",
256             "",
257             "final class A {",
258             "  @Inject A() {}",
259             "}");
260     Source a2 =
261         CompilerTests.javaSource(
262             "test.A2",
263             "package test;",
264             "",
265             "import javax.inject.Inject;",
266             "",
267             "final class A2 {",
268             "  @Inject A2() {}",
269             "}");
270     Source parent =
271         CompilerTests.javaSource(
272             "test.Parent",
273             "package test;",
274             "",
275             "import javax.inject.Inject;",
276             "",
277             "class Parent<X, Y> {",
278             "  @Inject X x;",
279             "  @Inject Y y;",
280             "  @Inject A2 a2;",
281             "",
282             "  @Inject Parent() {}",
283             "}");
284     Source child =
285         CompilerTests.javaSource(
286             "test.Child",
287             "package test;",
288             "",
289             "import javax.inject.Inject;",
290             "",
291             "class Child<T> extends Parent<T, A> {",
292             "  @Inject A a;",
293             "  @Inject T t;",
294             "",
295             "  @Inject Child() {}",
296             "}");
297     CompilerTests.daggerCompiler(a, a2, parent, child)
298         .withProcessingOptions(compilerMode.processorOptions())
299         .compile(
300             subject -> {
301               subject.hasErrorCount(0);
302               subject.generatedSource(goldenFileRule.goldenSource("test/Child_MembersInjector"));
303             });
304   }
305 
fieldInjection()306   @Test public void fieldInjection() {
307     Source file =
308         CompilerTests.javaSource(
309             "test.FieldInjection",
310             "package test;",
311             "",
312             "import dagger.Lazy;",
313             "import javax.inject.Inject;",
314             "import javax.inject.Provider;",
315             "",
316             "class FieldInjection {",
317             "  @Inject String string;",
318             "  @Inject Lazy<String> lazyString;",
319             "  @Inject Provider<String> stringProvider;",
320             "}");
321     CompilerTests.daggerCompiler(file)
322         .withProcessingOptions(compilerMode.processorOptions())
323         .compile(
324             subject -> {
325               subject.hasErrorCount(0);
326               subject.generatedSource(
327                   goldenFileRule.goldenSource("test/FieldInjection_MembersInjector"));
328             });
329   }
330 
331   @Test
fieldInjectionWithQualifier()332   public void fieldInjectionWithQualifier() {
333     Source file =
334         CompilerTests.javaSource(
335             "test.FieldInjectionWithQualifier",
336             "package test;",
337             "",
338             "import dagger.Lazy;",
339             "import javax.inject.Inject;",
340             "import javax.inject.Named;",
341             "import javax.inject.Provider;",
342             "",
343             "class FieldInjectionWithQualifier {",
344             "  @Inject @Named(\"A\") String a;",
345             "  @Inject @Named(\"B\") String b;",
346             "}");
347     CompilerTests.daggerCompiler(file)
348         .withProcessingOptions(compilerMode.processorOptions())
349         .compile(
350             subject -> {
351               subject.hasErrorCount(0);
352               subject.generatedSource(
353                   goldenFileRule.goldenSource("test/FieldInjectionWithQualifier_MembersInjector"));
354             });
355   }
356 
methodInjection()357   @Test public void methodInjection() {
358     Source file =
359         CompilerTests.javaSource(
360             "test.MethodInjection",
361             "package test;",
362             "",
363             "import dagger.Lazy;",
364             "import javax.inject.Inject;",
365             "import javax.inject.Provider;",
366             "",
367             "class MethodInjection {",
368             "  @Inject void noArgs() {}",
369             "  @Inject void oneArg(String string) {}",
370             "  @Inject void manyArgs(",
371             "      String string, Lazy<String> lazyString, Provider<String> stringProvider) {}",
372             "}");
373     CompilerTests.daggerCompiler(file)
374         .withProcessingOptions(compilerMode.processorOptions())
375         .compile(
376             subject -> {
377               subject.hasErrorCount(0);
378               subject.generatedSource(
379                   goldenFileRule.goldenSource("test/MethodInjection_MembersInjector"));
380             });
381   }
382 
383   @Test
mixedMemberInjection()384   public void mixedMemberInjection() {
385     Source file =
386         CompilerTests.javaSource(
387             "test.MixedMemberInjection",
388             "package test;",
389             "",
390             "import dagger.Lazy;",
391             "import javax.inject.Inject;",
392             "import javax.inject.Provider;",
393             "",
394             "class MixedMemberInjection {",
395             "  @Inject String string;",
396             "  @Inject void setString(String s) {}",
397             "  @Inject Object object;",
398             "  @Inject void setObject(Object o) {}",
399             "}");
400     CompilerTests.daggerCompiler(file)
401         .withProcessingOptions(compilerMode.processorOptions())
402         .compile(
403             subject -> {
404               subject.hasErrorCount(0);
405               subject.generatedSource(
406                   goldenFileRule.goldenSource("test/MixedMemberInjection_MembersInjector"));
407             });
408   }
409 
injectConstructorAndMembersInjection()410   @Test public void injectConstructorAndMembersInjection() {
411     Source file =
412         CompilerTests.javaSource(
413             "test.AllInjections",
414             "package test;",
415             "",
416             "import javax.inject.Inject;",
417             "",
418             "class AllInjections {",
419             "  @Inject String s;",
420             "  @Inject AllInjections(String s) {}",
421             "  @Inject void s(String s) {}",
422             "}");
423     CompilerTests.daggerCompiler(file)
424         .withProcessingOptions(compilerMode.processorOptions())
425         .compile(
426             subject -> {
427               subject.hasErrorCount(0);
428               subject.generatedSource(
429                   goldenFileRule.goldenSource("test/AllInjections_MembersInjector"));
430             });
431   }
432 
supertypeMembersInjection()433   @Test public void supertypeMembersInjection() {
434     Source aFile =
435         CompilerTests.javaSource(
436             "test.A",
437             "package test;",
438             "",
439             "class A {}");
440     Source bFile =
441         CompilerTests.javaSource(
442             "test.B",
443             "package test;",
444             "",
445             "import javax.inject.Inject;",
446             "",
447             "class B extends A {",
448             "  @Inject String s;",
449             "}");
450     CompilerTests.daggerCompiler(aFile, bFile)
451         .withProcessingOptions(compilerMode.processorOptions())
452         .compile(
453             subject -> {
454               subject.hasErrorCount(0);
455               subject.generatedSource(goldenFileRule.goldenSource("test/B_MembersInjector"));
456             });
457   }
458 
459   @Test
simpleComponentWithNesting()460   public void simpleComponentWithNesting() {
461     Source nestedTypesFile =
462         CompilerTests.javaSource(
463             "test.OuterType",
464             "package test;",
465             "",
466             "import dagger.Component;",
467             "import javax.inject.Inject;",
468             "",
469             "final class OuterType {",
470             "  static class A {",
471             "    @Inject A() {}",
472             "  }",
473             "  static class B {",
474             "    @Inject A a;",
475             "  }",
476             "  @Component interface SimpleComponent {",
477             "    A a();",
478             "    void inject(B b);",
479             "  }",
480             "}");
481     CompilerTests.daggerCompiler(nestedTypesFile)
482         .withProcessingOptions(compilerMode.processorOptions())
483         .compile(
484             subject -> {
485               subject.hasErrorCount(0);
486               subject.generatedSource(
487                   goldenFileRule.goldenSource("test/OuterType_B_MembersInjector"));
488             });
489   }
490 
491   @Test
componentWithNestingAndGeneratedType()492   public void componentWithNestingAndGeneratedType() {
493     Source nestedTypesFile =
494         CompilerTests.javaSource(
495             "test.OuterType",
496             "package test;",
497             "",
498             "import dagger.Component;",
499             "import javax.inject.Inject;",
500             "",
501             "final class OuterType {",
502             "  @Inject GeneratedInjectType generated;",
503             "  static class A {",
504             "    @Inject A() {}",
505             "  }",
506             "  static class B {",
507             "    @Inject A a;",
508             "  }",
509             "  @Component interface SimpleComponent {",
510             "    A a();",
511             "    void inject(B b);",
512             "  }",
513             "}");
514     TypeSpec generatedInjectType =
515         TypeSpec.classBuilder("GeneratedInjectType")
516             .addMethod(
517                 MethodSpec.constructorBuilder()
518                     .addAnnotation(TypeNames.INJECT_JAVAX)
519                     .build())
520             .build();
521 
522     CompilerTests.daggerCompiler(nestedTypesFile)
523         .withProcessingOptions(compilerMode.processorOptions())
524         .withProcessingSteps(() -> new GeneratingProcessingStep("test", generatedInjectType))
525         .compile(
526             subject -> {
527               subject.hasErrorCount(0);
528               subject.generatedSource(
529                   goldenFileRule.goldenSource("test/OuterType_B_MembersInjector"));
530             });
531   }
532 
533   @Test
lowerCaseNamedMembersInjector_forLowerCaseType()534   public void lowerCaseNamedMembersInjector_forLowerCaseType() {
535     Source foo =
536         CompilerTests.javaSource(
537             "test.foo",
538             "package test;",
539             "",
540             "import javax.inject.Inject;",
541             "",
542             "class foo {",
543             "  @Inject String string;",
544             "}");
545     Source fooModule =
546         CompilerTests.javaSource(
547             "test.fooModule",
548             "package test;",
549             "",
550             "import dagger.Module;",
551             "import dagger.Provides;",
552             "",
553             "@Module",
554             "class fooModule {",
555             "  @Provides String string() { return \"foo\"; }",
556             "}");
557     Source fooComponent =
558         CompilerTests.javaSource(
559             "test.fooComponent",
560             "package test;",
561             "",
562             "import dagger.Component;",
563             "",
564             "@Component(modules = fooModule.class)",
565             "interface fooComponent {",
566             "  void inject(foo target);",
567             "}");
568 
569     CompilerTests.daggerCompiler(foo, fooModule, fooComponent)
570         .withProcessingOptions(compilerMode.processorOptions())
571         .compile(
572             subject -> {
573               subject.hasErrorCount(0);
574               subject.generatedSourceFileWithPath("test/foo_MembersInjector.java");
575             });
576   }
577 
578   @Test
fieldInjectionForShadowedMember()579   public void fieldInjectionForShadowedMember() {
580     Source foo =
581         CompilerTests.javaSource(
582             "test.Foo",
583             "package test;",
584             "",
585             "import javax.inject.Inject;",
586             "",
587             "class Foo {",
588             "  @Inject Foo() {}",
589             "}");
590     Source bar =
591         CompilerTests.javaSource(
592             "test.Bar",
593             "package test;",
594             "",
595             "import javax.inject.Inject;",
596             "",
597             "class Bar {",
598             "  @Inject Bar() {}",
599             "}");
600     Source parent =
601         CompilerTests.javaSource(
602             "test.Parent",
603             "package test;",
604             "",
605             "import javax.inject.Inject;",
606             "",
607             "class Parent { ",
608             "  @Inject Foo object;",
609             "}");
610     Source child =
611         CompilerTests.javaSource(
612             "test.Child",
613             "package test;",
614             "",
615             "import javax.inject.Inject;",
616             "",
617             "class Child extends Parent { ",
618             "  @Inject Bar object;",
619             "}");
620     Source component =
621         CompilerTests.javaSource(
622             "test.C",
623             "package test;",
624             "",
625             "import dagger.Component;",
626             "",
627             "@Component",
628             "interface C { ",
629             "  void inject(Child child);",
630             "}");
631 
632     CompilerTests.daggerCompiler(foo, bar, parent, child, component)
633         .withProcessingOptions(compilerMode.processorOptions())
634         .compile(
635             subject -> {
636               subject.hasErrorCount(0);
637               subject.generatedSource(
638                   goldenFileRule.goldenSource("test/Child_MembersInjector"));
639             });
640   }
641 
privateNestedClassError()642   @Test public void privateNestedClassError() {
643     Source file =
644         CompilerTests.javaSource(
645             "test.OuterClass",
646             "package test;",
647             "",
648             "import javax.inject.Inject;",
649             "",
650             "final class OuterClass {",
651             "  private static final class InnerClass {",
652             "    @Inject int field;",
653             "  }",
654             "}");
655     CompilerTests.daggerCompiler(file)
656         .withProcessingOptions(compilerMode.processorOptions())
657         .compile(
658             subject -> {
659               subject.hasErrorCount(1);
660               subject.hasErrorContaining("Dagger does not support injection into private classes")
661                   .onSource(file)
662                   .onLine(6);
663             });
664   }
665 
privateNestedClassWarning()666   @Test public void privateNestedClassWarning() {
667     Source file =
668         CompilerTests.javaSource(
669             "test.OuterClass",
670             "package test;",
671             "",
672             "import javax.inject.Inject;",
673             "",
674             "final class OuterClass {",
675             "  private static final class InnerClass {",
676             "    @Inject int field;",
677             "  }",
678             "}");
679     CompilerTests.daggerCompiler(file)
680         .withProcessingOptions(
681             ImmutableMap.<String, String>builder()
682                 .putAll(compilerMode.processorOptions())
683                 .put("dagger.privateMemberValidation", "WARNING")
684                 .buildOrThrow())
685         .compile(
686             subject -> {
687               subject.hasErrorCount(0);
688               subject.hasWarningCount(1);
689               subject.hasWarningContaining("Dagger does not support injection into private classes")
690                   .onSource(file)
691                   .onLine(6);
692             });
693   }
694 
privateSuperclassIsOkIfNotInjectedInto()695   @Test public void privateSuperclassIsOkIfNotInjectedInto() {
696     Source file =
697         CompilerTests.javaSource(
698             "test.OuterClass",
699             "package test;",
700             "",
701             "import javax.inject.Inject;",
702             "",
703             "final class OuterClass {",
704             "  private static class BaseClass {}",
705             "",
706             "  static final class DerivedClass extends BaseClass {",
707             "    @Inject int field;",
708             "  }",
709             "}");
710     CompilerTests.daggerCompiler(file)
711         .withProcessingOptions(compilerMode.processorOptions())
712         .compile(subject -> subject.hasErrorCount(0));
713   }
714 
715   @Test
rawFrameworkTypeField()716   public void rawFrameworkTypeField() {
717     Source file =
718         CompilerTests.javaSource(
719             "test.RawFrameworkTypes",
720             "package test;",
721             "",
722             "import javax.inject.Inject;",
723             "import javax.inject.Provider;",
724             "",
725             "class RawProviderField {",
726             "  @Inject",
727             "  Provider fieldWithRawProvider;",
728             "}");
729 
730     CompilerTests.daggerCompiler(file)
731         .withProcessingOptions(compilerMode.processorOptions())
732         .compile(
733             subject -> {
734               subject.hasErrorCount(1);
735               subject.hasErrorContaining(
736                       "Dagger does not support injecting raw type: javax.inject.Provider")
737                   .onSource(file)
738                   .onLineContaining("Provider fieldWithRawProvider");
739             });
740   }
741 
742   @Test
throwExceptionInjectedMethod()743   public void throwExceptionInjectedMethod() {
744     Source file =
745         CompilerTests.javaSource(
746             "test.",
747             "package test;",
748             "",
749             "import javax.inject.Inject;",
750             "class SomeClass {",
751             "@Inject void inject() throws Exception {}",
752             "}");
753 
754     CompilerTests.daggerCompiler(file)
755         .withProcessingOptions(compilerMode.processorOptions())
756         .compile(
757             subject -> {
758               subject.hasErrorCount(1);
759               subject.hasErrorContaining(
760                       "Methods with @Inject may not throw checked exceptions. "
761                           + "Please wrap your exceptions in a RuntimeException instead.")
762                   .onSource(file)
763                   .onLineContaining("throws Exception");
764             });
765   }
766 
767   @Test
rawFrameworkMethodTypeParameter()768   public void rawFrameworkMethodTypeParameter() {
769     Source file =
770         CompilerTests.javaSource(
771             "test.RawFrameworkTypes",
772             "package test;",
773             "",
774             "import javax.inject.Inject;",
775             "import javax.inject.Provider;",
776             "",
777             "class RawProviderParameter {",
778             "  @Inject",
779             "  void methodInjection(",
780             "      Provider rawProviderParameter) {}",
781             "}");
782 
783     CompilerTests.daggerCompiler(file)
784         .withProcessingOptions(compilerMode.processorOptions())
785         .compile(
786             subject -> {
787               subject.hasErrorCount(1);
788               subject.hasErrorContaining(
789                       "Dagger does not support injecting raw type: javax.inject.Provider")
790                   .onSource(file)
791                   .onLineContaining("Provider rawProviderParameter");
792             });
793   }
794 
795   @Test
rawFrameworkConstructorTypeParameter()796   public void rawFrameworkConstructorTypeParameter() {
797     Source file =
798         CompilerTests.javaSource(
799             "test.RawFrameworkTypes",
800             "package test;",
801             "",
802             "import dagger.Component;",
803             "import javax.inject.Inject;",
804             "import javax.inject.Provider;",
805             "",
806             "class RawProviderParameter {",
807             "  @Inject",
808             "  RawProviderParameter(",
809             "      Provider rawProviderParameter) {}",
810             "}");
811 
812     CompilerTests.daggerCompiler(file)
813         .withProcessingOptions(compilerMode.processorOptions())
814         .compile(
815             subject -> {
816               subject.hasErrorCount(1);
817               subject.hasErrorContaining(
818                       "Dagger does not support injecting raw type: javax.inject.Provider")
819                   .onSource(file)
820                   .onLineContaining("Provider rawProviderParameter");
821             });
822   }
823 
824   @Test
injectsPrimitive()825   public void injectsPrimitive() throws Exception {
826     Source injectedType =
827         CompilerTests.javaSource(
828             "test.InjectedType",
829             "package test;",
830             "",
831             "import javax.inject.Inject;",
832             "",
833             "class InjectedType {",
834             "  @Inject InjectedType() {}",
835             "",
836             "  @Inject int primitiveInt;",
837             "  @Inject Integer boxedInt;",
838             "}");
839 
840     CompilerTests.daggerCompiler(injectedType)
841         .withProcessingOptions(compilerMode.processorOptions())
842         .compile(
843             subject -> {
844               subject.hasErrorCount(0);
845               subject.generatedSource(
846                   goldenFileRule.goldenSource("test/InjectedType_MembersInjector"));
847               subject.generatedSource(
848                   goldenFileRule.goldenSource("test/InjectedType_Factory"));
849             });
850   }
851 
852   @Test
accessibility()853   public void accessibility() throws Exception {
854     Source foo =
855         CompilerTests.javaSource(
856             "other.Foo",
857             "package other;",
858             "",
859             "import javax.inject.Inject;",
860             "",
861             "class Foo {",
862             "  @Inject Foo() {}",
863             "}");
864     Source inaccessible =
865         CompilerTests.javaSource(
866             "other.Inaccessible",
867             "package other;",
868             "",
869             "import javax.inject.Inject;",
870             "",
871             "class Inaccessible {",
872             "  @Inject Inaccessible() {}",
873             "  @Inject Foo foo;",
874             "  @Inject void method(Foo foo) {}",
875             "}");
876     Source usesInaccessible =
877         CompilerTests.javaSource(
878             "other.UsesInaccessible",
879             "package other;",
880             "",
881             "import javax.inject.Inject;",
882             "",
883             "public class UsesInaccessible {",
884             "  @Inject UsesInaccessible(Inaccessible inaccessible) {}",
885             "}");
886     Source component =
887         CompilerTests.javaSource(
888             "test.TestComponent",
889             "package test;",
890             "",
891             "import dagger.Component;",
892             "import other.UsesInaccessible;",
893             "",
894             "@Component",
895             "interface TestComponent {",
896             "  UsesInaccessible usesInaccessible();",
897             "}");
898 
899     CompilerTests.daggerCompiler(foo, inaccessible, usesInaccessible, component)
900         .withProcessingOptions(compilerMode.processorOptions())
901         .compile(
902             subject -> {
903               subject.hasErrorCount(0);
904               subject.generatedSource(
905                   goldenFileRule.goldenSource("other/Inaccessible_MembersInjector"));
906               subject.generatedSource(
907                   goldenFileRule.goldenSource("test/DaggerTestComponent"));
908             });
909   }
910 
911   @Test
accessibleRawType_ofInaccessibleType()912   public void accessibleRawType_ofInaccessibleType() throws Exception {
913     Source inaccessible =
914         CompilerTests.javaSource(
915             "other.Inaccessible",
916             "package other;",
917             "",
918             "class Inaccessible {}");
919     Source inaccessiblesModule =
920         CompilerTests.javaSource(
921             "other.InaccessiblesModule",
922             "package other;",
923             "",
924             "import dagger.Module;",
925             "import dagger.Provides;",
926             "import java.util.ArrayList;",
927             "import java.util.List;",
928             "import javax.inject.Provider;",
929             "import javax.inject.Singleton;",
930             "",
931             "@Module",
932             "public class InaccessiblesModule {",
933             // force Provider initialization
934             "  @Provides @Singleton static List<Inaccessible> inaccessibles() {",
935             "    return new ArrayList<>();",
936             "  }",
937             "}");
938     Source usesInaccessibles =
939         CompilerTests.javaSource(
940             "other.UsesInaccessibles",
941             "package other;",
942             "",
943             "import java.util.List;",
944             "import javax.inject.Inject;",
945             "",
946             "public class UsesInaccessibles {",
947             "  @Inject UsesInaccessibles() {}",
948             "  @Inject List<Inaccessible> inaccessibles;",
949             "}");
950     Source component =
951         CompilerTests.javaSource(
952             "test.TestComponent",
953             "package test;",
954             "",
955             "import dagger.Component;",
956             "import javax.inject.Singleton;",
957             "import other.UsesInaccessibles;",
958             "",
959             "@Singleton",
960             "@Component(modules = other.InaccessiblesModule.class)",
961             "interface TestComponent {",
962             "  UsesInaccessibles usesInaccessibles();",
963             "}");
964 
965     CompilerTests.daggerCompiler(inaccessible, inaccessiblesModule, usesInaccessibles, component)
966         .withProcessingOptions(compilerMode.processorOptions())
967         .compile(
968             subject -> {
969               subject.hasErrorCount(0);
970               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
971             });
972   }
973 
974   @Test
publicSupertypeHiddenSubtype()975   public void publicSupertypeHiddenSubtype() throws Exception {
976     Source foo =
977         CompilerTests.javaSource(
978             "other.Foo",
979             "package other;",
980             "",
981             "import javax.inject.Inject;",
982             "",
983             "class Foo {",
984             "  @Inject Foo() {}",
985             "}");
986     Source supertype =
987         CompilerTests.javaSource(
988             "other.Supertype",
989             "package other;",
990             "",
991             "import javax.inject.Inject;",
992             "",
993             "public class Supertype<T> {",
994             "  @Inject T t;",
995             "}");
996     Source subtype =
997         CompilerTests.javaSource(
998             "other.Subtype",
999             "package other;",
1000             "",
1001             "import javax.inject.Inject;",
1002             "",
1003             "class Subtype extends Supertype<Foo> {",
1004             "  @Inject Subtype() {}",
1005             "}");
1006     Source injectsSubtype =
1007         CompilerTests.javaSource(
1008             "other.InjectsSubtype",
1009             "package other;",
1010             "",
1011             "import javax.inject.Inject;",
1012             "",
1013             "public class InjectsSubtype {",
1014             "  @Inject InjectsSubtype(Subtype s) {}",
1015             "}");
1016     Source component =
1017         CompilerTests.javaSource(
1018             "test.TestComponent",
1019             "package test;",
1020             "",
1021             "import dagger.Component;",
1022             "",
1023             "@Component",
1024             "interface TestComponent {",
1025             "  other.InjectsSubtype injectsSubtype();",
1026             "}");
1027 
1028     CompilerTests.daggerCompiler(foo, supertype, subtype, injectsSubtype, component)
1029         .withProcessingOptions(compilerMode.processorOptions())
1030         .compile(
1031             subject -> {
1032               subject.hasErrorCount(0);
1033               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
1034             });
1035   }
1036 
1037   // Shows that we shouldn't create a members injector for a type that doesn't have
1038   // @Inject fields or @Inject constructor even if it extends and is extended by types that do.
1039   @Test
middleClassNoFieldInjection()1040   public void middleClassNoFieldInjection() throws Exception {
1041     Source classA =
1042         CompilerTests.javaSource(
1043             "test.A",
1044             "package test;",
1045             "",
1046             "import javax.inject.Inject;",
1047             "",
1048             "class A extends B {",
1049             "  @Inject String valueA;",
1050             "}");
1051     Source classB =
1052         CompilerTests.javaSource(
1053             "test.B",
1054             "package test;",
1055             "",
1056             "class B extends C {",
1057             "}");
1058     Source classC =
1059         CompilerTests.javaSource(
1060             "test.C",
1061             "package test;",
1062             "",
1063             "import javax.inject.Inject;",
1064             "",
1065             "class C { ",
1066             "  @Inject String valueC;",
1067             "}");
1068 
1069     CompilerTests.daggerCompiler(classA, classB, classC)
1070         .withProcessingOptions(compilerMode.processorOptions())
1071         .compile(
1072             subject -> {
1073               subject.hasErrorCount(0);
1074               subject.generatedSource(goldenFileRule.goldenSource("test/A_MembersInjector"));
1075               subject.generatedSource(goldenFileRule.goldenSource("test/C_MembersInjector"));
1076 
1077               try {
1078                 subject.generatedSourceFileWithPath("test/B_MembersInjector");
1079                 // Can't throw an assertion error since it would be caught.
1080                 throw new IllegalStateException("Test generated a B_MembersInjector");
1081               } catch (AssertionError expected) {}
1082             });
1083   }
1084 
1085   // Shows that we do generate a MembersInjector for a type that has an @Inject
1086   // constructor and that extends a type with @Inject fields, even if it has no local field
1087   // injection sites
1088   // TODO(erichang): Are these even used anymore?
1089   @Test
testConstructorInjectedFieldInjection()1090   public void testConstructorInjectedFieldInjection() throws Exception {
1091     Source classA =
1092         CompilerTests.javaSource(
1093             "test.A",
1094             "package test;",
1095             "",
1096             "import javax.inject.Inject;",
1097             "",
1098             "class A extends B {",
1099             "  @Inject A() {}",
1100             "}");
1101     Source classB =
1102         CompilerTests.javaSource(
1103             "test.B",
1104             "package test;",
1105             "",
1106             "import javax.inject.Inject;",
1107             "",
1108             "class B { ",
1109             "  @Inject String valueB;",
1110             "}");
1111 
1112     CompilerTests.daggerCompiler(classA, classB)
1113         .withProcessingOptions(compilerMode.processorOptions())
1114         .compile(
1115             subject -> {
1116               subject.hasErrorCount(0);
1117               subject.generatedSource(goldenFileRule.goldenSource("test/A_MembersInjector"));
1118               subject.generatedSource(goldenFileRule.goldenSource("test/B_MembersInjector"));
1119             });
1120   }
1121 
1122   // Regression test for https://github.com/google/dagger/issues/3143
1123   @Test
testMembersInjectionBindingExistsInParentComponent()1124   public void testMembersInjectionBindingExistsInParentComponent() throws Exception {
1125     Source component =
1126         CompilerTests.javaSource(
1127             "test.MyComponent",
1128             "package test;",
1129             "",
1130             "import dagger.Component;",
1131             "",
1132             "@Component(modules = MyComponentModule.class)",
1133             "public interface MyComponent {",
1134             "  void inject(Bar bar);",
1135             "",
1136             "  MySubcomponent subcomponent();",
1137             "}");
1138 
1139     Source subcomponent =
1140         CompilerTests.javaSource(
1141             "test.MySubcomponent",
1142             "package test;",
1143             "",
1144             "import dagger.Subcomponent;",
1145             "",
1146             "@Subcomponent(modules = MySubcomponentModule.class)",
1147             "interface MySubcomponent {",
1148             "  Foo foo();",
1149             "}");
1150 
1151     Source foo =
1152         CompilerTests.javaSource(
1153             "test.Foo",
1154             "package test;",
1155             "",
1156             "import javax.inject.Inject;",
1157             "",
1158             "class Foo {",
1159             "  @Inject Foo(Bar bar) {}",
1160             "}");
1161 
1162     Source bar =
1163         CompilerTests.javaSource(
1164             "test.Bar",
1165             "package test;",
1166             "",
1167             "import java.util.Set;",
1168             "import javax.inject.Inject;",
1169             "",
1170             "class Bar {",
1171             "  @Inject Set<String> multibindingStrings;",
1172             "  @Inject Bar() {}",
1173             "}");
1174 
1175     Source componentModule =
1176         CompilerTests.javaSource(
1177             "test.MyComponentModule",
1178             "package test;",
1179             "",
1180             "import dagger.Module;",
1181             "import dagger.Provides;",
1182             "import dagger.multibindings.IntoSet;",
1183             "",
1184             "@Module",
1185             "interface MyComponentModule {",
1186             "  @Provides",
1187             "  @IntoSet",
1188             "  static String provideString() {",
1189             "    return \"\";",
1190             "  }",
1191             "}");
1192 
1193     Source subcomponentModule =
1194         CompilerTests.javaSource(
1195             "test.MySubcomponentModule",
1196             "package test;",
1197             "",
1198             "import dagger.Module;",
1199             "import dagger.Provides;",
1200             "import dagger.multibindings.IntoSet;",
1201             "",
1202             "@Module",
1203             "interface MySubcomponentModule {",
1204             "  @Provides",
1205             "  @IntoSet",
1206             "  static String provideString() {",
1207             "    return \"\";",
1208             "  }",
1209             "}");
1210 
1211     CompilerTests.daggerCompiler(
1212             component, subcomponent, foo, bar, componentModule, subcomponentModule)
1213         .withProcessingOptions(compilerMode.processorOptions())
1214         .compile(
1215             subject -> {
1216               subject.hasErrorCount(0);
1217               // Check that the injectBar() method is not shared across components.
1218               // We avoid sharing them in general because they may be different (e.g. in this case
1219               // we inject multibindings that are different across components).
1220               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerMyComponent"));
1221             });
1222 
1223   }
1224 
1225   // Test that if both a MembersInjectionBinding and ProvisionBinding both exist in the same
1226   // component they share the same inject methods rather than generating their own.
1227   @Test
testMembersInjectionBindingSharesInjectMethodsWithProvisionBinding()1228   public void testMembersInjectionBindingSharesInjectMethodsWithProvisionBinding()
1229       throws Exception {
1230     Source component =
1231         CompilerTests.javaSource(
1232             "test.MyComponent",
1233             "package test;",
1234             "",
1235             "import dagger.Component;",
1236             "",
1237             "@Component",
1238             "public interface MyComponent {",
1239             "  Foo foo();",
1240             "",
1241             "  void inject(Foo foo);",
1242             "}");
1243 
1244     Source foo =
1245         CompilerTests.javaSource(
1246             "test.Foo",
1247             "package test;",
1248             "",
1249             "import javax.inject.Inject;",
1250             "",
1251             "class Foo {",
1252             "  @Inject Bar bar;",
1253             "  @Inject Foo() {}",
1254             "}");
1255 
1256     Source bar =
1257         CompilerTests.javaSource(
1258             "test.Bar",
1259             "package test;",
1260             "",
1261             "import javax.inject.Inject;",
1262             "",
1263             "class Bar {",
1264             "  @Inject Bar() {}",
1265             "}");
1266     CompilerTests.daggerCompiler(component, foo, bar)
1267         .withProcessingOptions(compilerMode.processorOptions())
1268         .compile(
1269             subject -> {
1270               subject.hasErrorCount(0);
1271               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerMyComponent"));
1272             });
1273   }
1274 }
1275