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