• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.bindinggraphvalidation;
18 
19 import androidx.room.compiler.processing.util.Source;
20 import com.google.common.collect.ImmutableList;
21 import dagger.internal.codegen.CompilerMode;
22 import dagger.testing.compile.CompilerTests;
23 import org.junit.Test;
24 import org.junit.runner.RunWith;
25 import org.junit.runners.Parameterized;
26 import org.junit.runners.Parameterized.Parameters;
27 
28 @RunWith(Parameterized.class)
29 public class SetMultibindingValidationTest {
30   @Parameters(name = "{0}")
parameters()31   public static ImmutableList<Object[]> parameters() {
32     return CompilerMode.TEST_PARAMETERS;
33   }
34 
35   private final CompilerMode compilerMode;
36 
SetMultibindingValidationTest(CompilerMode compilerMode)37   public SetMultibindingValidationTest(CompilerMode compilerMode) {
38     this.compilerMode = compilerMode;
39   }
40 
41   private static final Source FOO =
42         CompilerTests.javaSource(
43             "test.Foo",
44             "package test;",
45             "",
46             "public interface Foo {}");
47 
48   private static final Source FOO_IMPL =
49         CompilerTests.javaSource(
50             "test.FooImpl",
51             "package test;",
52             "",
53             "import javax.inject.Inject;",
54             "",
55             "public final class FooImpl implements Foo {",
56             "  @Inject FooImpl() {}",
57             "}");
58 
testMultipleSetBindingsToSameFoo()59   @Test public void testMultipleSetBindingsToSameFoo() {
60     Source module =
61         CompilerTests.javaSource(
62             "test.TestModule",
63             "package test;",
64             "",
65             "import dagger.Binds;",
66             "import dagger.multibindings.IntoSet;",
67             "import javax.inject.Inject;",
68             "",
69             "@dagger.Module",
70             "interface TestModule {",
71             "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
72             "",
73             "  @Binds @IntoSet Foo bindFooAgain(FooImpl impl);",
74             "}");
75     Source component =
76         CompilerTests.javaSource(
77             "test.TestComponent",
78             "package test;",
79             "",
80             "import dagger.Component;",
81             "import java.util.Set;",
82             "",
83             "@Component(modules = TestModule.class)",
84             "interface TestComponent {",
85             "  Set<Foo> setOfFoo();",
86             "}");
87     CompilerTests.daggerCompiler(FOO, FOO_IMPL, module, component)
88         .withProcessingOptions(compilerMode.processorOptions())
89         .compile(
90             subject -> {
91               subject.hasErrorCount(1);
92               subject.hasErrorContaining(
93                   "Multiple set contributions into Set<Foo> for the same contribution key: "
94                       + "FooImpl");
95             });
96   }
97 
98   // Regression test for b/316582741 to ensure the duplicate binding gets reported rather than
99   // causing a crash.
testSetBindingsToDuplicateBinding()100   @Test public void testSetBindingsToDuplicateBinding() {
101     Source module =
102         CompilerTests.javaSource(
103             "test.TestModule",
104             "package test;",
105             "",
106             "import dagger.Binds;",
107             "import dagger.Module;",
108             "import dagger.Provides;",
109             "import dagger.multibindings.IntoSet;",
110             "",
111             "@Module",
112             "interface TestModule {",
113             "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
114             "",
115             "  @Provides static FooImpl provideFooImpl() { return null; }",
116             "",
117             "  @Provides static FooImpl provideFooImplAgain() { return null; }",
118             "}");
119     Source component =
120         CompilerTests.javaSource(
121             "test.TestComponent",
122             "package test;",
123             "",
124             "import dagger.Component;",
125             "import java.util.Set;",
126             "",
127             "@Component(modules = TestModule.class)",
128             "interface TestComponent {",
129             "  Set<Foo> setOfFoo();",
130             "}");
131     CompilerTests.daggerCompiler(FOO, FOO_IMPL, module, component)
132         .withProcessingOptions(compilerMode.processorOptions())
133         .compile(
134             subject -> {
135               subject.hasErrorCount(1);
136               subject.hasErrorContaining("FooImpl is bound multiple times");
137             });
138   }
139 
testSetBindingsToMissingBinding()140   @Test public void testSetBindingsToMissingBinding() {
141     Source module =
142         CompilerTests.javaSource(
143             "test.TestModule",
144             "package test;",
145             "",
146             "import dagger.Binds;",
147             "import dagger.Module;",
148             "import dagger.multibindings.IntoSet;",
149             "",
150             "@Module",
151             "interface TestModule {",
152             "  @Binds @IntoSet Foo bindFoo(MissingFooImpl impl);",
153             "",
154             "  static class MissingFooImpl implements Foo {}",
155             "}");
156     Source component =
157         CompilerTests.javaSource(
158             "test.TestComponent",
159             "package test;",
160             "",
161             "import dagger.Component;",
162             "import java.util.Set;",
163             "",
164             "@Component(modules = TestModule.class)",
165             "interface TestComponent {",
166             "  Set<Foo> setOfFoo();",
167             "}");
168     CompilerTests.daggerCompiler(FOO, module, component)
169         .withProcessingOptions(compilerMode.processorOptions())
170         .compile(
171             subject -> {
172               subject.hasErrorCount(1);
173               subject.hasErrorContaining("MissingFooImpl cannot be provided");
174             });
175   }
176 
testMultipleSetBindingsToSameFooThroughMultipleBinds()177   @Test public void testMultipleSetBindingsToSameFooThroughMultipleBinds() {
178     Source module =
179         CompilerTests.javaSource(
180             "test.TestModule",
181             "package test;",
182             "",
183             "import dagger.Binds;",
184             "import dagger.multibindings.IntoSet;",
185             "import javax.inject.Inject;",
186             "",
187             "@dagger.Module",
188             "interface TestModule {",
189             "  @Binds @IntoSet Object bindObject(FooImpl impl);",
190             "",
191             "  @Binds @IntoSet Object bindObjectAgain(Foo impl);",
192             "",
193             "  @Binds Foo bindFoo(FooImpl impl);",
194             "}");
195     Source component =
196         CompilerTests.javaSource(
197             "test.TestComponent",
198             "package test;",
199             "",
200             "import dagger.Component;",
201             "import java.util.Set;",
202             "",
203             "@Component(modules = TestModule.class)",
204             "interface TestComponent {",
205             "  Set<Object> setOfObject();",
206             "}");
207     CompilerTests.daggerCompiler(FOO, FOO_IMPL, module, component)
208         .withProcessingOptions(compilerMode.processorOptions())
209         .compile(
210             subject -> {
211               subject.hasErrorCount(1);
212               subject.hasErrorContaining(
213                   "Multiple set contributions into Set<Object> for the same contribution key: "
214                       + "FooImpl");
215             });
216   }
217 
testMultipleSetBindingsViaElementsIntoSet()218   @Test public void testMultipleSetBindingsViaElementsIntoSet() {
219     Source module =
220         CompilerTests.javaSource(
221             "test.TestModule",
222             "package test;",
223             "",
224             "import dagger.Binds;",
225             "import dagger.Provides;",
226             "import dagger.multibindings.ElementsIntoSet;",
227             "import java.util.HashSet;",
228             "import java.util.Set;",
229             "import javax.inject.Inject;",
230             "import javax.inject.Qualifier;",
231             "",
232             "@dagger.Module",
233             "interface TestModule {",
234             "",
235             "  @Qualifier",
236             "  @interface Internal {}",
237             "",
238             "  @Provides @Internal static Set<Foo> provideSet() { return new HashSet<>(); }",
239             "",
240             "  @Binds @ElementsIntoSet Set<Foo> bindSet(@Internal Set<Foo> fooSet);",
241             "",
242             "  @Binds @ElementsIntoSet Set<Foo> bindSetAgain(@Internal Set<Foo> fooSet);",
243             "}");
244     Source component =
245         CompilerTests.javaSource(
246             "test.TestComponent",
247             "package test;",
248             "",
249             "import dagger.Component;",
250             "import java.util.Set;",
251             "",
252             "@Component(modules = TestModule.class)",
253             "interface TestComponent {",
254             "  Set<Foo> setOfFoo();",
255             "}");
256     CompilerTests.daggerCompiler(FOO, module, component)
257         .withProcessingOptions(compilerMode.processorOptions())
258         .compile(
259             subject -> {
260               subject.hasErrorCount(1);
261               subject.hasErrorContaining(
262                   "Multiple set contributions into Set<Foo> for the same contribution key: "
263                       + "@TestModule.Internal Set<Foo>");
264             });
265   }
266 
testMultipleSetBindingsToSameFooSubcomponents()267   @Test public void testMultipleSetBindingsToSameFooSubcomponents() {
268     Source parentModule =
269         CompilerTests.javaSource(
270             "test.ParentModule",
271             "package test;",
272             "",
273             "import dagger.Binds;",
274             "import dagger.multibindings.IntoSet;",
275             "import javax.inject.Inject;",
276             "",
277             "@dagger.Module",
278             "interface ParentModule {",
279             "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
280             "}");
281     Source childModule =
282         CompilerTests.javaSource(
283             "test.ChildModule",
284             "package test;",
285             "",
286             "import dagger.Binds;",
287             "import dagger.multibindings.IntoSet;",
288             "import javax.inject.Inject;",
289             "",
290             "@dagger.Module",
291             "interface ChildModule {",
292             "  @Binds @IntoSet Foo bindFoo(FooImpl impl);",
293             "}");
294     Source parentComponent =
295         CompilerTests.javaSource(
296             "test.ParentComponent",
297             "package test;",
298             "",
299             "import dagger.Component;",
300             "import java.util.Set;",
301             "",
302             "@Component(modules = ParentModule.class)",
303             "interface ParentComponent {",
304             "  Set<Foo> setOfFoo();",
305             "  ChildComponent child();",
306             "}");
307     Source childComponent =
308         CompilerTests.javaSource(
309             "test.ChildComponent",
310             "package test;",
311             "",
312             "import dagger.Subcomponent;",
313             "import java.util.Set;",
314             "",
315             "@Subcomponent(modules = ChildModule.class)",
316             "interface ChildComponent {",
317             "  Set<Foo> setOfFoo();",
318             "}");
319     CompilerTests.daggerCompiler(
320             FOO, FOO_IMPL, parentModule, childModule, parentComponent, childComponent)
321         .withProcessingOptions(compilerMode.processorOptions())
322         .compile(
323             subject -> {
324               subject.hasErrorCount(1);
325               subject.hasErrorContaining(
326                   "Multiple set contributions into Set<Foo> for the same contribution key: "
327                       + "FooImpl");
328               subject.hasErrorContaining("ParentComponent → ChildComponent");
329             });
330   }
331 
testMultipleSetBindingsToSameKeyButDifferentBindings()332   @Test public void testMultipleSetBindingsToSameKeyButDifferentBindings() {
333     // Use an impl with local multibindings to create different bindings. We still want this to fail
334     // even though there are separate bindings because it is likely an unintentional error anyway.
335     Source fooImplWithMult =
336         CompilerTests.javaSource(
337             "test.FooImplWithMult",
338             "package test;",
339             "",
340             "import java.util.Set;",
341             "import javax.inject.Inject;",
342             "",
343             "public final class FooImplWithMult implements Foo {",
344             "  @Inject FooImplWithMult(Set<Long> longSet) {}",
345             "}");
346     // Scoping the @Binds is necessary to ensure it goes to different bindings
347     Source parentModule =
348         CompilerTests.javaSource(
349             "test.ParentModule",
350             "package test;",
351             "",
352             "import dagger.Binds;",
353             "import dagger.Provides;",
354             "import dagger.multibindings.IntoSet;",
355             "import javax.inject.Inject;",
356             "import javax.inject.Singleton;",
357             "",
358             "@dagger.Module",
359             "interface ParentModule {",
360             "  @Singleton",
361             "  @Binds @IntoSet Foo bindFoo(FooImplWithMult impl);",
362             "",
363             "  @Provides @IntoSet static Long provideLong() {",
364             "    return 0L;",
365             "  }",
366             "}");
367     Source childModule =
368         CompilerTests.javaSource(
369             "test.ChildModule",
370             "package test;",
371             "",
372             "import dagger.Binds;",
373             "import dagger.Provides;",
374             "import dagger.multibindings.IntoSet;",
375             "import javax.inject.Inject;",
376             "",
377             "@dagger.Module",
378             "interface ChildModule {",
379             "  @Binds @IntoSet Foo bindFoo(FooImplWithMult impl);",
380             "",
381             "  @Provides @IntoSet static Long provideLong() {",
382             "    return 1L;",
383             "  }",
384             "}");
385     Source parentComponent =
386         CompilerTests.javaSource(
387             "test.ParentComponent",
388             "package test;",
389             "",
390             "import dagger.Component;",
391             "import java.util.Set;",
392             "import javax.inject.Singleton;",
393             "",
394             "@Singleton",
395             "@Component(modules = ParentModule.class)",
396             "interface ParentComponent {",
397             "  Set<Foo> setOfFoo();",
398             "  ChildComponent child();",
399             "}");
400     Source childComponent =
401         CompilerTests.javaSource(
402             "test.ChildComponent",
403             "package test;",
404             "",
405             "import dagger.Subcomponent;",
406             "import java.util.Set;",
407             "",
408             "@Subcomponent(modules = ChildModule.class)",
409             "interface ChildComponent {",
410             "  Set<Foo> setOfFoo();",
411             "}");
412     CompilerTests.daggerCompiler(
413             FOO, fooImplWithMult, parentModule, childModule, parentComponent, childComponent)
414         .withProcessingOptions(compilerMode.processorOptions())
415         .compile(
416             subject -> {
417               subject.hasErrorCount(1);
418               subject.hasErrorContaining(
419                   "Multiple set contributions into Set<Foo> for the same contribution key: "
420                       + "FooImplWithMult");
421               subject.hasErrorContaining("ParentComponent → ChildComponent");
422             });
423   }
424 }
425