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