• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.google.testing.compile.CompilationSubject.assertThat;
20 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
22 import static dagger.internal.codegen.Compilers.compilerWithOptions;
23 
24 import com.google.testing.compile.Compilation;
25 import com.google.testing.compile.JavaFileObjects;
26 import java.util.Collection;
27 import javax.tools.JavaFileObject;
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 MapBindingExpressionWithGuavaTest {
35   @Parameters(name = "{0}")
parameters()36   public static Collection<Object[]> parameters() {
37     return CompilerMode.TEST_PARAMETERS;
38   }
39 
40   private final CompilerMode compilerMode;
41 
MapBindingExpressionWithGuavaTest(CompilerMode compilerMode)42   public MapBindingExpressionWithGuavaTest(CompilerMode compilerMode) {
43     this.compilerMode = compilerMode;
44   }
45 
46   @Test
mapBindings()47   public void mapBindings() {
48     JavaFileObject mapModuleFile =
49         JavaFileObjects.forSourceLines(
50             "test.MapModule",
51             "package test;",
52             "",
53             "import dagger.Module;",
54             "import dagger.Provides;",
55             "import dagger.multibindings.IntKey;",
56             "import dagger.multibindings.IntoMap;",
57             "import dagger.multibindings.LongKey;",
58             "import dagger.multibindings.Multibinds;",
59             "import java.util.Map;",
60             "",
61             "@Module",
62             "interface MapModule {",
63             "  @Multibinds Map<String, String> stringMap();",
64             "  @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }",
65             "  @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }",
66             "  @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }",
67             "  @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }",
68             "}");
69     JavaFileObject subcomponentModuleFile =
70         JavaFileObjects.forSourceLines(
71             "test.SubcomponentMapModule",
72             "package test;",
73             "",
74             "import dagger.Module;",
75             "import dagger.Provides;",
76             "import dagger.multibindings.IntKey;",
77             "import dagger.multibindings.IntoMap;",
78             "import dagger.multibindings.LongKey;",
79             "import dagger.multibindings.Multibinds;",
80             "import java.util.Map;",
81             "",
82             "@Module",
83             "interface SubcomponentMapModule {",
84             "  @Provides @IntoMap @LongKey(3) static long provideLong3() { return 3; }",
85             "  @Provides @IntoMap @LongKey(4) static long provideLong4() { return 4; }",
86             "  @Provides @IntoMap @LongKey(5) static long provideLong5() { return 5; }",
87             "}");
88     JavaFileObject componentFile =
89         JavaFileObjects.forSourceLines(
90             "test.TestComponent",
91             "package test;",
92             "",
93             "import dagger.Component;",
94             "import java.util.Map;",
95             "import javax.inject.Provider;",
96             "",
97             "@Component(modules = MapModule.class)",
98             "interface TestComponent {",
99             "  Map<String, String> strings();",
100             "  Map<String, Provider<String>> providerStrings();",
101             "",
102             "  Map<Integer, Integer> ints();",
103             "  Map<Integer, Provider<Integer>> providerInts();",
104             "  Map<Long, Long> longs();",
105             "  Map<Long, Provider<Long>> providerLongs();",
106             "",
107             "  Sub sub();",
108             "}");
109     JavaFileObject subcomponent =
110         JavaFileObjects.forSourceLines(
111             "test.Sub",
112             "package test;",
113             "",
114             "import dagger.Subcomponent;",
115             "import java.util.Map;",
116             "import javax.inject.Provider;",
117             "",
118             "@Subcomponent(modules = SubcomponentMapModule.class)",
119             "interface Sub {",
120             "  Map<Long, Long> longs();",
121             "  Map<Long, Provider<Long>> providerLongs();",
122             "}");
123     JavaFileObject generatedComponent =
124         compilerMode
125             .javaFileBuilder("test.DaggerTestComponent")
126             .addLines(
127                 "package test;",
128                 "",
129                 GeneratedLines.generatedAnnotations(),
130                 "final class DaggerTestComponent implements TestComponent {")
131             .addLinesIn(
132                 FAST_INIT_MODE,
133                 "  private volatile Provider<Integer> provideIntProvider;",
134                 "  private volatile Provider<Long> provideLong0Provider;",
135                 "  private volatile Provider<Long> provideLong1Provider;",
136                 "  private volatile Provider<Long> provideLong2Provider;",
137                 "",
138                 "  private Provider<Integer> provideIntProvider() {",
139                 "    Object local = provideIntProvider;",
140                 "    if (local == null) {",
141                 "      local = new SwitchingProvider<>(0);",
142                 "      provideIntProvider = (Provider<Integer>) local;",
143                 "    }",
144                 "    return (Provider<Integer>) local;",
145                 "  }",
146                 "",
147                 "  private Provider<Long> provideLong0Provider() {",
148                 "    Object local = provideLong0Provider;",
149                 "    if (local == null) {",
150                 "      local = new SwitchingProvider<>(1);",
151                 "      provideLong0Provider = (Provider<Long>) local;",
152                 "    }",
153                 "    return (Provider<Long>) local;",
154                 "  }",
155                 "",
156                 "  private Provider<Long> provideLong1Provider() {",
157                 "    Object local = provideLong1Provider;",
158                 "    if (local == null) {",
159                 "      local = new SwitchingProvider<>(2);",
160                 "      provideLong1Provider = (Provider<Long>) local;",
161                 "    }",
162                 "    return (Provider<Long>) local;",
163                 "  }",
164                 "",
165                 "  private Provider<Long> provideLong2Provider() {",
166                 "    Object local = provideLong2Provider;",
167                 "    if (local == null) {",
168                 "      local = new SwitchingProvider<>(3);",
169                 "      provideLong2Provider = (Provider<Long>) local;",
170                 "    }",
171                 "    return (Provider<Long>) local;",
172                 "  }")
173             .addLines(
174                 "  @Override",
175                 "  public Map<String, String> strings() {",
176                 "    return ImmutableMap.<String, String>of();",
177                 "  }",
178                 "",
179                 "  @Override",
180                 "  public Map<String, Provider<String>> providerStrings() {",
181                 "    return ImmutableMap.<String, Provider<String>>of();",
182                 "  }",
183                 "",
184                 "  @Override",
185                 "  public Map<Integer, Integer> ints() {",
186                 "    return ImmutableMap.<Integer, Integer>of(0, MapModule.provideInt());",
187                 "  }",
188                 "",
189                 "  @Override",
190                 "  public Map<Integer, Provider<Integer>> providerInts() {",
191                 "    return ImmutableMap.<Integer, Provider<Integer>>of(")
192             .addLinesIn(
193                 DEFAULT_MODE, //
194                 "        0, MapModule_ProvideIntFactory.create());")
195             .addLinesIn(
196                 FAST_INIT_MODE, //
197                 "        0, provideIntProvider());")
198             .addLines(
199                 "  }",
200                 "",
201                 "  @Override",
202                 "  public Map<Long, Long> longs() {",
203                 "    return ImmutableMap.<Long, Long>of(",
204                 "      0L, MapModule.provideLong0(),",
205                 "      1L, MapModule.provideLong1(),",
206                 "      2L, MapModule.provideLong2());",
207                 "  }",
208                 "",
209                 "  @Override",
210                 "  public Map<Long, Provider<Long>> providerLongs() {",
211                 "    return ImmutableMap.<Long, Provider<Long>>of(")
212             .addLinesIn(
213                 DEFAULT_MODE,
214                 "      0L, MapModule_ProvideLong0Factory.create(),",
215                 "      1L, MapModule_ProvideLong1Factory.create(),",
216                 "      2L, MapModule_ProvideLong2Factory.create());")
217             .addLinesIn(
218                 FAST_INIT_MODE,
219                 "      0L, provideLong0Provider(),",
220                 "      1L, provideLong1Provider(),",
221                 "      2L, provideLong2Provider());")
222             .addLines(
223                 "  }",
224                 "",
225                 "  @Override",
226                 "  public Sub sub() {",
227                 "    return new SubImpl();",
228                 "  }",
229                 "",
230                 "  private final class SubImpl implements Sub {")
231             .addLinesIn(
232                 FAST_INIT_MODE,
233                 "    private volatile Provider<Long> provideLong3Provider;",
234                 "    private volatile Provider<Long> provideLong4Provider;",
235                 "    private volatile Provider<Long> provideLong5Provider;",
236                 "    private SubImpl() {}",
237                 "",
238                 "    private Provider<Long> provideLong3Provider() {",
239                 "      Object local = provideLong3Provider;",
240                 "      if (local == null) {",
241                 "        local = new SwitchingProvider<>(0);",
242                 "        provideLong3Provider = (Provider<Long>) local;",
243                 "      }",
244                 "      return (Provider<Long>) local;",
245                 "    }",
246                 "",
247                 "    private Provider<Long> provideLong4Provider() {",
248                 "      Object local = provideLong4Provider;",
249                 "      if (local == null) {",
250                 "        local = new SwitchingProvider<>(1);",
251                 "        provideLong4Provider = (Provider<Long>) local;",
252                 "      }",
253                 "      return (Provider<Long>) local;",
254                 "    }",
255                 "",
256                 "    private Provider<Long> provideLong5Provider() {",
257                 "      Object local = provideLong5Provider;",
258                 "      if (local == null) {",
259                 "        local = new SwitchingProvider<>(2);",
260                 "        provideLong5Provider = (Provider<Long>) local;",
261                 "      }",
262                 "      return (Provider<Long>) local;",
263                 "    }")
264             .addLines(
265                 "    @Override",
266                 "    public Map<Long, Long> longs() {",
267                 "      return ImmutableMap.<Long, Long>builderWithExpectedSize(6)",
268                 "          .put(0L, MapModule.provideLong0())",
269                 "          .put(1L, MapModule.provideLong1())",
270                 "          .put(2L, MapModule.provideLong2())",
271                 "          .put(3L, SubcomponentMapModule.provideLong3())",
272                 "          .put(4L, SubcomponentMapModule.provideLong4())",
273                 "          .put(5L, SubcomponentMapModule.provideLong5())",
274                 "          .build();",
275                 "    }",
276                 "",
277                 "    @Override",
278                 "    public Map<Long, Provider<Long>> providerLongs() {",
279                 "      return ImmutableMap.<Long, Provider<Long>>builderWithExpectedSize(6)")
280             .addLinesIn(
281                 DEFAULT_MODE,
282                 "          .put(0L, MapModule_ProvideLong0Factory.create())",
283                 "          .put(1L, MapModule_ProvideLong1Factory.create())",
284                 "          .put(2L, MapModule_ProvideLong2Factory.create())",
285                 "          .put(3L, SubcomponentMapModule_ProvideLong3Factory.create())",
286                 "          .put(4L, SubcomponentMapModule_ProvideLong4Factory.create())",
287                 "          .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())")
288             .addLinesIn(
289                 FAST_INIT_MODE,
290                 "          .put(0L, DaggerTestComponent.this.provideLong0Provider())",
291                 "          .put(1L, DaggerTestComponent.this.provideLong1Provider())",
292                 "          .put(2L, DaggerTestComponent.this.provideLong2Provider())",
293                 "          .put(3L, provideLong3Provider())",
294                 "          .put(4L, provideLong4Provider())",
295                 "          .put(5L, provideLong5Provider())")
296             .addLines( //
297                 "          .build();", "    }")
298             .addLinesIn(
299                 FAST_INIT_MODE,
300                 "    private final class SwitchingProvider<T> implements Provider<T> {",
301                 "      private final int id;",
302                 "",
303                 "      SwitchingProvider(int id) {",
304                 "        this.id = id;",
305                 "      }",
306                 "",
307                 "      @SuppressWarnings(\"unchecked\")",
308                 "      @Override",
309                 "      public T get() {",
310                 "        switch (id) {",
311                 "          case 0: return (T) (Long) SubcomponentMapModule.provideLong3();",
312                 "          case 1: return (T) (Long) SubcomponentMapModule.provideLong4();",
313                 "          case 2: return (T) (Long) SubcomponentMapModule.provideLong5();",
314                 "          default: throw new AssertionError(id);",
315                 "        }",
316                 "      }",
317                 "    }",
318                 "  }",
319                 "",
320                 "  private final class SwitchingProvider<T> implements Provider<T> {",
321                 "    private final int id;",
322                 "",
323                 "    SwitchingProvider(int id) {",
324                 "      this.id = id;",
325                 "    }",
326                 "",
327                 "    @SuppressWarnings(\"unchecked\")",
328                 "    @Override",
329                 "    public T get() {",
330                 "      switch (id) {",
331                 "        case 0: return (T) (Integer) MapModule.provideInt();",
332                 "        case 1: return (T) (Long) MapModule.provideLong0();",
333                 "        case 2: return (T) (Long) MapModule.provideLong1();",
334                 "        case 3: return (T) (Long) MapModule.provideLong2();",
335                 "        default: throw new AssertionError(id);",
336                 "      }",
337                 "    }",
338                 "  }",
339                 "}")
340             .build();
341     Compilation compilation =
342         compilerWithOptions(compilerMode.javacopts())
343             .compile(mapModuleFile, componentFile, subcomponentModuleFile, subcomponent);
344     assertThat(compilation).succeeded();
345     assertThat(compilation)
346         .generatedSourceFile("test.DaggerTestComponent")
347         .containsElementsIn(generatedComponent);
348   }
349 
350   @Test
inaccessible()351   public void inaccessible() {
352     JavaFileObject inaccessible =
353         JavaFileObjects.forSourceLines(
354             "other.Inaccessible", "package other;", "", "class Inaccessible {}");
355     JavaFileObject usesInaccessible =
356         JavaFileObjects.forSourceLines(
357             "other.UsesInaccessible",
358             "package other;",
359             "",
360             "import java.util.Map;",
361             "import javax.inject.Inject;",
362             "",
363             "public class UsesInaccessible {",
364             "  @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}",
365             "}");
366 
367     JavaFileObject module =
368         JavaFileObjects.forSourceLines(
369             "other.TestModule",
370             "package other;",
371             "",
372             "import dagger.Module;",
373             "import dagger.multibindings.Multibinds;",
374             "import java.util.Map;",
375             "",
376             "@Module",
377             "public abstract class TestModule {",
378             "  @Multibinds abstract Map<Integer, Inaccessible> ints();",
379             "}");
380     JavaFileObject componentFile =
381         JavaFileObjects.forSourceLines(
382             "test.TestComponent",
383             "package test;",
384             "",
385             "import dagger.Component;",
386             "import java.util.Map;",
387             "import javax.inject.Provider;",
388             "import other.TestModule;",
389             "import other.UsesInaccessible;",
390             "",
391             "@Component(modules = TestModule.class)",
392             "interface TestComponent {",
393             "  UsesInaccessible usesInaccessible();",
394             "}");
395     JavaFileObject generatedComponent =
396         JavaFileObjects.forSourceLines(
397             "test.DaggerTestComponent",
398             "package test;",
399             "",
400             "import other.UsesInaccessible;",
401             "import other.UsesInaccessible_Factory;",
402             "",
403             GeneratedLines.generatedAnnotations(),
404             "final class DaggerTestComponent implements TestComponent {",
405             "  @Override",
406             "  public UsesInaccessible usesInaccessible() {",
407             "    return UsesInaccessible_Factory.newInstance((Map) ImmutableMap.of());",
408             "  }",
409             "}");
410     Compilation compilation =
411         compilerWithOptions(compilerMode.javacopts())
412             .compile(module, inaccessible, usesInaccessible, componentFile);
413     assertThat(compilation).succeeded();
414     assertThat(compilation)
415         .generatedSourceFile("test.DaggerTestComponent")
416         .containsElementsIn(generatedComponent);
417   }
418 
419   @Test
subcomponentOmitsInheritedBindings()420   public void subcomponentOmitsInheritedBindings() {
421     JavaFileObject parent =
422         JavaFileObjects.forSourceLines(
423             "test.Parent",
424             "package test;",
425             "",
426             "import dagger.Component;",
427             "",
428             "@Component(modules = ParentModule.class)",
429             "interface Parent {",
430             "  Child child();",
431             "}");
432     JavaFileObject parentModule =
433         JavaFileObjects.forSourceLines(
434             "test.ParentModule",
435             "package test;",
436             "",
437             "import dagger.Module;",
438             "import dagger.Provides;",
439             "import dagger.multibindings.IntoMap;",
440             "import dagger.multibindings.StringKey;",
441             "",
442             "@Module",
443             "class ParentModule {",
444             "  @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
445             "    return \"parent value\";",
446             "  }",
447             "}");
448     JavaFileObject child =
449         JavaFileObjects.forSourceLines(
450             "test.Child",
451             "package test;",
452             "",
453             "import dagger.Subcomponent;",
454             "import java.util.Map;",
455             "",
456             "@Subcomponent",
457             "interface Child {",
458             "  Map<String, Object> objectMap();",
459             "}");
460     JavaFileObject generatedComponent =
461         JavaFileObjects.forSourceLines(
462             "test.DaggerParent",
463             "package test;",
464             "",
465             GeneratedLines.generatedAnnotations(),
466             "final class DaggerParent implements Parent {",
467             "  private final ParentModule parentModule;",
468             "",
469             "  private final class ChildImpl implements Child {",
470             "    @Override",
471             "    public Map<String, Object> objectMap() {",
472             "      return ImmutableMap.<String, Object>of(",
473             "          \"parent key\",",
474             "          ParentModule_ParentKeyObjectFactory.parentKeyObject(",
475             "              DaggerParent.this.parentModule));",
476             "    }",
477             "  }",
478             "}");
479 
480     Compilation compilation =
481         compilerWithOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
482     assertThat(compilation).succeeded();
483     assertThat(compilation)
484         .generatedSourceFile("test.DaggerParent")
485         .containsElementsIn(generatedComponent);
486   }
487 
488   @Test
productionComponents()489   public void productionComponents() {
490     JavaFileObject mapModuleFile =
491         JavaFileObjects.forSourceLines(
492             "test.MapModule",
493             "package test;",
494             "",
495             "import dagger.Module;",
496             "import dagger.multibindings.Multibinds;",
497             "import java.util.Map;",
498             "",
499             "@Module",
500             "interface MapModule {",
501             "  @Multibinds Map<String, String> stringMap();",
502             "}");
503     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
504         "package test;",
505         "",
506         "import com.google.common.util.concurrent.ListenableFuture;",
507         "import dagger.producers.ProductionComponent;",
508         "import java.util.Map;",
509         "",
510         "@ProductionComponent(modules = MapModule.class)",
511         "interface TestComponent {",
512         "  ListenableFuture<Map<String, String>> stringMap();",
513         "}");
514     JavaFileObject generatedComponent =
515         JavaFileObjects.forSourceLines(
516             "test.DaggerTestComponent",
517             "package test;",
518             "",
519             "import dagger.producers.internal.CancellationListener;",
520             "",
521             GeneratedLines.generatedAnnotations(),
522             "final class DaggerTestComponent implements TestComponent, "
523                 + "CancellationListener {",
524             "  @Override",
525             "  public ListenableFuture<Map<String, String>> stringMap() {",
526             "    return Futures.immediateFuture(",
527             "        (Map<String, String>) ImmutableMap.<String, String>of());",
528             "  }",
529             "",
530             "  @Override",
531             "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
532             "}");
533     Compilation compilation =
534         compilerWithOptions(
535                 compilerMode
536                 , CompilerMode.JAVA7
537                 )
538             .compile(mapModuleFile, componentFile);
539     assertThat(compilation).succeeded();
540     assertThat(compilation)
541         .generatedSourceFile("test.DaggerTestComponent")
542         .containsElementsIn(generatedComponent);
543   }
544 }
545