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