1 /* 2 * Copyright (C) 2022 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.kotlin; 18 19 import androidx.room.compiler.processing.util.Source; 20 import dagger.testing.compile.CompilerTests; 21 import org.junit.Test; 22 import org.junit.runner.RunWith; 23 import org.junit.runners.JUnit4; 24 25 @RunWith(JUnit4.class) 26 public final class KspComponentProcessorTest { 27 @Test emptyComponentTest()28 public void emptyComponentTest() throws Exception { 29 Source componentSrc = 30 CompilerTests.kotlinSource( 31 "MyComponent.kt", 32 "package test", 33 "", 34 "import dagger.Component", 35 "", 36 "@Component", 37 "interface MyComponent {}"); 38 39 CompilerTests.daggerCompiler(componentSrc) 40 .compile( 41 subject -> { 42 subject.hasErrorCount(0); 43 subject.generatedSource( 44 CompilerTests.javaSource( 45 "test/DaggerMyComponent", 46 "package test;", 47 "", 48 "import dagger.internal.DaggerGenerated;", 49 "import javax.annotation.processing.Generated;", 50 "", 51 "@DaggerGenerated", 52 "@Generated(", 53 " value = \"dagger.internal.codegen.ComponentProcessor\",", 54 " comments = \"https://dagger.dev\"", 55 ")", 56 "@SuppressWarnings({", 57 " \"unchecked\",", 58 " \"rawtypes\",", 59 " \"KotlinInternal\",", 60 " \"KotlinInternalInJava\",", 61 " \"cast\",", 62 " \"deprecation\",", 63 " \"nullness:initialization.field.uninitialized\"", 64 "})", 65 "public final class DaggerMyComponent {", 66 " private DaggerMyComponent() {", 67 " }", 68 "", 69 " public static Builder builder() {", 70 " return new Builder();", 71 " }", 72 "", 73 " public static MyComponent create() {", 74 " return new Builder().build();", 75 " }", 76 "", 77 " public static final class Builder {", 78 " private Builder() {", 79 " }", 80 "", 81 " public MyComponent build() {", 82 " return new MyComponentImpl();", 83 " }", 84 " }", 85 "", 86 " private static final class MyComponentImpl implements MyComponent {", 87 " private final MyComponentImpl myComponentImpl = this;", 88 "", 89 " private MyComponentImpl() {", 90 "", 91 "", 92 " }", 93 " }", 94 "}")); 95 }); 96 } 97 98 @Test 99 public void testComponentReferencingGeneratedTypeInCompanionObject_successfullyGeneratedComponent()100 testComponentReferencingGeneratedTypeInCompanionObject_successfullyGeneratedComponent() 101 throws Exception { 102 Source componentSrc = 103 CompilerTests.kotlinSource( 104 "MyComponent.kt", 105 "package test", 106 "", 107 "import dagger.BindsInstance", 108 "import dagger.Component", 109 "", 110 "@Component", 111 "interface MyComponent {", 112 " @Component.Builder", 113 " interface Builder {", 114 " @BindsInstance fun text(text: String): Builder", 115 " fun build(): MyComponent", 116 " }", 117 "", 118 " companion object {", 119 " fun getComponent(text: String) = DaggerMyComponent.builder().text(text).build()", 120 " }", 121 "}"); 122 123 CompilerTests.daggerCompiler(componentSrc).compile(subject -> subject.hasErrorCount(0)); 124 } 125 126 @Test injectBindingComponentTest()127 public void injectBindingComponentTest() throws Exception { 128 Source componentSrc = 129 CompilerTests.kotlinSource( 130 "MyComponent.kt", 131 "package test", 132 "", 133 "import dagger.Component", 134 "import javax.inject.Inject", 135 "", 136 "@Component", 137 "interface MyComponent {", 138 " fun foo(): Foo", 139 "}", 140 "", 141 "class Foo @Inject constructor(bar: Bar)", 142 "", 143 "class Bar @Inject constructor()"); 144 145 CompilerTests.daggerCompiler(componentSrc) 146 .compile( 147 subject -> { 148 subject.hasErrorCount(0); 149 subject.generatedSource( 150 CompilerTests.javaSource( 151 "test/DaggerMyComponent", 152 "package test;", 153 "", 154 "import dagger.internal.DaggerGenerated;", 155 "import javax.annotation.processing.Generated;", 156 "", 157 "@DaggerGenerated", 158 "@Generated(", 159 " value = \"dagger.internal.codegen.ComponentProcessor\",", 160 " comments = \"https://dagger.dev\"", 161 ")", 162 "@SuppressWarnings({", 163 " \"unchecked\",", 164 " \"rawtypes\",", 165 " \"KotlinInternal\",", 166 " \"KotlinInternalInJava\",", 167 " \"cast\",", 168 " \"deprecation\",", 169 " \"nullness:initialization.field.uninitialized\"", 170 "})", 171 "public final class DaggerMyComponent {", 172 " private DaggerMyComponent() {", 173 " }", 174 "", 175 " public static Builder builder() {", 176 " return new Builder();", 177 " }", 178 "", 179 " public static MyComponent create() {", 180 " return new Builder().build();", 181 " }", 182 "", 183 " public static final class Builder {", 184 " private Builder() {", 185 " }", 186 "", 187 " public MyComponent build() {", 188 " return new MyComponentImpl();", 189 " }", 190 " }", 191 "", 192 " private static final class MyComponentImpl implements MyComponent {", 193 " private final MyComponentImpl myComponentImpl = this;", 194 "", 195 " private MyComponentImpl() {", 196 "", 197 "", 198 " }", 199 "", 200 " @Override", 201 " public Foo foo() {", 202 " return new Foo(new Bar());", 203 " }", 204 " }", 205 "}")); 206 }); 207 } 208 209 @Test injectBindingWithProvidersComponentTest()210 public void injectBindingWithProvidersComponentTest() throws Exception { 211 Source componentSrc = 212 CompilerTests.kotlinSource( 213 "MyComponent.kt", 214 "package test", 215 "", 216 "import dagger.Component", 217 "import javax.inject.Inject", 218 "import javax.inject.Provider", 219 "", 220 "@Component", 221 "interface MyComponent {", 222 " fun foo(): Provider<Foo>", 223 "}", 224 "", 225 "class Foo @Inject constructor(bar: Bar, barProvider: Provider<Bar>)", 226 "", 227 "class Bar @Inject constructor()"); 228 229 CompilerTests.daggerCompiler(componentSrc) 230 .compile( 231 subject -> { 232 subject.hasErrorCount(0); 233 subject.generatedSource( 234 CompilerTests.javaSource( 235 "test/DaggerMyComponent", 236 "package test;", 237 "", 238 "import dagger.internal.DaggerGenerated;", 239 "import dagger.internal.Provider;", 240 "import javax.annotation.processing.Generated;", 241 "", 242 "@DaggerGenerated", 243 "@Generated(", 244 " value = \"dagger.internal.codegen.ComponentProcessor\",", 245 " comments = \"https://dagger.dev\"", 246 ")", 247 "@SuppressWarnings({", 248 " \"unchecked\",", 249 " \"rawtypes\",", 250 " \"KotlinInternal\",", 251 " \"KotlinInternalInJava\",", 252 " \"cast\",", 253 " \"deprecation\",", 254 " \"nullness:initialization.field.uninitialized\"", 255 "})", 256 "public final class DaggerMyComponent {", 257 " private DaggerMyComponent() {", 258 " }", 259 "", 260 " public static Builder builder() {", 261 " return new Builder();", 262 " }", 263 "", 264 " public static MyComponent create() {", 265 " return new Builder().build();", 266 " }", 267 "", 268 " public static final class Builder {", 269 " private Builder() {", 270 " }", 271 "", 272 " public MyComponent build() {", 273 " return new MyComponentImpl();", 274 " }", 275 " }", 276 "", 277 " private static final class MyComponentImpl implements MyComponent {", 278 " private final MyComponentImpl myComponentImpl = this;", 279 "", 280 " private Provider<Foo> fooProvider;", 281 "", 282 " private MyComponentImpl() {", 283 "", 284 " initialize();", 285 "", 286 " }", 287 "", 288 " @SuppressWarnings(\"unchecked\")", 289 " private void initialize() {", 290 " this.fooProvider = " 291 + "Foo_Factory.create(Bar_Factory.create(), Bar_Factory.create());", 292 " }", 293 "", 294 " @Override", 295 " public javax.inject.Provider<Foo> foo() {", 296 " return fooProvider;", 297 " }", 298 " }", 299 "}")); 300 }); 301 } 302 303 @Test moduleProvidesBindingTest()304 public void moduleProvidesBindingTest() throws Exception { 305 Source componentSrc = 306 CompilerTests.kotlinSource( 307 "MyComponent.kt", 308 "package test", 309 "", 310 "import dagger.Component", 311 "import dagger.Module", 312 "import dagger.Provides", 313 "import javax.inject.Inject", 314 "import javax.inject.Named", 315 "import javax.inject.Provider", 316 "", 317 "@Component(modules = [MyModule::class])", 318 "interface MyComponent {", 319 " @Named(\"key\") fun foo(): Foo", 320 "}", 321 "", 322 "@Module", 323 "class MyModule {", 324 " @Provides @Named(\"key\") fun provideFoo(@Named(\"key\") bar: Bar) = Foo(bar)", 325 " @Provides @Named(\"key\") fun provideBar() = Bar()", 326 "}", 327 "", 328 "class Foo constructor(bar: Bar)", 329 "", 330 "class Bar"); 331 332 CompilerTests.daggerCompiler(componentSrc) 333 .compile( 334 subject -> { 335 subject.hasErrorCount(0); 336 subject.generatedSource( 337 CompilerTests.javaSource( 338 "test/DaggerMyComponent", 339 "package test;", 340 "", 341 "import dagger.internal.DaggerGenerated;", 342 "import dagger.internal.Preconditions;", 343 "import javax.annotation.processing.Generated;", 344 "", 345 "@DaggerGenerated", 346 "@Generated(", 347 " value = \"dagger.internal.codegen.ComponentProcessor\",", 348 " comments = \"https://dagger.dev\"", 349 ")", 350 "@SuppressWarnings({", 351 " \"unchecked\",", 352 " \"rawtypes\",", 353 " \"KotlinInternal\",", 354 " \"KotlinInternalInJava\",", 355 " \"cast\",", 356 " \"deprecation\",", 357 " \"nullness:initialization.field.uninitialized\"", 358 "})", 359 "public final class DaggerMyComponent {", 360 " private DaggerMyComponent() {", 361 " }", 362 "", 363 " public static Builder builder() {", 364 " return new Builder();", 365 " }", 366 "", 367 " public static MyComponent create() {", 368 " return new Builder().build();", 369 " }", 370 "", 371 " public static final class Builder {", 372 " private MyModule myModule;", 373 "", 374 " private Builder() {", 375 " }", 376 "", 377 " public Builder myModule(MyModule myModule) {", 378 " this.myModule = Preconditions.checkNotNull(myModule);", 379 " return this;", 380 " }", 381 "", 382 " public MyComponent build() {", 383 " if (myModule == null) {", 384 " this.myModule = new MyModule();", 385 " }", 386 " return new MyComponentImpl(myModule);", 387 " }", 388 " }", 389 "", 390 " private static final class MyComponentImpl implements MyComponent {", 391 " private final MyModule myModule;", 392 "", 393 " private final MyComponentImpl myComponentImpl = this;", 394 "", 395 " private MyComponentImpl(MyModule myModuleParam) {", 396 " this.myModule = myModuleParam;", 397 "", 398 " }", 399 "", 400 " @Override", 401 " public Foo foo() {", 402 " return MyModule_ProvideFooFactory.provideFoo(" 403 + "myModule, MyModule_ProvideBarFactory.provideBar(myModule));", 404 " }", 405 " }", 406 "}")); 407 }); 408 } 409 410 @Test membersInjectionMethodTest()411 public void membersInjectionMethodTest() throws Exception { 412 Source componentSrc = 413 CompilerTests.kotlinSource( 414 "MyComponent.kt", 415 "package test", 416 "", 417 "import dagger.Component", 418 "import dagger.Module", 419 "import dagger.Provides", 420 "import javax.inject.Inject", 421 "import javax.inject.Named", 422 "import javax.inject.Provider", 423 "", 424 "@Component(modules = [MyModule::class])", 425 "interface MyComponent {", 426 " fun injectFoo(foo: Foo)", 427 "}", 428 "", 429 "@Module", 430 "class MyModule {", 431 " @Provides @Named(\"key\") fun provideBar() = Bar()", 432 "}", 433 "", 434 "class Foo {", 435 " @Inject @Named(\"key\") lateinit var bar: Bar", 436 "}", 437 "", 438 "class Bar"); 439 440 CompilerTests.daggerCompiler(componentSrc) 441 .compile( 442 subject -> { 443 subject.hasErrorCount(0); 444 subject.generatedSource( 445 CompilerTests.javaSource( 446 "test/DaggerMyComponent", 447 "package test;", 448 "", 449 "import com.google.errorprone.annotations.CanIgnoreReturnValue;", 450 "import dagger.internal.DaggerGenerated;", 451 "import dagger.internal.Preconditions;", 452 "import javax.annotation.processing.Generated;", 453 "", 454 "@DaggerGenerated", 455 "@Generated(", 456 " value = \"dagger.internal.codegen.ComponentProcessor\",", 457 " comments = \"https://dagger.dev\"", 458 ")", 459 "@SuppressWarnings({", 460 " \"unchecked\",", 461 " \"rawtypes\",", 462 " \"KotlinInternal\",", 463 " \"KotlinInternalInJava\",", 464 " \"cast\",", 465 " \"deprecation\",", 466 " \"nullness:initialization.field.uninitialized\"", 467 "})", 468 "public final class DaggerMyComponent {", 469 " private DaggerMyComponent() {", 470 " }", 471 "", 472 " public static Builder builder() {", 473 " return new Builder();", 474 " }", 475 "", 476 " public static MyComponent create() {", 477 " return new Builder().build();", 478 " }", 479 "", 480 " public static final class Builder {", 481 " private MyModule myModule;", 482 "", 483 " private Builder() {", 484 " }", 485 "", 486 " public Builder myModule(MyModule myModule) {", 487 " this.myModule = Preconditions.checkNotNull(myModule);", 488 " return this;", 489 " }", 490 "", 491 " public MyComponent build() {", 492 " if (myModule == null) {", 493 " this.myModule = new MyModule();", 494 " }", 495 " return new MyComponentImpl(myModule);", 496 " }", 497 " }", 498 "", 499 " private static final class MyComponentImpl implements MyComponent {", 500 " private final MyModule myModule;", 501 "", 502 " private final MyComponentImpl myComponentImpl = this;", 503 "", 504 " private MyComponentImpl(MyModule myModuleParam) {", 505 " this.myModule = myModuleParam;", 506 "", 507 " }", 508 "", 509 " @Override", 510 " public void injectFoo(Foo foo) {", 511 " injectFoo2(foo);", 512 " }", 513 "", 514 " @CanIgnoreReturnValue", 515 " private Foo injectFoo2(Foo instance) {", 516 " Foo_MembersInjector.injectBar(" 517 + "instance, MyModule_ProvideBarFactory.provideBar(myModule));", 518 " return instance;", 519 " }", 520 " }", 521 "}")); 522 }); 523 } 524 525 @Test membersInjectionTest()526 public void membersInjectionTest() throws Exception { 527 Source componentSrc = 528 CompilerTests.kotlinSource( 529 "MyComponent.kt", 530 "package test", 531 "", 532 "import dagger.Component", 533 "import dagger.Module", 534 "import dagger.Provides", 535 "import javax.inject.Inject", 536 "import javax.inject.Named", 537 "import javax.inject.Provider", 538 "", 539 "@Component(modules = [MyModule::class])", 540 "interface MyComponent {", 541 " fun foo(): Foo", 542 "}", 543 "", 544 "@Module", 545 "class MyModule {", 546 " @Provides @Named(\"key\") fun provideBar() = Bar()", 547 "}", 548 "", 549 "class Foo @Inject constructor() {", 550 " @Inject @Named(\"key\") lateinit var bar: Bar", 551 "}", 552 "", 553 "class Bar"); 554 555 CompilerTests.daggerCompiler(componentSrc) 556 .compile( 557 subject -> { 558 subject.hasErrorCount(0); 559 subject.generatedSource( 560 CompilerTests.javaSource( 561 "test/DaggerMyComponent", 562 "package test;", 563 "", 564 "import com.google.errorprone.annotations.CanIgnoreReturnValue;", 565 "import dagger.internal.DaggerGenerated;", 566 "import dagger.internal.Preconditions;", 567 "import javax.annotation.processing.Generated;", 568 "", 569 "@DaggerGenerated", 570 "@Generated(", 571 " value = \"dagger.internal.codegen.ComponentProcessor\",", 572 " comments = \"https://dagger.dev\"", 573 ")", 574 "@SuppressWarnings({", 575 " \"unchecked\",", 576 " \"rawtypes\",", 577 " \"KotlinInternal\",", 578 " \"KotlinInternalInJava\",", 579 " \"cast\",", 580 " \"deprecation\",", 581 " \"nullness:initialization.field.uninitialized\"", 582 "})", 583 "public final class DaggerMyComponent {", 584 " private DaggerMyComponent() {", 585 " }", 586 "", 587 " public static Builder builder() {", 588 " return new Builder();", 589 " }", 590 "", 591 " public static MyComponent create() {", 592 " return new Builder().build();", 593 " }", 594 "", 595 " public static final class Builder {", 596 " private MyModule myModule;", 597 "", 598 " private Builder() {", 599 " }", 600 "", 601 " public Builder myModule(MyModule myModule) {", 602 " this.myModule = Preconditions.checkNotNull(myModule);", 603 " return this;", 604 " }", 605 "", 606 " public MyComponent build() {", 607 " if (myModule == null) {", 608 " this.myModule = new MyModule();", 609 " }", 610 " return new MyComponentImpl(myModule);", 611 " }", 612 " }", 613 "", 614 " private static final class MyComponentImpl implements MyComponent {", 615 " private final MyModule myModule;", 616 "", 617 " private final MyComponentImpl myComponentImpl = this;", 618 "", 619 " private MyComponentImpl(MyModule myModuleParam) {", 620 " this.myModule = myModuleParam;", 621 "", 622 " }", 623 "", 624 " @Override", 625 " public Foo foo() {", 626 " return injectFoo(Foo_Factory.newInstance());", 627 " }", 628 "", 629 " @CanIgnoreReturnValue", 630 " private Foo injectFoo(Foo instance) {", 631 " Foo_MembersInjector.injectBar(" 632 + "instance, MyModule_ProvideBarFactory.provideBar(myModule));", 633 " return instance;", 634 " }", 635 " }", 636 "}")); 637 }); 638 } 639 } 640