1 /*
2  * Copyright (C) 2017 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 dagger.testing.compile.CompilerTests;
21 import dagger.testing.golden.GoldenFileRule;
22 import java.util.Collection;
23 import org.junit.Rule;
24 import org.junit.Test;
25 import org.junit.runner.RunWith;
26 import org.junit.runners.Parameterized;
27 import org.junit.runners.Parameterized.Parameters;
28 
29 @RunWith(Parameterized.class)
30 public class DelegateRequestRepresentationTest {
31   @Parameters(name = "{0}")
parameters()32   public static Collection<Object[]> parameters() {
33     return CompilerMode.TEST_PARAMETERS;
34   }
35 
36   @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule();
37 
38   private final CompilerMode compilerMode;
39 
DelegateRequestRepresentationTest(CompilerMode compilerMode)40   public DelegateRequestRepresentationTest(CompilerMode compilerMode) {
41     this.compilerMode = compilerMode;
42   }
43 
44   private static final Source REGULAR_SCOPED =
45       CompilerTests.javaSource(
46           "test.RegularScoped",
47           "package test;",
48           "",
49           "import javax.inject.Scope;",
50           "import javax.inject.Inject;",
51           "",
52           "@RegularScoped.CustomScope",
53           "class RegularScoped {",
54           "  @Inject RegularScoped() {}",
55           "",
56           "  @Scope @interface CustomScope {}",
57           "}");
58 
59   private static final Source REUSABLE_SCOPED =
60       CompilerTests.javaSource(
61           "test.ReusableScoped",
62           "package test;",
63           "",
64           "import dagger.Reusable;",
65           "import javax.inject.Inject;",
66           "",
67           "@Reusable",
68           "class ReusableScoped {",
69           "  @Inject ReusableScoped() {}",
70           "}");
71 
72   private static final Source UNSCOPED =
73       CompilerTests.javaSource(
74           "test.Unscoped",
75           "package test;",
76           "",
77           "import javax.inject.Inject;",
78           "",
79           "class Unscoped {",
80           "  @Inject Unscoped() {}",
81           "}");
82 
83   private static final Source COMPONENT =
84       CompilerTests.javaSource(
85           "test.TestComponent",
86           "package test;",
87           "",
88           "import dagger.Component;",
89           "",
90           "@Component(modules = TestModule.class)",
91           "@RegularScoped.CustomScope",
92           "interface TestComponent {",
93           "  @Qualifier(RegularScoped.class)",
94           "  Object regular();",
95           "",
96           "  @Qualifier(ReusableScoped.class)",
97           "  Object reusable();",
98           "",
99           "  @Qualifier(Unscoped.class)",
100           "  Object unscoped();",
101           "}");
102 
103   private static final Source QUALIFIER =
104       CompilerTests.javaSource(
105           "test.Qualifier",
106           "package test;",
107           "",
108           "@javax.inject.Qualifier",
109           "@interface Qualifier {",
110           "  Class<?> value();",
111           "}");
112 
113   @Test
toDoubleCheck()114   public void toDoubleCheck() throws Exception {
115     Source module =
116         CompilerTests.javaSource(
117             "test.TestModule",
118             "package test;",
119             "",
120             "import dagger.Binds;",
121             "import dagger.Module;",
122             "",
123             "@Module",
124             "interface TestModule {",
125             "  @Binds @RegularScoped.CustomScope @Qualifier(RegularScoped.class)",
126             "  Object regular(RegularScoped delegate);",
127             "",
128             "  @Binds @RegularScoped.CustomScope @Qualifier(ReusableScoped.class)",
129             "  Object reusable(ReusableScoped delegate);",
130             "",
131             "  @Binds @RegularScoped.CustomScope @Qualifier(Unscoped.class)",
132             "  Object unscoped(Unscoped delegate);",
133             "}");
134 
135     CompilerTests.daggerCompiler(
136             module, COMPONENT, QUALIFIER, REGULAR_SCOPED, REUSABLE_SCOPED, UNSCOPED)
137         .withProcessingOptions(compilerMode.processorOptions())
138         .compile(
139             subject -> {
140               subject.hasErrorCount(0);
141               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
142             });
143   }
144 
145   @Test
toSingleCheck()146   public void toSingleCheck() throws Exception {
147     Source module =
148         CompilerTests.javaSource(
149             "test.TestModule",
150             "package test;",
151             "",
152             "import dagger.Binds;",
153             "import dagger.Module;",
154             "import dagger.Reusable;",
155             "",
156             "@Module",
157             "interface TestModule {",
158             "  @Binds @Reusable @Qualifier(RegularScoped.class)",
159             "  Object regular(RegularScoped delegate);",
160             "",
161             "  @Binds @Reusable @Qualifier(ReusableScoped.class)",
162             "  Object reusable(ReusableScoped delegate);",
163             "",
164             "  @Binds @Reusable @Qualifier(Unscoped.class)",
165             "  Object unscoped(Unscoped delegate);",
166             "}");
167 
168     CompilerTests.daggerCompiler(
169             module, COMPONENT, QUALIFIER, REGULAR_SCOPED, REUSABLE_SCOPED, UNSCOPED)
170         .withProcessingOptions(compilerMode.processorOptions())
171         .compile(
172             subject -> {
173               subject.hasErrorCount(0);
174               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
175             });
176   }
177 
178   @Test
toUnscoped()179   public void toUnscoped() throws Exception {
180     Source module =
181         CompilerTests.javaSource(
182             "test.TestModule",
183             "package test;",
184             "",
185             "import dagger.Binds;",
186             "import dagger.Module;",
187             "",
188             "@Module",
189             "interface TestModule {",
190             "  @Binds @Qualifier(RegularScoped.class)",
191             "  Object regular(RegularScoped delegate);",
192             "",
193             "  @Binds @Qualifier(ReusableScoped.class)",
194             "  Object reusable(ReusableScoped delegate);",
195             "",
196             "  @Binds @Qualifier(Unscoped.class)",
197             "  Object unscoped(Unscoped delegate);",
198             "}");
199 
200     CompilerTests.daggerCompiler(
201             module, COMPONENT, QUALIFIER, REGULAR_SCOPED, REUSABLE_SCOPED, UNSCOPED)
202         .withProcessingOptions(compilerMode.processorOptions())
203         .compile(
204             subject -> {
205               subject.hasErrorCount(0);
206               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
207             });
208   }
209 
210   @Test
castNeeded_rawTypes_Provider_get()211   public void castNeeded_rawTypes_Provider_get() throws Exception {
212     Source accessibleSupertype =
213         CompilerTests.javaSource(
214             "other.Supertype",
215             "package other;",
216             "",
217             // accessible from the component, but the subtype is not
218             "public interface Supertype {}");
219     Source inaccessibleSubtype =
220         CompilerTests.javaSource(
221             "other.Subtype",
222             "package other;",
223             "",
224             "import javax.inject.Inject;",
225             "import javax.inject.Singleton;",
226             "",
227             "@Singleton",
228             "class Subtype implements Supertype {",
229             "  @Inject Subtype() {}",
230             "}");
231     Source module =
232         CompilerTests.javaSource(
233             "other.SupertypeModule",
234             "package other;",
235             "",
236             "import dagger.Binds;",
237             "import dagger.Module;",
238             "",
239             "@Module",
240             "public interface SupertypeModule {",
241             "  @Binds Supertype to(Subtype subtype);",
242             "}");
243     Source component =
244         CompilerTests.javaSource(
245             "test.TestComponent",
246             "package test;",
247             "",
248             "import dagger.Component;",
249             "import javax.inject.Singleton;",
250             "",
251             "@Singleton",
252             "@Component(modules = other.SupertypeModule.class)",
253             "interface TestComponent {",
254             "  other.Supertype supertype();",
255             "}");
256 
257     CompilerTests.daggerCompiler(accessibleSupertype, inaccessibleSubtype, module, component)
258         .withProcessingOptions(compilerMode.processorOptions())
259         .compile(
260             subject -> {
261               subject.hasErrorCount(0);
262               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
263             });
264   }
265 
266   @Test
noCast_rawTypes_Provider_get_toInaccessibleType()267   public void noCast_rawTypes_Provider_get_toInaccessibleType() throws Exception {
268     Source supertype =
269         CompilerTests.javaSource(
270             "other.Supertype",
271             "package other;",
272             "",
273             "interface Supertype {}");
274     Source subtype =
275         CompilerTests.javaSource(
276             "other.Subtype",
277             "package other;",
278             "",
279             "import javax.inject.Inject;",
280             "import javax.inject.Singleton;",
281             "",
282             "@Singleton",
283             "class Subtype implements Supertype {",
284             "  @Inject Subtype() {}",
285             "}");
286     Source usesSupertype =
287         CompilerTests.javaSource(
288             "other.UsesSupertype",
289             "package other;",
290             "",
291             "import javax.inject.Inject;",
292             "",
293             "public class UsesSupertype {",
294             "  @Inject UsesSupertype(Supertype supertype) {}",
295             "}");
296     Source module =
297         CompilerTests.javaSource(
298             "other.SupertypeModule",
299             "package other;",
300             "",
301             "import dagger.Binds;",
302             "import dagger.Module;",
303             "",
304             "@Module",
305             "public interface SupertypeModule {",
306             "  @Binds Supertype to(Subtype subtype);",
307             "}");
308     Source component =
309         CompilerTests.javaSource(
310             "test.TestComponent",
311             "package test;",
312             "",
313             "import dagger.Component;",
314             "import javax.inject.Singleton;",
315             "",
316             "@Singleton",
317             "@Component(modules = other.SupertypeModule.class)",
318             "interface TestComponent {",
319             "  other.UsesSupertype usesSupertype();",
320             "}");
321 
322     CompilerTests.daggerCompiler(supertype, subtype, usesSupertype, module, component)
323         .withProcessingOptions(compilerMode.processorOptions())
324         .compile(
325             subject -> {
326               subject.hasErrorCount(0);
327               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
328             });
329   }
330 
331   @Test
castedToRawType()332   public void castedToRawType() throws Exception {
333     Source module =
334         CompilerTests.javaSource(
335             "test.TestModule",
336             "package test;",
337             "",
338             "import dagger.Binds;",
339             "import dagger.Module;",
340             "import dagger.Provides;",
341             "import javax.inject.Named;",
342             "",
343             "@Module",
344             "interface TestModule {",
345             "  @Provides",
346             "  static String provideString() { return new String(); }",
347             "",
348             "  @Binds",
349             "  CharSequence charSequence(String string);",
350             "",
351             "  @Binds",
352             "  @Named(\"named\")",
353             "  String namedString(String string);",
354             "}");
355     Source component =
356          CompilerTests.javaSource(
357             "test.TestComponent",
358             "package test;",
359             "",
360             "import dagger.Component;",
361             "import javax.inject.Named;",
362             "import javax.inject.Provider;",
363             "",
364             "@Component(modules = TestModule.class)",
365             "interface TestComponent {",
366             "  Provider<CharSequence> charSequence();",
367             "  CharSequence charSequenceInstance();",
368             "",
369             "  @Named(\"named\") Provider<String> namedString();",
370             "}");
371 
372     CompilerTests.daggerCompiler(module, component)
373         .withProcessingOptions(compilerMode.processorOptions())
374         .compile(
375             subject -> {
376               subject.hasErrorCount(0);
377               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
378             });
379   }
380 
381   @Test
doubleBinds()382   public void doubleBinds() throws Exception {
383     Source module =
384         CompilerTests.javaSource(
385             "test.TestModule",
386             "package test;",
387             "",
388             "import dagger.Binds;",
389             "import dagger.Module;",
390             "import dagger.Provides;",
391             "",
392             "@Module",
393             "interface TestModule {",
394             "  @Provides",
395             "  static String provideString() { return new String(); }",
396             "",
397             "  @Binds",
398             "  CharSequence charSequence(String string);",
399             "",
400             "  @Binds",
401             "  Object object(CharSequence charSequence);",
402             "}");
403     Source component =
404         CompilerTests.javaSource(
405             "test.TestComponent",
406             "package test;",
407             "",
408             "import dagger.Component;",
409             "import javax.inject.Named;",
410             "import javax.inject.Provider;",
411             "",
412             "@Component(modules = TestModule.class)",
413             "interface TestComponent {",
414             "  Provider<CharSequence> charSequence();",
415             "  Provider<Object> object();",
416             "}");
417 
418     CompilerTests.daggerCompiler(module, component)
419         .withProcessingOptions(compilerMode.processorOptions())
420         .compile(
421             subject -> {
422               subject.hasErrorCount(0);
423               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
424             });
425   }
426 
427   @Test
inlineFactoryOfInacessibleType()428   public void inlineFactoryOfInacessibleType() throws Exception {
429     Source supertype =
430         CompilerTests.javaSource(
431             "other.Supertype",
432             "package other;",
433             "",
434             "public interface Supertype {}");
435     Source injectableSubtype =
436         CompilerTests.javaSource(
437             "other.Subtype",
438             "package other;",
439             "",
440             "import javax.inject.Inject;",
441             "",
442             "final class Subtype implements Supertype {",
443             // important: this doesn't have any dependencies and therefore the factory will be able
444             // to be referenced with an inline Subtype_Factory.create()
445             "  @Inject Subtype() {}",
446             "}");
447     Source module =
448         CompilerTests.javaSource(
449             "other.TestModule",
450             "package other;",
451             "",
452             "import dagger.Binds;",
453             "import dagger.Module;",
454             "",
455             "@Module",
456             "public interface TestModule {",
457             "  @Binds Supertype to(Subtype subtype);",
458             "}");
459     Source component =
460         CompilerTests.javaSource(
461             "test.RequestsSubtypeAsProvider",
462             "package test;",
463             "",
464             "import dagger.Component;",
465             "import javax.inject.Provider;",
466             "",
467             "@Component(modules = other.TestModule.class)",
468             "interface RequestsSubtypeAsProvider {",
469             "  Provider<other.Supertype> supertypeProvider();",
470             "}");
471 
472     CompilerTests.daggerCompiler(supertype, injectableSubtype, module, component)
473         .withProcessingOptions(compilerMode.processorOptions())
474         .compile(
475             subject -> {
476               subject.hasErrorCount(0);
477               subject.generatedSource(
478                   goldenFileRule.goldenSource("test/DaggerRequestsSubtypeAsProvider"));
479             });
480   }
481 
482   @Test
providerWhenBindsScopeGreaterThanDependencyScope()483   public void providerWhenBindsScopeGreaterThanDependencyScope() throws Exception {
484     Source module =
485         CompilerTests.javaSource(
486             "test.TestModule",
487             "package test;",
488             "",
489             "import dagger.Binds;",
490             "import dagger.Module;",
491             "import dagger.Provides;",
492             "import dagger.Reusable;",
493             "import javax.inject.Singleton;",
494             "",
495             "@Module",
496             "public abstract class TestModule {",
497             "  @Reusable",
498             "  @Provides",
499             "  static String provideString() {",
500             "    return \"\";",
501             "  }",
502             "",
503             "  @Binds",
504             "  @Singleton",
505             "  abstract Object bindString(String str);",
506             "}");
507     Source component =
508         CompilerTests.javaSource(
509             "test.TestComponent",
510             "package test;",
511             "",
512             "import dagger.Component;",
513             "import javax.inject.Singleton;",
514             "import javax.inject.Provider;",
515             "",
516             "@Singleton",
517             "@Component(modules = TestModule.class)",
518             "interface TestComponent {",
519             "  Provider<Object> object();",
520             "}");
521 
522     CompilerTests.daggerCompiler(module, component)
523         .withProcessingOptions(compilerMode.processorOptions())
524         .compile(
525             subject -> {
526               subject.hasErrorCount(0);
527               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
528             });
529   }
530 }
531