• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 static dagger.internal.codegen.bindinggraphvalidation.NullableBindingValidator.nullableToNonNullable;
20 
21 import androidx.room.compiler.processing.util.Source;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ImmutableMap;
24 import dagger.internal.codegen.CompilerMode;
25 import dagger.testing.compile.CompilerTests;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.junit.runners.Parameterized;
29 import org.junit.runners.Parameterized.Parameters;
30 
31 @RunWith(Parameterized.class)
32 public class NullableBindingValidationTest {
33   @Parameters(name = "{0}")
parameters()34   public static ImmutableList<Object[]> parameters() {
35     return CompilerMode.TEST_PARAMETERS;
36   }
37 
38   private final CompilerMode compilerMode;
39 
NullableBindingValidationTest(CompilerMode compilerMode)40   public NullableBindingValidationTest(CompilerMode compilerMode) {
41     this.compilerMode = compilerMode;
42   }
43 
44   private static final Source NULLABLE =
45         CompilerTests.javaSource(
46             "test.Nullable", // force one-string-per-line format
47             "package test;",
48             "",
49             "public @interface Nullable {}");
50 
nullCheckForConstructorParameters()51   @Test public void nullCheckForConstructorParameters() {
52     Source a =
53         CompilerTests.javaSource(
54             "test.A",
55             "package test;",
56             "",
57             "import javax.inject.Inject;",
58             "",
59             "final class A {",
60             "  @Inject A(String string) {}",
61             "}");
62     Source module =
63         CompilerTests.javaSource(
64             "test.TestModule",
65             "package test;",
66             "",
67             "import dagger.Provides;",
68             "import javax.inject.Inject;",
69             "",
70             "@dagger.Module",
71             "final class TestModule {",
72             "  @Nullable @Provides String provideString() { return null; }",
73             "}");
74     Source component =
75         CompilerTests.javaSource(
76             "test.TestComponent",
77             "package test;",
78             "",
79             "import dagger.Component;",
80             "",
81             "@Component(modules = TestModule.class)",
82             "interface TestComponent {",
83             "  A a();",
84             "}");
85     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
86         .withProcessingOptions(compilerMode.processorOptions())
87         .compile(
88             subject -> {
89               subject.hasErrorCount(1);
90               subject.hasErrorContaining(
91                   nullableToNonNullable(
92                       "String",
93                       "@Nullable @Provides String TestModule.provideString()"));
94             });
95 
96     // but if we disable the validation, then it compiles fine.
97     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
98         .withProcessingOptions(
99             ImmutableMap.<String, String>builder()
100                 .putAll(compilerMode.processorOptions())
101                 .put("dagger.nullableValidation", "WARNING")
102                 .buildOrThrow())
103         .compile(subject -> subject.hasErrorCount(0));
104   }
105 
nullCheckForMembersInjectParam()106   @Test public void nullCheckForMembersInjectParam() {
107     Source a =
108         CompilerTests.javaSource(
109             "test.A",
110             "package test;",
111             "",
112             "import javax.inject.Inject;",
113             "",
114             "final class A {",
115             "  @Inject A() {}",
116             "  @Inject void register(String string) {}",
117             "}");
118     Source module =
119         CompilerTests.javaSource(
120             "test.TestModule",
121             "package test;",
122             "",
123             "import dagger.Provides;",
124             "import javax.inject.Inject;",
125             "",
126             "@dagger.Module",
127             "final class TestModule {",
128             "  @Nullable @Provides String provideString() { return null; }",
129             "}");
130     Source component =
131         CompilerTests.javaSource(
132             "test.TestComponent",
133             "package test;",
134             "",
135             "import dagger.Component;",
136             "",
137             "@Component(modules = TestModule.class)",
138             "interface TestComponent {",
139             "  A a();",
140             "}");
141     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
142         .withProcessingOptions(compilerMode.processorOptions())
143         .compile(
144             subject -> {
145               subject.hasErrorCount(1);
146               subject.hasErrorContaining(
147                   nullableToNonNullable(
148                       "String",
149                       "@Nullable @Provides String TestModule.provideString()"));
150             });
151 
152     // but if we disable the validation, then it compiles fine.
153     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
154         .withProcessingOptions(
155             ImmutableMap.<String, String>builder()
156                 .putAll(compilerMode.processorOptions())
157                 .put("dagger.nullableValidation", "WARNING")
158                 .buildOrThrow())
159         .compile(subject -> subject.hasErrorCount(0));
160   }
161 
nullCheckForVariable()162   @Test public void nullCheckForVariable() {
163     Source a =
164         CompilerTests.javaSource(
165             "test.A",
166             "package test;",
167             "",
168             "import javax.inject.Inject;",
169             "",
170             "final class A {",
171             "  @Inject String string;",
172             "  @Inject A() {}",
173             "}");
174     Source module =
175         CompilerTests.javaSource(
176             "test.TestModule",
177             "package test;",
178             "",
179             "import dagger.Provides;",
180             "import javax.inject.Inject;",
181             "",
182             "@dagger.Module",
183             "final class TestModule {",
184             "  @Nullable @Provides String provideString() { return null; }",
185             "}");
186     Source component =
187         CompilerTests.javaSource(
188             "test.TestComponent",
189             "package test;",
190             "",
191             "import dagger.Component;",
192             "",
193             "@Component(modules = TestModule.class)",
194             "interface TestComponent {",
195             "  A a();",
196             "}");
197     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
198         .withProcessingOptions(compilerMode.processorOptions())
199         .compile(
200             subject -> {
201               subject.hasErrorCount(1);
202               subject.hasErrorContaining(
203                   nullableToNonNullable(
204                       "String",
205                       "@Nullable @Provides String TestModule.provideString()"));
206             });
207 
208     // but if we disable the validation, then it compiles fine.
209     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
210         .withProcessingOptions(
211             ImmutableMap.<String, String>builder()
212                 .putAll(compilerMode.processorOptions())
213                 .put("dagger.nullableValidation", "WARNING")
214                 .buildOrThrow())
215         .compile(subject -> subject.hasErrorCount(0));
216   }
217 
nullCheckForComponentReturn()218   @Test public void nullCheckForComponentReturn() {
219     Source module =
220         CompilerTests.javaSource(
221             "test.TestModule",
222             "package test;",
223             "",
224             "import dagger.Provides;",
225             "import javax.inject.Inject;",
226             "",
227             "@dagger.Module",
228             "final class TestModule {",
229             "  @Nullable @Provides String provideString() { return null; }",
230             "}");
231     Source component =
232         CompilerTests.javaSource(
233             "test.TestComponent",
234             "package test;",
235             "",
236             "import dagger.Component;",
237             "",
238             "@Component(modules = TestModule.class)",
239             "interface TestComponent {",
240             "  String string();",
241             "}");
242     CompilerTests.daggerCompiler(NULLABLE, module, component)
243         .withProcessingOptions(compilerMode.processorOptions())
244         .compile(
245             subject -> {
246               subject.hasErrorCount(1);
247               subject.hasErrorContaining(
248                   nullableToNonNullable(
249                       "String",
250                       "@Nullable @Provides String TestModule.provideString()"));
251             });
252 
253     // but if we disable the validation, then it compiles fine.
254     CompilerTests.daggerCompiler(NULLABLE, module, component)
255         .withProcessingOptions(
256             ImmutableMap.<String, String>builder()
257                 .putAll(compilerMode.processorOptions())
258                 .put("dagger.nullableValidation", "WARNING")
259                 .buildOrThrow())
260         .compile(subject -> subject.hasErrorCount(0));
261   }
262 
263   @Test
nullCheckForOptionalInstance()264   public void nullCheckForOptionalInstance() {
265     Source a =
266         CompilerTests.javaSource(
267             "test.A",
268             "package test;",
269             "",
270             "import com.google.common.base.Optional;",
271             "import javax.inject.Inject;",
272             "",
273             "final class A {",
274             "  @Inject A(Optional<String> optional) {}",
275             "}");
276     Source module =
277         CompilerTests.javaSource(
278             "test.TestModule",
279             "package test;",
280             "",
281             "import dagger.BindsOptionalOf;",
282             "import dagger.Provides;",
283             "import javax.inject.Inject;",
284             "",
285             "@dagger.Module",
286             "abstract class TestModule {",
287             "  @Nullable @Provides static String provideString() { return null; }",
288             "  @BindsOptionalOf abstract String optionalString();",
289             "}");
290     Source component =
291         CompilerTests.javaSource(
292             "test.TestComponent",
293             "package test;",
294             "",
295             "import dagger.Component;",
296             "",
297             "@Component(modules = TestModule.class)",
298             "interface TestComponent {",
299             "  A a();",
300             "}");
301     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
302         .withProcessingOptions(compilerMode.processorOptions())
303         .compile(
304             subject -> {
305               subject.hasErrorCount(1);
306               subject.hasErrorContaining(
307                   nullableToNonNullable(
308                       "String",
309                       "@Nullable @Provides String TestModule.provideString()"));
310             });
311   }
312 
313   @Test
nullCheckForOptionalProvider()314   public void nullCheckForOptionalProvider() {
315     Source a =
316         CompilerTests.javaSource(
317             "test.A",
318             "package test;",
319             "",
320             "import com.google.common.base.Optional;",
321             "import javax.inject.Inject;",
322             "import javax.inject.Provider;",
323             "",
324             "final class A {",
325             "  @Inject A(Optional<Provider<String>> optional) {}",
326             "}");
327     Source module =
328         CompilerTests.javaSource(
329             "test.TestModule",
330             "package test;",
331             "",
332             "import dagger.BindsOptionalOf;",
333             "import dagger.Provides;",
334             "import javax.inject.Inject;",
335             "",
336             "@dagger.Module",
337             "abstract class TestModule {",
338             "  @Nullable @Provides static String provideString() { return null; }",
339             "  @BindsOptionalOf abstract String optionalString();",
340             "}");
341     Source component =
342         CompilerTests.javaSource(
343 
344             "test.TestComponent",
345             "package test;",
346             "",
347             "import dagger.Component;",
348             "",
349             "@Component(modules = TestModule.class)",
350             "interface TestComponent {",
351             "  A a();",
352             "}");
353     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
354         .withProcessingOptions(compilerMode.processorOptions())
355         .compile(subject -> subject.hasErrorCount(0));
356   }
357 
358   @Test
nullCheckForOptionalLazy()359   public void nullCheckForOptionalLazy() {
360     Source a =
361         CompilerTests.javaSource(
362             "test.A",
363             "package test;",
364             "",
365             "import com.google.common.base.Optional;",
366             "import dagger.Lazy;",
367             "import javax.inject.Inject;",
368             "",
369             "final class A {",
370             "  @Inject A(Optional<Lazy<String>> optional) {}",
371             "}");
372     Source module =
373         CompilerTests.javaSource(
374             "test.TestModule",
375             "package test;",
376             "",
377             "import dagger.BindsOptionalOf;",
378             "import dagger.Provides;",
379             "import javax.inject.Inject;",
380             "",
381             "@dagger.Module",
382             "abstract class TestModule {",
383             "  @Nullable @Provides static String provideString() { return null; }",
384             "  @BindsOptionalOf abstract String optionalString();",
385             "}");
386     Source component =
387         CompilerTests.javaSource(
388             "test.TestComponent",
389             "package test;",
390             "",
391             "import dagger.Component;",
392             "",
393             "@Component(modules = TestModule.class)",
394             "interface TestComponent {",
395             "  A a();",
396             "}");
397     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
398         .withProcessingOptions(compilerMode.processorOptions())
399         .compile(subject -> subject.hasErrorCount(0));
400   }
401 
402   @Test
nullCheckForOptionalProviderOfLazy()403   public void nullCheckForOptionalProviderOfLazy() {
404     Source a =
405         CompilerTests.javaSource(
406             "test.A",
407             "package test;",
408             "",
409             "import com.google.common.base.Optional;",
410             "import dagger.Lazy;",
411             "import javax.inject.Inject;",
412             "import javax.inject.Provider;",
413             "",
414             "final class A {",
415             "  @Inject A(Optional<Provider<Lazy<String>>> optional) {}",
416             "}");
417     Source module =
418         CompilerTests.javaSource(
419             "test.TestModule",
420             "package test;",
421             "",
422             "import dagger.BindsOptionalOf;",
423             "import dagger.Provides;",
424             "import javax.inject.Inject;",
425             "",
426             "@dagger.Module",
427             "abstract class TestModule {",
428             "  @Nullable @Provides static String provideString() { return null; }",
429             "  @BindsOptionalOf abstract String optionalString();",
430             "}");
431     Source component =
432         CompilerTests.javaSource(
433             "test.TestComponent",
434             "package test;",
435             "",
436             "import dagger.Component;",
437             "",
438             "@Component(modules = TestModule.class)",
439             "interface TestComponent {",
440             "  A a();",
441             "}");
442     CompilerTests.daggerCompiler(NULLABLE, a, module, component)
443         .withProcessingOptions(compilerMode.processorOptions())
444         .compile(subject -> subject.hasErrorCount(0));
445   }
446 
447   @Test
moduleValidation()448   public void moduleValidation() {
449     Source module =
450         CompilerTests.javaSource(
451             "test.TestModule",
452             "package test;",
453             "",
454             "import dagger.Binds;",
455             "import dagger.Module;",
456             "import dagger.Provides;",
457             "",
458             "@Module",
459             "abstract class TestModule {",
460             "  @Provides @Nullable static String nullableString() { return null; }",
461             "  @Binds abstract Object object(String string);",
462             "}");
463 
464     CompilerTests.daggerCompiler(NULLABLE, module)
465         .withProcessingOptions(
466             ImmutableMap.<String, String>builder()
467                 .putAll(compilerMode.processorOptions())
468                 .put("dagger.fullBindingGraphValidation", "ERROR")
469                 .buildOrThrow())
470         .compile(
471             subject -> {
472               subject.hasErrorCount(1);
473               subject.hasErrorContaining(
474                   nullableToNonNullable(
475                       "String",
476                       "@Provides @Nullable String TestModule.nullableString()"));
477             });
478   }
479 }
480