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.Compilers.compilerWithOptions;
21 import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
22 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
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 SetBindingRequestFulfillmentWithGuavaTest {
35   @Parameters(name = "{0}")
parameters()36   public static Collection<Object[]> parameters() {
37     return CompilerMode.TEST_PARAMETERS;
38   }
39 
40   private final CompilerMode compilerMode;
41 
SetBindingRequestFulfillmentWithGuavaTest(CompilerMode compilerMode)42   public SetBindingRequestFulfillmentWithGuavaTest(CompilerMode compilerMode) {
43     this.compilerMode = compilerMode;
44   }
45 
46   @Test
setBindings()47   public void setBindings() {
48     JavaFileObject emptySetModuleFile = JavaFileObjects.forSourceLines("test.EmptySetModule",
49         "package test;",
50         "",
51         "import dagger.Module;",
52         "import dagger.Provides;",
53         "import dagger.multibindings.ElementsIntoSet;",
54         "import dagger.multibindings.Multibinds;",
55         "import java.util.Collections;",
56         "import java.util.Set;",
57         "",
58         "@Module",
59         "abstract class EmptySetModule {",
60         "  @Multibinds abstract Set<Object> objects();",
61         "",
62         "  @Provides @ElementsIntoSet",
63         "  static Set<String> emptySet() { ",
64         "    return Collections.emptySet();",
65         "  }",
66         "  @Provides @ElementsIntoSet",
67         "  static Set<Integer> onlyContributionIsElementsIntoSet() { ",
68         "    return Collections.emptySet();",
69         "  }",
70         "}");
71     JavaFileObject setModuleFile = JavaFileObjects.forSourceLines("test.SetModule",
72         "package test;",
73         "",
74         "import dagger.Module;",
75         "import dagger.Provides;",
76         "import dagger.multibindings.IntoSet;",
77         "",
78         "@Module",
79         "final class SetModule {",
80         "  @Provides @IntoSet static String string() { return \"\"; }",
81         "}");
82     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
83         "package test;",
84         "",
85         "import dagger.Component;",
86         "import java.util.Set;",
87         "import javax.inject.Provider;",
88         "",
89         "@Component(modules = {EmptySetModule.class, SetModule.class})",
90         "interface TestComponent {",
91         "  Set<String> strings();",
92         "  Set<Object> objects();",
93         "  Set<Integer> onlyContributionIsElementsIntoSet();",
94         "}");
95     JavaFileObject generatedComponent =
96         JavaFileObjects.forSourceLines(
97             "test.DaggerTestComponent",
98             "package test;",
99             "",
100             "import com.google.common.collect.ImmutableSet;",
101             "",
102             GENERATED_CODE_ANNOTATIONS,
103             "final class DaggerTestComponent implements TestComponent {",
104             "  @Override",
105             "  public Set<String> strings() {",
106             "    return ImmutableSet.<String>builderWithExpectedSize(2)",
107             "        .addAll(EmptySetModule_EmptySetFactory.emptySet())",
108             "        .add(SetModule_StringFactory.string())",
109             "        .build();",
110             "  }",
111             "",
112             "  @Override",
113             "  public Set<Object> objects() {",
114             "    return ImmutableSet.<Object>of();",
115             "  }",
116             "",
117             "  @Override",
118             "  public Set<Integer> onlyContributionIsElementsIntoSet() {",
119             "    return ImmutableSet.<Integer>copyOf(",
120             "        EmptySetModule_OnlyContributionIsElementsIntoSetFactory",
121             "            .onlyContributionIsElementsIntoSet());",
122             "  }",
123             "}");
124     Compilation compilation =
125         compilerWithOptions(compilerMode.javacopts())
126             .compile(emptySetModuleFile, setModuleFile, componentFile);
127     assertThat(compilation).succeeded();
128     assertThat(compilation)
129         .generatedSourceFile("test.DaggerTestComponent")
130         .containsElementsIn(generatedComponent);
131   }
132 
133   @Test
inaccessible()134   public void inaccessible() {
135     JavaFileObject inaccessible =
136         JavaFileObjects.forSourceLines(
137             "other.Inaccessible",
138             "package other;",
139             "",
140             "class Inaccessible {}");
141     JavaFileObject inaccessible2 =
142         JavaFileObjects.forSourceLines(
143             "other.Inaccessible2",
144             "package other;",
145             "",
146             "class Inaccessible2 {}");
147     JavaFileObject usesInaccessible =
148         JavaFileObjects.forSourceLines(
149             "other.UsesInaccessible",
150             "package other;",
151             "",
152             "import java.util.Set;",
153             "import javax.inject.Inject;",
154             "",
155             "public class UsesInaccessible {",
156             "  @Inject UsesInaccessible(Set<Inaccessible> set1, Set<Inaccessible2> set2) {}",
157             "}");
158 
159     JavaFileObject module =
160         JavaFileObjects.forSourceLines(
161             "other.TestModule",
162             "package other;",
163             "",
164             "import dagger.Module;",
165             "import dagger.Provides;",
166             "import dagger.multibindings.ElementsIntoSet;",
167             "import dagger.multibindings.Multibinds;",
168             "import java.util.Collections;",
169             "import java.util.Set;",
170             "",
171             "@Module",
172             "public abstract class TestModule {",
173             "  @Multibinds abstract Set<Inaccessible> objects();",
174             "",
175             "  @Provides @ElementsIntoSet",
176             "  static Set<Inaccessible2> emptySet() { ",
177             "    return Collections.emptySet();",
178             "  }",
179             "}");
180     JavaFileObject componentFile =
181         JavaFileObjects.forSourceLines(
182             "test.TestComponent",
183             "package test;",
184             "",
185             "import dagger.Component;",
186             "import java.util.Set;",
187             "import javax.inject.Provider;",
188             "import other.TestModule;",
189             "import other.UsesInaccessible;",
190             "",
191             "@Component(modules = TestModule.class)",
192             "interface TestComponent {",
193             "  UsesInaccessible usesInaccessible();",
194             "}");
195     JavaFileObject generatedComponent =
196         JavaFileObjects.forSourceLines(
197             "test.DaggerTestComponent",
198             "package test;",
199             "",
200             "import com.google.common.collect.ImmutableSet;",
201             "import other.TestModule_EmptySetFactory;",
202             "import other.UsesInaccessible;",
203             "import other.UsesInaccessible_Factory;",
204             "",
205             GENERATED_CODE_ANNOTATIONS,
206             "final class DaggerTestComponent implements TestComponent {",
207             "  private Set setOfInaccessible2() {",
208             "    return ImmutableSet.copyOf(TestModule_EmptySetFactory.emptySet());",
209             "  }",
210             "",
211             "  @Override",
212             "  public UsesInaccessible usesInaccessible() {",
213             "    return UsesInaccessible_Factory.newInstance(",
214             "        (Set) ImmutableSet.of(),",
215             "        (Set) setOfInaccessible2());",
216             "  }",
217             "}");
218     Compilation compilation =
219         compilerWithOptions(compilerMode.javacopts())
220             .compile(module, inaccessible, inaccessible2, usesInaccessible, componentFile);
221     assertThat(compilation).succeeded();
222     assertThat(compilation)
223         .generatedSourceFile("test.DaggerTestComponent")
224         .containsElementsIn(generatedComponent);
225   }
226 
227   @Test
subcomponentOmitsInheritedBindings()228   public void subcomponentOmitsInheritedBindings() {
229     JavaFileObject parent =
230         JavaFileObjects.forSourceLines(
231             "test.Parent",
232             "package test;",
233             "",
234             "import dagger.Component;",
235             "",
236             "@Component(modules = ParentModule.class)",
237             "interface Parent {",
238             "  Child child();",
239             "}");
240     JavaFileObject parentModule =
241         JavaFileObjects.forSourceLines(
242             "test.ParentModule",
243             "package test;",
244             "",
245             "import dagger.Module;",
246             "import dagger.Provides;",
247             "import dagger.multibindings.IntoSet;",
248             "import dagger.multibindings.StringKey;",
249             "",
250             "@Module",
251             "class ParentModule {",
252             "  @Provides @IntoSet static Object parentObject() {",
253             "    return \"parent object\";",
254             "  }",
255             "}");
256     JavaFileObject child =
257         JavaFileObjects.forSourceLines(
258             "test.Child",
259             "package test;",
260             "",
261             "import dagger.Subcomponent;",
262             "import java.util.Set;",
263             "",
264             "@Subcomponent",
265             "interface Child {",
266             "  Set<Object> objectSet();",
267             "}");
268     JavaFileObject generatedComponent =
269         JavaFileObjects.forSourceLines(
270             "test.DaggerParent",
271             "package test;",
272             "",
273             "import com.google.common.collect.ImmutableSet;",
274             "",
275             GENERATED_CODE_ANNOTATIONS,
276             "final class DaggerParent implements Parent {",
277             "  private final class ChildImpl implements Child {",
278             "    @Override",
279             "    public Set<Object> objectSet() {",
280             "      return ImmutableSet.<Object>of(",
281             "          ParentModule_ParentObjectFactory.parentObject());",
282             "    }",
283             "  }",
284             "}");
285     Compilation compilation =
286         compilerWithOptions(compilerMode.javacopts()).compile(parent, parentModule, child);
287     assertThat(compilation).succeeded();
288     assertThat(compilation)
289         .generatedSourceFile("test.DaggerParent")
290         .containsElementsIn(generatedComponent);
291   }
292 
293   @Test
productionComponents()294   public void productionComponents() {
295     JavaFileObject emptySetModuleFile = JavaFileObjects.forSourceLines("test.EmptySetModule",
296         "package test;",
297         "",
298         "import dagger.Module;",
299         "import dagger.Provides;",
300         "import dagger.multibindings.ElementsIntoSet;",
301         "import java.util.Collections;",
302         "import java.util.Set;",
303         "",
304         "@Module",
305         "abstract class EmptySetModule {",
306         "  @Provides @ElementsIntoSet",
307         "  static Set<String> emptySet() { ",
308         "    return Collections.emptySet();",
309         "  }",
310         "}");
311     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
312         "package test;",
313         "",
314         "import com.google.common.util.concurrent.ListenableFuture;",
315         "import dagger.producers.ProductionComponent;",
316         "import java.util.Set;",
317         "",
318         "@ProductionComponent(modules = EmptySetModule.class)",
319         "interface TestComponent {",
320         "  ListenableFuture<Set<String>> strings();",
321         "}");
322     JavaFileObject generatedComponent =
323         JavaFileObjects.forSourceLines(
324             "test.DaggerTestComponent",
325             "package test;",
326             "",
327             "import com.google.common.collect.ImmutableSet;",
328             "import com.google.common.util.concurrent.Futures;",
329             "import com.google.common.util.concurrent.ListenableFuture;",
330             "import dagger.producers.internal.CancellationListener;",
331             "import java.util.Set;",
332             IMPORT_GENERATED_ANNOTATION,
333             "",
334             GENERATED_CODE_ANNOTATIONS,
335             "final class DaggerTestComponent implements TestComponent, "
336                 + "CancellationListener {",
337             "  private DaggerTestComponent() {}",
338             "",
339             "  public static Builder builder() {",
340             "    return new Builder();",
341             "  }",
342             "",
343             "  public static TestComponent create() {",
344             "    return new Builder().build();",
345             "  }",
346             "",
347             "  private Set<String> setOfString() {",
348             "    return ImmutableSet.<String>copyOf(",
349             "        EmptySetModule_EmptySetFactory.emptySet());",
350             "  }",
351             "",
352             "  @Override",
353             "  public ListenableFuture<Set<String>> strings() {",
354             "    return Futures.immediateFuture(setOfString());",
355             "  }",
356             "",
357             "  @Override",
358             "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
359             "",
360             "  static final class Builder {",
361             "    private Builder() {}",
362             "",
363             "    public TestComponent build() {",
364             "      return new DaggerTestComponent();",
365             "    }",
366             "  }",
367             "}");
368 
369     Compilation compilation =
370         compilerWithOptions(compilerMode.javacopts())
371             .compile(emptySetModuleFile, componentFile);
372     assertThat(compilation).succeeded();
373     assertThat(compilation)
374         .generatedSourceFile("test.DaggerTestComponent")
375         .hasSourceEquivalentTo(generatedComponent);
376   }
377 }
378