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