• 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;
18 
19 import static com.google.testing.compile.CompilationSubject.assertThat;
20 import static com.google.testing.compile.Compiler.javac;
21 import static dagger.internal.codegen.Compilers.daggerCompiler;
22 import static dagger.internal.codegen.NullableBindingValidator.nullableToNonNullable;
23 
24 import com.google.testing.compile.Compilation;
25 import com.google.testing.compile.JavaFileObjects;
26 import javax.tools.JavaFileObject;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.junit.runners.JUnit4;
30 
31 @RunWith(JUnit4.class)
32 public class NullableBindingValidationTest {
33   private static final JavaFileObject NULLABLE =
34       JavaFileObjects.forSourceLines(
35           "test.Nullable", // force one-string-per-line format
36           "package test;",
37           "",
38           "public @interface Nullable {}");
39 
nullCheckForConstructorParameters()40   @Test public void nullCheckForConstructorParameters() {
41     JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
42         "package test;",
43         "",
44         "import javax.inject.Inject;",
45         "",
46         "final class A {",
47         "  @Inject A(String string) {}",
48         "}");
49     JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
50         "package test;",
51         "",
52         "import dagger.Provides;",
53         "import javax.inject.Inject;",
54         "",
55         "@dagger.Module",
56         "final class TestModule {",
57         "  @Nullable @Provides String provideString() { return null; }",
58         "}");
59     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
60         "package test;",
61         "",
62         "import dagger.Component;",
63         "",
64         "@Component(modules = TestModule.class)",
65         "interface TestComponent {",
66         "  A a();",
67         "}");
68     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
69     assertThat(compilation).failed();
70     assertThat(compilation)
71         .hadErrorContaining(
72             nullableToNonNullable(
73                 "java.lang.String",
74                 "@test.Nullable @Provides String test.TestModule.provideString()"));
75 
76     // but if we disable the validation, then it compiles fine.
77     Compilation compilation2 =
78         javac()
79             .withOptions("-Adagger.nullableValidation=WARNING")
80             .withProcessors(new ComponentProcessor())
81             .compile(NULLABLE, a, module, component);
82     assertThat(compilation2).succeeded();
83   }
84 
nullCheckForMembersInjectParam()85   @Test public void nullCheckForMembersInjectParam() {
86     JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
87         "package test;",
88         "",
89         "import javax.inject.Inject;",
90         "",
91         "final class A {",
92         "  @Inject A() {}",
93         "  @Inject void register(String string) {}",
94         "}");
95     JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
96         "package test;",
97         "",
98         "import dagger.Provides;",
99         "import javax.inject.Inject;",
100         "",
101         "@dagger.Module",
102         "final class TestModule {",
103         "  @Nullable @Provides String provideString() { return null; }",
104         "}");
105     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
106         "package test;",
107         "",
108         "import dagger.Component;",
109         "",
110         "@Component(modules = TestModule.class)",
111         "interface TestComponent {",
112         "  A a();",
113         "}");
114     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
115     assertThat(compilation).failed();
116     assertThat(compilation)
117         .hadErrorContaining(
118             nullableToNonNullable(
119                 "java.lang.String",
120                 "@test.Nullable @Provides String test.TestModule.provideString()"));
121 
122     // but if we disable the validation, then it compiles fine.
123     Compilation compilation2 =
124         javac()
125             .withOptions("-Adagger.nullableValidation=WARNING")
126             .withProcessors(new ComponentProcessor())
127             .compile(NULLABLE, a, module, component);
128     assertThat(compilation2).succeeded();
129   }
130 
nullCheckForVariable()131   @Test public void nullCheckForVariable() {
132     JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
133         "package test;",
134         "",
135         "import javax.inject.Inject;",
136         "",
137         "final class A {",
138         "  @Inject String string;",
139         "  @Inject A() {}",
140         "}");
141     JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
142         "package test;",
143         "",
144         "import dagger.Provides;",
145         "import javax.inject.Inject;",
146         "",
147         "@dagger.Module",
148         "final class TestModule {",
149         "  @Nullable @Provides String provideString() { return null; }",
150         "}");
151     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
152         "package test;",
153         "",
154         "import dagger.Component;",
155         "",
156         "@Component(modules = TestModule.class)",
157         "interface TestComponent {",
158         "  A a();",
159         "}");
160     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
161     assertThat(compilation).failed();
162     assertThat(compilation)
163         .hadErrorContaining(
164             nullableToNonNullable(
165                 "java.lang.String",
166                 "@test.Nullable @Provides String test.TestModule.provideString()"));
167 
168     // but if we disable the validation, then it compiles fine.
169     Compilation compilation2 =
170         javac()
171             .withOptions("-Adagger.nullableValidation=WARNING")
172             .withProcessors(new ComponentProcessor())
173             .compile(NULLABLE, a, module, component);
174     assertThat(compilation2).succeeded();
175   }
176 
nullCheckForComponentReturn()177   @Test public void nullCheckForComponentReturn() {
178     JavaFileObject module = JavaFileObjects.forSourceLines("test.TestModule",
179         "package test;",
180         "",
181         "import dagger.Provides;",
182         "import javax.inject.Inject;",
183         "",
184         "@dagger.Module",
185         "final class TestModule {",
186         "  @Nullable @Provides String provideString() { return null; }",
187         "}");
188     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
189         "package test;",
190         "",
191         "import dagger.Component;",
192         "",
193         "@Component(modules = TestModule.class)",
194         "interface TestComponent {",
195         "  String string();",
196         "}");
197     Compilation compilation = daggerCompiler().compile(NULLABLE, module, component);
198     assertThat(compilation).failed();
199     assertThat(compilation)
200         .hadErrorContaining(
201             nullableToNonNullable(
202                 "java.lang.String",
203                 "@test.Nullable @Provides String test.TestModule.provideString()"));
204 
205     // but if we disable the validation, then it compiles fine.
206     Compilation compilation2 =
207         javac()
208             .withOptions("-Adagger.nullableValidation=WARNING")
209             .withProcessors(new ComponentProcessor())
210             .compile(NULLABLE, module, component);
211     assertThat(compilation2).succeeded();
212   }
213 
214   @Test
nullCheckForOptionalInstance()215   public void nullCheckForOptionalInstance() {
216     JavaFileObject a =
217         JavaFileObjects.forSourceLines(
218             "test.A",
219             "package test;",
220             "",
221             "import com.google.common.base.Optional;",
222             "import javax.inject.Inject;",
223             "",
224             "final class A {",
225             "  @Inject A(Optional<String> optional) {}",
226             "}");
227     JavaFileObject module =
228         JavaFileObjects.forSourceLines(
229             "test.TestModule",
230             "package test;",
231             "",
232             "import dagger.BindsOptionalOf;",
233             "import dagger.Provides;",
234             "import javax.inject.Inject;",
235             "",
236             "@dagger.Module",
237             "abstract class TestModule {",
238             "  @Nullable @Provides static String provideString() { return null; }",
239             "  @BindsOptionalOf abstract String optionalString();",
240             "}");
241     JavaFileObject component =
242         JavaFileObjects.forSourceLines(
243             "test.TestComponent",
244             "package test;",
245             "",
246             "import dagger.Component;",
247             "",
248             "@Component(modules = TestModule.class)",
249             "interface TestComponent {",
250             "  A a();",
251             "}");
252     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
253     assertThat(compilation).failed();
254     assertThat(compilation)
255         .hadErrorContaining(
256             nullableToNonNullable(
257                 "java.lang.String",
258                 "@test.Nullable @Provides String test.TestModule.provideString()"));
259   }
260 
261   @Test
nullCheckForOptionalProvider()262   public void nullCheckForOptionalProvider() {
263     JavaFileObject a =
264         JavaFileObjects.forSourceLines(
265             "test.A",
266             "package test;",
267             "",
268             "import com.google.common.base.Optional;",
269             "import javax.inject.Inject;",
270             "import javax.inject.Provider;",
271             "",
272             "final class A {",
273             "  @Inject A(Optional<Provider<String>> optional) {}",
274             "}");
275     JavaFileObject module =
276         JavaFileObjects.forSourceLines(
277             "test.TestModule",
278             "package test;",
279             "",
280             "import dagger.BindsOptionalOf;",
281             "import dagger.Provides;",
282             "import javax.inject.Inject;",
283             "",
284             "@dagger.Module",
285             "abstract class TestModule {",
286             "  @Nullable @Provides static String provideString() { return null; }",
287             "  @BindsOptionalOf abstract String optionalString();",
288             "}");
289     JavaFileObject component =
290         JavaFileObjects.forSourceLines(
291             "test.TestComponent",
292             "package test;",
293             "",
294             "import dagger.Component;",
295             "",
296             "@Component(modules = TestModule.class)",
297             "interface TestComponent {",
298             "  A a();",
299             "}");
300     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
301     assertThat(compilation).succeeded();
302   }
303 
304   @Test
nullCheckForOptionalLazy()305   public void nullCheckForOptionalLazy() {
306     JavaFileObject a =
307         JavaFileObjects.forSourceLines(
308             "test.A",
309             "package test;",
310             "",
311             "import com.google.common.base.Optional;",
312             "import dagger.Lazy;",
313             "import javax.inject.Inject;",
314             "",
315             "final class A {",
316             "  @Inject A(Optional<Lazy<String>> optional) {}",
317             "}");
318     JavaFileObject module =
319         JavaFileObjects.forSourceLines(
320             "test.TestModule",
321             "package test;",
322             "",
323             "import dagger.BindsOptionalOf;",
324             "import dagger.Provides;",
325             "import javax.inject.Inject;",
326             "",
327             "@dagger.Module",
328             "abstract class TestModule {",
329             "  @Nullable @Provides static String provideString() { return null; }",
330             "  @BindsOptionalOf abstract String optionalString();",
331             "}");
332     JavaFileObject component =
333         JavaFileObjects.forSourceLines(
334             "test.TestComponent",
335             "package test;",
336             "",
337             "import dagger.Component;",
338             "",
339             "@Component(modules = TestModule.class)",
340             "interface TestComponent {",
341             "  A a();",
342             "}");
343     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
344     assertThat(compilation).succeeded();
345   }
346 
347   @Test
nullCheckForOptionalProviderOfLazy()348   public void nullCheckForOptionalProviderOfLazy() {
349     JavaFileObject a =
350         JavaFileObjects.forSourceLines(
351             "test.A",
352             "package test;",
353             "",
354             "import com.google.common.base.Optional;",
355             "import dagger.Lazy;",
356             "import javax.inject.Inject;",
357             "import javax.inject.Provider;",
358             "",
359             "final class A {",
360             "  @Inject A(Optional<Provider<Lazy<String>>> optional) {}",
361             "}");
362     JavaFileObject module =
363         JavaFileObjects.forSourceLines(
364             "test.TestModule",
365             "package test;",
366             "",
367             "import dagger.BindsOptionalOf;",
368             "import dagger.Provides;",
369             "import javax.inject.Inject;",
370             "",
371             "@dagger.Module",
372             "abstract class TestModule {",
373             "  @Nullable @Provides static String provideString() { return null; }",
374             "  @BindsOptionalOf abstract String optionalString();",
375             "}");
376     JavaFileObject component =
377         JavaFileObjects.forSourceLines(
378             "test.TestComponent",
379             "package test;",
380             "",
381             "import dagger.Component;",
382             "",
383             "@Component(modules = TestModule.class)",
384             "interface TestComponent {",
385             "  A a();",
386             "}");
387     Compilation compilation = daggerCompiler().compile(NULLABLE, a, module, component);
388     assertThat(compilation).succeeded();
389   }
390 
391   @Test
moduleValidation()392   public void moduleValidation() {
393     JavaFileObject module =
394         JavaFileObjects.forSourceLines(
395             "test.TestModule",
396             "package test;",
397             "",
398             "import dagger.Binds;",
399             "import dagger.Module;",
400             "import dagger.Provides;",
401             "",
402             "@Module",
403             "abstract class TestModule {",
404             "  @Provides @Nullable static String nullableString() { return null; }",
405             "  @Binds abstract Object object(String string);",
406             "}");
407 
408     Compilation compilation =
409         daggerCompiler()
410             .withOptions("-Adagger.fullBindingGraphValidation=ERROR")
411             .compile(module, NULLABLE);
412     assertThat(compilation).failed();
413     assertThat(compilation)
414         .hadErrorContaining(
415             nullableToNonNullable(
416                 "java.lang.String",
417                 "@Provides @test.Nullable String test.TestModule.nullableString()"));
418   }
419 }
420