• 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.CLASS_PATH_WITHOUT_GUAVA_OPTION;
23 import static dagger.internal.codegen.Compilers.compilerWithOptions;
24 
25 import com.google.testing.compile.Compilation;
26 import com.google.testing.compile.Compiler;
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 MapBindingExpressionTest {
37   @Parameters(name = "{0}")
parameters()38   public static Collection<Object[]> parameters() {
39     return CompilerMode.TEST_PARAMETERS;
40   }
41 
42   private final CompilerMode compilerMode;
43 
MapBindingExpressionTest(CompilerMode compilerMode)44   public MapBindingExpressionTest(CompilerMode compilerMode) {
45     this.compilerMode = compilerMode;
46   }
47 
48   @Test
mapBindings()49   public void mapBindings() {
50     JavaFileObject mapModuleFile = JavaFileObjects.forSourceLines("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 componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
70         "package test;",
71         "",
72         "import dagger.Component;",
73         "import java.util.Map;",
74         "import javax.inject.Provider;",
75         "",
76         "@Component(modules = MapModule.class)",
77         "interface TestComponent {",
78         "  Map<String, String> strings();",
79         "  Map<String, Provider<String>> providerStrings();",
80         "",
81         "  Map<Integer, Integer> ints();",
82         "  Map<Integer, Provider<Integer>> providerInts();",
83         "  Map<Long, Long> longs();",
84         "  Map<Long, Provider<Long>> providerLongs();",
85         "}");
86     JavaFileObject generatedComponent =
87         compilerMode
88             .javaFileBuilder("test.DaggerTestComponent")
89             .addLines(
90                 "package test;",
91                 "",
92                 "import dagger.internal.MapBuilder;",
93                 "",
94                 GeneratedLines.generatedAnnotations(),
95                 "final class DaggerTestComponent implements TestComponent {")
96             .addLinesIn(
97                 FAST_INIT_MODE,
98                 "  private volatile Provider<Integer> provideIntProvider;",
99                 "  private volatile Provider<Long> provideLong0Provider;",
100                 "  private volatile Provider<Long> provideLong1Provider;",
101                 "  private volatile Provider<Long> provideLong2Provider;",
102                 "",
103                 "  private Provider<Integer> provideIntProvider() {",
104                 "    Object local = provideIntProvider;",
105                 "    if (local == null) {",
106                 "      local = new SwitchingProvider<>(0);",
107                 "      provideIntProvider = (Provider<Integer>) local;",
108                 "    }",
109                 "    return (Provider<Integer>) local;",
110                 "  }",
111                 "",
112                 "  private Provider<Long> provideLong0Provider() {",
113                 "    Object local = provideLong0Provider;",
114                 "    if (local == null) {",
115                 "      local = new SwitchingProvider<>(1);",
116                 "      provideLong0Provider = (Provider<Long>) local;",
117                 "    }",
118                 "    return (Provider<Long>) local;",
119                 "  }",
120                 "",
121                 "  private Provider<Long> provideLong1Provider() {",
122                 "    Object local = provideLong1Provider;",
123                 "    if (local == null) {",
124                 "      local = new SwitchingProvider<>(2);",
125                 "      provideLong1Provider = (Provider<Long>) local;",
126                 "    }",
127                 "    return (Provider<Long>) local;",
128                 "  }",
129                 "",
130                 "  private Provider<Long> provideLong2Provider() {",
131                 "    Object local = provideLong2Provider;",
132                 "    if (local == null) {",
133                 "      local = new SwitchingProvider<>(3);",
134                 "      provideLong2Provider = (Provider<Long>) local;",
135                 "    }",
136                 "    return (Provider<Long>) local;",
137                 "  }")
138             .addLines(
139                 "  @Override",
140                 "  public Map<String, String> strings() {",
141                 "    return Collections.<String, String>emptyMap();",
142                 "  }",
143                 "",
144                 "  @Override",
145                 "  public Map<String, Provider<String>> providerStrings() {",
146                 "    return Collections.<String, Provider<String>>emptyMap();",
147                 "  }",
148                 "",
149                 "  @Override",
150                 "  public Map<Integer, Integer> ints() {",
151                 "    return Collections.<Integer, Integer>singletonMap(0, MapModule.provideInt());",
152                 "  }",
153                 "",
154                 "  @Override",
155                 "  public Map<Integer, Provider<Integer>> providerInts() {",
156                 "    return Collections.<Integer, Provider<Integer>>singletonMap(")
157             .addLinesIn(
158                 DEFAULT_MODE, //
159                 "        0, MapModule_ProvideIntFactory.create());")
160             .addLinesIn(
161                 FAST_INIT_MODE,
162                 "        0, provideIntProvider());")
163             .addLines(
164                 "  }",
165                 "",
166                 "  @Override",
167                 "  public Map<Long, Long> longs() {",
168                 "    return MapBuilder.<Long, Long>newMapBuilder(3)",
169                 "        .put(0L, MapModule.provideLong0())",
170                 "        .put(1L, MapModule.provideLong1())",
171                 "        .put(2L, MapModule.provideLong2())",
172                 "        .build();",
173                 "  }",
174                 "",
175                 "  @Override",
176                 "  public Map<Long, Provider<Long>> providerLongs() {",
177                 "    return MapBuilder.<Long, Provider<Long>>newMapBuilder(3)")
178             .addLinesIn(
179                 DEFAULT_MODE,
180                 "        .put(0L, MapModule_ProvideLong0Factory.create())",
181                 "        .put(1L, MapModule_ProvideLong1Factory.create())",
182                 "        .put(2L, MapModule_ProvideLong2Factory.create())")
183             .addLinesIn(
184                 FAST_INIT_MODE,
185                 "        .put(0L, provideLong0Provider())",
186                 "        .put(1L, provideLong1Provider())",
187                 "        .put(2L, provideLong2Provider())")
188             .addLines( //
189                 "        .build();", "  }")
190             .addLinesIn(
191                 FAST_INIT_MODE,
192                 "  private final class SwitchingProvider<T> implements Provider<T> {",
193                 "    private final int id;",
194                 "",
195                 "    SwitchingProvider(int id) {",
196                 "      this.id = id;",
197                 "    }",
198                 "",
199                 "    @SuppressWarnings(\"unchecked\")",
200                 "    @Override",
201                 "    public T get() {",
202                 "      switch (id) {",
203                 "        case 0: return (T) (Integer) MapModule.provideInt();",
204                 "        case 1: return (T) (Long) MapModule.provideLong0();",
205                 "        case 2: return (T) (Long) MapModule.provideLong1();",
206                 "        case 3: return (T) (Long) MapModule.provideLong2();",
207                 "        default: throw new AssertionError(id);",
208                 "      }",
209                 "    }",
210                 "  }",
211                 "}")
212             .build();
213     Compilation compilation = daggerCompilerWithoutGuava().compile(mapModuleFile, componentFile);
214     assertThat(compilation).succeeded();
215     assertThat(compilation)
216         .generatedSourceFile("test.DaggerTestComponent")
217         .containsElementsIn(generatedComponent);
218   }
219 
220   @Test
inaccessible()221   public void inaccessible() {
222     JavaFileObject inaccessible =
223         JavaFileObjects.forSourceLines(
224             "other.Inaccessible",
225             "package other;",
226             "",
227             "class Inaccessible {}");
228     JavaFileObject usesInaccessible =
229         JavaFileObjects.forSourceLines(
230             "other.UsesInaccessible",
231             "package other;",
232             "",
233             "import java.util.Map;",
234             "import javax.inject.Inject;",
235             "",
236             "public class UsesInaccessible {",
237             "  @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}",
238             "}");
239 
240     JavaFileObject module =
241         JavaFileObjects.forSourceLines(
242             "other.TestModule",
243             "package other;",
244             "",
245             "import dagger.Module;",
246             "import dagger.multibindings.Multibinds;",
247             "import java.util.Map;",
248             "",
249             "@Module",
250             "public abstract class TestModule {",
251             "  @Multibinds abstract Map<Integer, Inaccessible> ints();",
252             "}");
253     JavaFileObject componentFile =
254         JavaFileObjects.forSourceLines(
255             "test.TestComponent",
256             "package test;",
257             "",
258             "import dagger.Component;",
259             "import java.util.Map;",
260             "import javax.inject.Provider;",
261             "import other.TestModule;",
262             "import other.UsesInaccessible;",
263             "",
264             "@Component(modules = TestModule.class)",
265             "interface TestComponent {",
266             "  UsesInaccessible usesInaccessible();",
267             "}");
268     JavaFileObject generatedComponent =
269         JavaFileObjects.forSourceLines(
270             "test.DaggerTestComponent",
271             "package test;",
272             "",
273             "import other.UsesInaccessible;",
274             "import other.UsesInaccessible_Factory;",
275             "",
276             GeneratedLines.generatedAnnotations(),
277             "final class DaggerTestComponent implements TestComponent {",
278             "  @Override",
279             "  public UsesInaccessible usesInaccessible() {",
280             "    return UsesInaccessible_Factory.newInstance(",
281             "        (Map) Collections.emptyMap());",
282             "  }",
283             "}");
284     Compilation compilation =
285         daggerCompilerWithoutGuava().compile(module, inaccessible, usesInaccessible, componentFile);
286     assertThat(compilation).succeeded();
287     assertThat(compilation)
288         .generatedSourceFile("test.DaggerTestComponent")
289         .containsElementsIn(generatedComponent);
290   }
291 
292   @Test
subcomponentOmitsInheritedBindings()293   public void subcomponentOmitsInheritedBindings() {
294     JavaFileObject parent =
295         JavaFileObjects.forSourceLines(
296             "test.Parent",
297             "package test;",
298             "",
299             "import dagger.Component;",
300             "",
301             "@Component(modules = ParentModule.class)",
302             "interface Parent {",
303             "  Child child();",
304             "}");
305     JavaFileObject parentModule =
306         JavaFileObjects.forSourceLines(
307             "test.ParentModule",
308             "package test;",
309             "",
310             "import dagger.Module;",
311             "import dagger.Provides;",
312             "import dagger.multibindings.IntoMap;",
313             "import dagger.multibindings.StringKey;",
314             "",
315             "@Module",
316             "class ParentModule {",
317             "  @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {",
318             "    return \"parent value\";",
319             "  }",
320             "}");
321     JavaFileObject child =
322         JavaFileObjects.forSourceLines(
323             "test.Child",
324             "package test;",
325             "",
326             "import dagger.Subcomponent;",
327             "import java.util.Map;",
328             "import java.util.Map;",
329             "",
330             "@Subcomponent",
331             "interface Child {",
332             "  Map<String, Object> objectMap();",
333             "}");
334     JavaFileObject generatedComponent =
335         JavaFileObjects.forSourceLines(
336             "test.DaggerParent",
337             "package test;",
338             "",
339             GeneratedLines.generatedAnnotations(),
340             "final class DaggerParent implements Parent {",
341             "  private final ParentModule parentModule;",
342             "",
343             "  private final class ChildImpl implements Child {",
344             "    @Override",
345             "    public Map<String, Object> objectMap() {",
346             "      return Collections.<String, Object>singletonMap(",
347             "          \"parent key\",",
348             "          ParentModule_ParentKeyObjectFactory.parentKeyObject(",
349             "              DaggerParent.this.parentModule));",
350             "    }",
351             "  }",
352             "}");
353 
354     Compilation compilation = daggerCompilerWithoutGuava().compile(parent, parentModule, child);
355     assertThat(compilation).succeeded();
356     assertThat(compilation)
357         .generatedSourceFile("test.DaggerParent")
358         .containsElementsIn(generatedComponent);
359   }
360 
daggerCompilerWithoutGuava()361   private Compiler daggerCompilerWithoutGuava() {
362     return compilerWithOptions(compilerMode.javacopts())
363         .withClasspath(CLASS_PATH_WITHOUT_GUAVA_OPTION);
364   }
365 }
366