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