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