• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static com.google.testing.compile.CompilationSubject.assertThat;
20 import static dagger.internal.codegen.Compilers.compilerWithOptions;
21 import static dagger.internal.codegen.Compilers.daggerCompiler;
22 
23 import androidx.room.compiler.processing.util.Source;
24 import com.google.testing.compile.Compilation;
25 import com.google.testing.compile.JavaFileObjects;
26 import dagger.testing.compile.CompilerTests;
27 import dagger.testing.golden.GoldenFileRule;
28 import java.util.Collection;
29 import javax.tools.JavaFileObject;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.Parameterized;
34 import org.junit.runners.Parameterized.Parameters;
35 
36 @RunWith(Parameterized.class)
37 public class MapBindingComponentProcessorTest {
38   @Parameters(name = "{0}")
parameters()39   public static Collection<Object[]> parameters() {
40     return CompilerMode.TEST_PARAMETERS;
41   }
42 
43   @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule();
44 
45   private final CompilerMode compilerMode;
46 
MapBindingComponentProcessorTest(CompilerMode compilerMode)47   public MapBindingComponentProcessorTest(CompilerMode compilerMode) {
48     this.compilerMode = compilerMode;
49   }
50 
51   @Test
mapBindingsWithEnumKey()52   public void mapBindingsWithEnumKey() throws Exception {
53     Source mapModuleOneFile =
54         CompilerTests.javaSource(
55             "test.MapModuleOne",
56             "package test;",
57             "",
58             "import dagger.Module;",
59             "import dagger.Provides;",
60             "import dagger.multibindings.IntoMap;",
61             "",
62             "@Module",
63             "final class MapModuleOne {",
64             "  @Provides @IntoMap @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {",
65             "    return new AdminHandler();",
66             "  }",
67             "}");
68     Source mapModuleTwoFile =
69         CompilerTests.javaSource(
70             "test.MapModuleTwo",
71             "package test;",
72             "",
73             "import dagger.Module;",
74             "import dagger.Provides;",
75             "import dagger.multibindings.IntoMap;",
76             "",
77             "@Module",
78             "final class MapModuleTwo {",
79             "  @Provides @IntoMap @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {",
80             "    return new LoginHandler();",
81             "  }",
82             "}");
83     Source enumKeyFile =
84         CompilerTests.javaSource(
85             "test.PathKey",
86             "package test;",
87             "import dagger.MapKey;",
88             "import java.lang.annotation.Retention;",
89             "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
90             "",
91             "@MapKey(unwrapValue = true)",
92             "@Retention(RUNTIME)",
93             "public @interface PathKey {",
94             "  PathEnum value();",
95             "}");
96     Source pathEnumFile =
97         CompilerTests.javaSource(
98             "test.PathEnum",
99             "package test;",
100             "",
101             "public enum PathEnum {",
102             "    ADMIN,",
103             "    LOGIN;",
104             "}");
105 
106     Source handlerFile =
107         CompilerTests.javaSource(
108             "test.Handler",
109             "package test;",
110             "",
111             "interface Handler {}");
112     Source loginHandlerFile =
113         CompilerTests.javaSource(
114             "test.LoginHandler",
115             "package test;",
116             "",
117             "class LoginHandler implements Handler {",
118             "  public LoginHandler() {}",
119             "}");
120     Source adminHandlerFile =
121         CompilerTests.javaSource(
122             "test.AdminHandler",
123             "package test;",
124             "",
125             "class AdminHandler implements Handler {",
126             "  public AdminHandler() {}",
127             "}");
128     Source componentFile =
129         CompilerTests.javaSource(
130             "test.TestComponent",
131             "package test;",
132             "",
133             "import dagger.Component;",
134             "import java.util.Map;",
135             "import javax.inject.Provider;",
136             "",
137             "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
138             "interface TestComponent {",
139             "  Provider<Map<PathEnum, Provider<Handler>>> dispatcher();",
140             "}");
141 
142     CompilerTests.daggerCompiler(
143             mapModuleOneFile,
144             mapModuleTwoFile,
145             enumKeyFile,
146             pathEnumFile,
147             handlerFile,
148             loginHandlerFile,
149             adminHandlerFile,
150             componentFile)
151         .withProcessingOptions(compilerMode.processorOptions())
152         .compile(
153             subject -> {
154               subject.hasErrorCount(0);
155               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
156             });
157   }
158 
159   @Test
mapBindingsWithInaccessibleKeys()160   public void mapBindingsWithInaccessibleKeys() throws Exception {
161     JavaFileObject mapKeys =
162         JavaFileObjects.forSourceLines(
163             "mapkeys.MapKeys",
164             "package mapkeys;",
165             "",
166             "import dagger.MapKey;",
167             "import dagger.multibindings.ClassKey;",
168             "",
169             "public class MapKeys {",
170             "  @MapKey(unwrapValue = false)",
171             "  public @interface ComplexKey {",
172             "    Class<?>[] manyClasses();",
173             "    Class<?> oneClass();",
174             "    ClassKey annotation();",
175             "  }",
176             "",
177             "  @MapKey",
178             "  @interface EnumKey {",
179             "    PackagePrivateEnum value();",
180             "  }",
181             "",
182             "  enum PackagePrivateEnum { INACCESSIBLE }",
183             "",
184             "  interface Inaccessible {}",
185             "}");
186     JavaFileObject moduleFile =
187         JavaFileObjects.forSourceLines(
188             "mapkeys.MapModule",
189             "package mapkeys;",
190             "",
191             "import dagger.Binds;",
192             "import dagger.Module;",
193             "import dagger.Provides;",
194             "import dagger.multibindings.ClassKey;",
195             "import dagger.multibindings.IntoMap;",
196             "import java.util.Map;",
197             "import javax.inject.Provider;",
198             "",
199             "@Module",
200             "public interface MapModule {",
201             "  @Provides @IntoMap @ClassKey(MapKeys.Inaccessible.class)",
202             "  static int classKey() { return 1; }",
203             "",
204             "  @Provides @IntoMap @MapKeys.EnumKey(MapKeys.PackagePrivateEnum.INACCESSIBLE)",
205             "  static int enumKey() { return 1; }",
206             "",
207             "  @Binds Object bindInaccessibleEnumMapToAccessibleTypeForComponent(",
208             "    Map<MapKeys.PackagePrivateEnum, Integer> map);",
209             "",
210             "  @Provides @IntoMap",
211             "  @MapKeys.ComplexKey(",
212             "    manyClasses = {java.lang.Object.class, java.lang.String.class},",
213             "    oneClass = MapKeys.Inaccessible.class,",
214             "    annotation = @ClassKey(java.lang.Object.class)",
215             "  )",
216             "  static int complexKeyWithInaccessibleValue() { return 1; }",
217             "",
218             "  @Provides @IntoMap",
219             "  @MapKeys.ComplexKey(",
220             "    manyClasses = {MapKeys.Inaccessible.class, java.lang.String.class},",
221             "    oneClass = java.lang.String.class,",
222             "    annotation = @ClassKey(java.lang.Object.class)",
223             "  )",
224             "  static int complexKeyWithInaccessibleArrayValue() { return 1; }",
225             "",
226             "  @Provides @IntoMap",
227             "  @MapKeys.ComplexKey(",
228             "    manyClasses = {java.lang.String.class},",
229             "    oneClass = java.lang.String.class,",
230             "    annotation = @ClassKey(MapKeys.Inaccessible.class)",
231             "  )",
232             "  static int complexKeyWithInaccessibleAnnotationValue() { return 1; }",
233             "}");
234     JavaFileObject componentFile =
235         JavaFileObjects.forSourceLines(
236             "test.TestComponent",
237             "package test;",
238             "",
239             "import dagger.Component;",
240             "import java.util.Map;",
241             "import javax.inject.Provider;",
242             "import mapkeys.MapKeys;",
243             "import mapkeys.MapModule;",
244             "",
245             "@Component(modules = MapModule.class)",
246             "interface TestComponent {",
247             "  Map<Class<?>, Integer> classKey();",
248             "  Provider<Map<Class<?>, Integer>> classKeyProvider();",
249             "",
250             "  Object inaccessibleEnum();",
251             "  Provider<Object> inaccessibleEnumProvider();",
252             "",
253             "  Map<MapKeys.ComplexKey, Integer> complexKey();",
254             "  Provider<Map<MapKeys.ComplexKey, Integer>> complexKeyProvider();",
255             "}");
256     Compilation compilation = daggerCompiler().compile(mapKeys, moduleFile, componentFile);
257     assertThat(compilation).succeeded();
258     assertThat(compilation)
259         .generatedSourceFile("test.DaggerTestComponent")
260         .hasSourceEquivalentTo(goldenFileRule.goldenFile("test.DaggerTestComponent"));
261     assertThat(compilation)
262         .generatedSourceFile(
263             "mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey")
264         .hasSourceEquivalentTo(
265             goldenFileRule.goldenFile(
266                 "mapkeys.MapModule_ComplexKeyWithInaccessibleAnnotationValueMapKey"));
267     assertThat(compilation)
268         .generatedSourceFile("mapkeys.MapModule_ClassKeyMapKey")
269         .hasSourceEquivalentTo(goldenFileRule.goldenFile("mapkeys.MapModule_ClassKeyMapKey"));
270   }
271 
272   @Test
mapBindingsWithStringKey()273   public void mapBindingsWithStringKey() throws Exception {
274     Source mapModuleOneFile =
275         CompilerTests.javaSource(
276             "test.MapModuleOne",
277             "package test;",
278             "",
279             "import dagger.Module;",
280             "import dagger.Provides;",
281             "import dagger.multibindings.StringKey;",
282             "import dagger.multibindings.IntoMap;",
283             "",
284             "@Module",
285             "final class MapModuleOne {",
286             "  @Provides @IntoMap @StringKey(\"Admin\") Handler provideAdminHandler() {",
287             "    return new AdminHandler();",
288             "  }",
289             "}");
290     Source mapModuleTwoFile =
291         CompilerTests.javaSource(
292             "test.MapModuleTwo",
293             "package test;",
294             "",
295             "import dagger.Module;",
296             "import dagger.Provides;",
297             "import dagger.multibindings.IntoMap;",
298             "import dagger.multibindings.StringKey;",
299             "",
300             "@Module",
301             "final class MapModuleTwo {",
302             "  @Provides @IntoMap @StringKey(\"Login\") Handler provideLoginHandler() {",
303             "    return new LoginHandler();",
304             "  }",
305             "}");
306     Source handlerFile =
307         CompilerTests.javaSource(
308             "test.Handler",
309             "package test;",
310             "",
311             "interface Handler {}");
312     Source loginHandlerFile =
313         CompilerTests.javaSource(
314             "test.LoginHandler",
315             "package test;",
316             "",
317             "class LoginHandler implements Handler {",
318             "  public LoginHandler() {}",
319             "}");
320     Source adminHandlerFile =
321         CompilerTests.javaSource(
322             "test.AdminHandler",
323             "package test;",
324             "",
325             "class AdminHandler implements Handler {",
326             "  public AdminHandler() {}",
327             "}");
328     Source componentFile =
329         CompilerTests.javaSource(
330             "test.TestComponent",
331             "package test;",
332             "",
333             "import dagger.Component;",
334             "import java.util.Map;",
335             "import javax.inject.Provider;",
336             "",
337             "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
338             "interface TestComponent {",
339             "  Provider<Map<String, Provider<Handler>>> dispatcher();",
340             "}");
341 
342     CompilerTests.daggerCompiler(
343             mapModuleOneFile,
344             mapModuleTwoFile,
345             handlerFile,
346             loginHandlerFile,
347             adminHandlerFile,
348             componentFile)
349         .withProcessingOptions(compilerMode.processorOptions())
350         .compile(
351             subject -> {
352               subject.hasErrorCount(0);
353               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
354             });
355   }
356 
357   @Test
mapBindingsWithWrappedKey()358   public void mapBindingsWithWrappedKey() throws Exception {
359     JavaFileObject mapModuleOneFile =
360         JavaFileObjects
361             .forSourceLines("test.MapModuleOne",
362                 "package test;",
363                 "",
364                 "import dagger.Module;",
365                 "import dagger.Provides;",
366                 "import dagger.multibindings.IntoMap;",
367                 "",
368                 "@Module",
369                 "final class MapModuleOne {",
370                 "  @Provides @IntoMap",
371                 "  @WrappedClassKey(Integer.class) Handler provideAdminHandler() {",
372                 "    return new AdminHandler();",
373                 "  }",
374                 "}");
375     JavaFileObject mapModuleTwoFile =
376         JavaFileObjects
377             .forSourceLines("test.MapModuleTwo",
378                 "package test;",
379                 "",
380                 "import dagger.Module;",
381                 "import dagger.Provides;",
382                 "import dagger.multibindings.IntoMap;",
383                 "",
384                 "@Module",
385                 "final class MapModuleTwo {",
386                 "  @Provides @IntoMap",
387                 "  @WrappedClassKey(Long.class) Handler provideLoginHandler() {",
388                 "    return new LoginHandler();",
389                 "  }",
390                 "}");
391     JavaFileObject wrappedClassKeyFile = JavaFileObjects.forSourceLines("test.WrappedClassKey",
392         "package test;",
393         "import dagger.MapKey;",
394         "import java.lang.annotation.Retention;",
395         "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
396         "",
397         "@MapKey(unwrapValue = false)",
398         "@Retention(RUNTIME)",
399         "public @interface WrappedClassKey {",
400         "  Class<?> value();",
401         "}");
402     JavaFileObject handlerFile =
403         JavaFileObjects.forSourceLines("test.Handler", "package test;", "", "interface Handler {}");
404     JavaFileObject loginHandlerFile =
405         JavaFileObjects.forSourceLines(
406             "test.LoginHandler",
407             "package test;",
408             "",
409             "class LoginHandler implements Handler {",
410             "  public LoginHandler() {}",
411             "}");
412     JavaFileObject adminHandlerFile =
413         JavaFileObjects.forSourceLines(
414             "test.AdminHandler",
415             "package test;",
416             "",
417             "class AdminHandler implements Handler {",
418             "  public AdminHandler() {}",
419             "}");
420     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
421         "package test;",
422         "",
423         "import dagger.Component;",
424         "import java.util.Map;",
425         "import javax.inject.Provider;",
426         "",
427         "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
428         "interface TestComponent {",
429         "  Provider<Map<WrappedClassKey, Provider<Handler>>> dispatcher();",
430         "}");
431 
432     Compilation compilation =
433         compilerWithOptions(compilerMode.javacopts())
434             .compile(
435                 mapModuleOneFile,
436                 mapModuleTwoFile,
437                 wrappedClassKeyFile,
438                 handlerFile,
439                 loginHandlerFile,
440                 adminHandlerFile,
441                 componentFile);
442     assertThat(compilation).succeeded();
443     assertThat(compilation)
444         .generatedSourceFile("test.DaggerTestComponent")
445         .hasSourceEquivalentTo(goldenFileRule.goldenFile("test.DaggerTestComponent"));
446   }
447 
448   @Test
mapBindingsWithNonProviderValue()449   public void mapBindingsWithNonProviderValue() throws Exception {
450     Source mapModuleOneFile =
451         CompilerTests.javaSource(
452             "test.MapModuleOne",
453             "package test;",
454             "",
455             "import dagger.Module;",
456             "import dagger.Provides;",
457             "import dagger.multibindings.IntoMap;",
458             "",
459             "@Module",
460             "final class MapModuleOne {",
461             "  @Provides @IntoMap @PathKey(PathEnum.ADMIN) Handler provideAdminHandler() {",
462             "    return new AdminHandler();",
463             "  }",
464             "}");
465     Source mapModuleTwoFile =
466         CompilerTests.javaSource(
467             "test.MapModuleTwo",
468             "package test;",
469             "",
470             "import dagger.Module;",
471             "import dagger.Provides;",
472             "import dagger.multibindings.IntoMap;",
473             "",
474             "@Module",
475             "final class MapModuleTwo {",
476             "  @Provides @IntoMap @PathKey(PathEnum.LOGIN) Handler provideLoginHandler() {",
477             "    return new LoginHandler();",
478             "  }",
479             "}");
480     Source enumKeyFile =
481         CompilerTests.javaSource(
482             "test.PathKey",
483             "package test;",
484             "import dagger.MapKey;",
485             "import java.lang.annotation.Retention;",
486             "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
487             "",
488             "@MapKey(unwrapValue = true)",
489             "@Retention(RUNTIME)",
490             "public @interface PathKey {",
491             "  PathEnum value();",
492             "}");
493     Source pathEnumFile =
494         CompilerTests.javaSource(
495             "test.PathEnum",
496             "package test;",
497             "",
498             "public enum PathEnum {",
499             "    ADMIN,",
500             "    LOGIN;",
501             "}");
502     Source handlerFile =
503         CompilerTests.javaSource(
504             "test.Handler",
505             "package test;",
506             "",
507             "interface Handler {}");
508     Source loginHandlerFile =
509         CompilerTests.javaSource(
510             "test.LoginHandler",
511             "package test;",
512             "",
513             "class LoginHandler implements Handler {",
514             "  public LoginHandler() {}",
515             "}");
516     Source adminHandlerFile =
517         CompilerTests.javaSource(
518             "test.AdminHandler",
519             "package test;",
520             "",
521             "class AdminHandler implements Handler {",
522             "  public AdminHandler() {}",
523             "}");
524     Source componentFile =
525         CompilerTests.javaSource(
526             "test.TestComponent",
527             "package test;",
528             "",
529             "import dagger.Component;",
530             "import java.util.Map;",
531             "import javax.inject.Provider;",
532             "",
533             "@Component(modules = {MapModuleOne.class, MapModuleTwo.class})",
534             "interface TestComponent {",
535             "  Provider<Map<PathEnum, Handler>> dispatcher();",
536             "}");
537 
538     CompilerTests.daggerCompiler(
539             mapModuleOneFile,
540             mapModuleTwoFile,
541             enumKeyFile,
542             pathEnumFile,
543             handlerFile,
544             loginHandlerFile,
545             adminHandlerFile,
546             componentFile)
547         .withProcessingOptions(compilerMode.processorOptions())
548         .compile(
549             subject -> {
550               subject.hasErrorCount(0);
551               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
552             });
553   }
554 
555   @Test
injectMapWithoutMapBinding()556   public void injectMapWithoutMapBinding() throws Exception {
557     Source mapModuleFile =
558         CompilerTests.javaSource(
559             "test.MapModule",
560             "package test;",
561             "",
562             "import dagger.Module;",
563             "import dagger.Provides;",
564             "import java.util.HashMap;",
565             "import java.util.Map;",
566             "",
567             "@Module",
568             "final class MapModule {",
569             "  @Provides Map<String, String> provideAMap() {",
570             "    Map<String, String> map = new HashMap<String, String>();",
571             "    map.put(\"Hello\", \"World\");",
572             "    return map;",
573             "  }",
574             "}");
575     Source componentFile =
576         CompilerTests.javaSource(
577             "test.TestComponent",
578             "package test;",
579             "",
580             "import dagger.Component;",
581             "import java.util.Map;",
582             "",
583             "@Component(modules = {MapModule.class})",
584             "interface TestComponent {",
585             "  Map<String, String> dispatcher();",
586             "}");
587 
588     CompilerTests.daggerCompiler(mapModuleFile, componentFile)
589         .withProcessingOptions(compilerMode.processorOptions())
590         .compile(
591             subject -> {
592               subject.hasErrorCount(0);
593               subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent"));
594             });
595   }
596 }
597