1 /* 2 * Copyright (C) 2015 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 androidx.room.compiler.processing.util.Source; 20 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableMap; 22 import com.squareup.javapoet.MethodSpec; 23 import com.squareup.javapoet.TypeSpec; 24 import dagger.internal.codegen.javapoet.TypeNames; 25 import dagger.testing.compile.CompilerTests; 26 import dagger.testing.golden.GoldenFileRule; 27 import org.junit.Rule; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.Parameterized; 31 import org.junit.runners.Parameterized.Parameters; 32 33 @RunWith(Parameterized.class) 34 public class MembersInjectionTest { 35 @Parameters(name = "{0}") parameters()36 public static ImmutableList<Object[]> parameters() { 37 return CompilerMode.TEST_PARAMETERS; 38 } 39 40 @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule(); 41 42 private final CompilerMode compilerMode; 43 MembersInjectionTest(CompilerMode compilerMode)44 public MembersInjectionTest(CompilerMode compilerMode) { 45 this.compilerMode = compilerMode; 46 } 47 48 @Test injectKotlinProtectField_fails()49 public void injectKotlinProtectField_fails() { 50 Source injectFieldSrc = 51 CompilerTests.kotlinSource( 52 "MyClass.kt", 53 "package test", 54 "", 55 "import javax.inject.Inject", 56 "", 57 "class MyClass @Inject constructor() {", 58 " @Inject protected lateinit var protectedField: String", 59 "}"); 60 Source moduleSrc = 61 CompilerTests.kotlinSource( 62 "MyModule.kt", 63 "package test", 64 "", 65 "import dagger.Module", 66 "import dagger.Provides", 67 "", 68 "@Module", 69 "object MyModule {", 70 " @Provides", 71 " fun providesString() = \"hello\"", 72 "}"); 73 Source componentSrc = 74 CompilerTests.kotlinSource( 75 "MyComponent.kt", 76 "package test", 77 "", 78 "import dagger.Component", 79 "@Component(modules = [MyModule::class])", 80 "interface MyComponent {}"); 81 CompilerTests.daggerCompiler(injectFieldSrc, moduleSrc, componentSrc) 82 .withProcessingOptions(compilerMode.processorOptions()) 83 .compile( 84 subject -> { 85 subject.hasErrorCount(1); 86 subject.hasErrorContaining( 87 "Dagger injector does not have access to kotlin protected fields"); 88 }); 89 } 90 91 @Test injectJavaProtectField_succeeds()92 public void injectJavaProtectField_succeeds() { 93 Source injectFieldSrc = 94 CompilerTests.javaSource( 95 "test.MyClass", 96 "package test;", 97 "", 98 "import javax.inject.Inject;", 99 "", 100 "public final class MyClass {", 101 " @Inject MyClass() {}", 102 " @Inject protected String protectedField;", 103 "}"); 104 Source moduleSrc = 105 CompilerTests.kotlinSource( 106 "MyModule.kt", 107 "package test", 108 "", 109 "import dagger.Module", 110 "import dagger.Provides", 111 "", 112 "@Module", 113 "object MyModule {", 114 " @Provides", 115 " fun providesString() = \"hello\"", 116 "}"); 117 Source componentSrc = 118 CompilerTests.kotlinSource( 119 "MyComponent.kt", 120 "package test", 121 "", 122 "import dagger.Component", 123 "@Component(modules = [MyModule::class])", 124 "interface MyComponent {}"); 125 CompilerTests.daggerCompiler(injectFieldSrc, moduleSrc, componentSrc) 126 .withProcessingOptions(compilerMode.processorOptions()) 127 .compile(subject -> subject.hasErrorCount(0)); 128 } 129 130 @Test parentClass_noInjectedMembers()131 public void parentClass_noInjectedMembers() throws Exception { 132 Source childFile = 133 CompilerTests.javaSource( 134 "test.Child", 135 "package test;", 136 "", 137 "import javax.inject.Inject;", 138 "", 139 "public final class Child extends Parent {", 140 " @Inject Child() {}", 141 "}"); 142 Source parentFile = 143 CompilerTests.javaSource( 144 "test.Parent", 145 "package test;", 146 "", 147 "public abstract class Parent {}"); 148 149 Source componentFile = 150 CompilerTests.javaSource( 151 "test.TestComponent", 152 "package test;", 153 "", 154 "import dagger.Component;", 155 "", 156 "@Component", 157 "interface TestComponent {", 158 " Child child();", 159 "}"); 160 161 CompilerTests.daggerCompiler(childFile, parentFile, componentFile) 162 .withProcessingOptions(compilerMode.processorOptions()) 163 .compile( 164 subject -> { 165 subject.hasErrorCount(0); 166 subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); 167 }); 168 } 169 170 @Test parentClass_injectedMembersInSupertype()171 public void parentClass_injectedMembersInSupertype() throws Exception { 172 Source childFile = 173 CompilerTests.javaSource( 174 "test.Child", 175 "package test;", 176 "", 177 "import javax.inject.Inject;", 178 "", 179 "public final class Child extends Parent {", 180 " @Inject Child() {}", 181 "}"); 182 Source parentFile = 183 CompilerTests.javaSource( 184 "test.Parent", 185 "package test;", 186 "", 187 "import javax.inject.Inject;", 188 "", 189 "public abstract class Parent {", 190 " @Inject Dep dep;", 191 "}"); 192 Source depFile = 193 CompilerTests.javaSource( 194 "test.Dep", 195 "package test;", 196 "", 197 "import javax.inject.Inject;", 198 "", 199 "final class Dep {", 200 " @Inject Dep() {}", 201 "}"); 202 Source componentFile = 203 CompilerTests.javaSource( 204 "test.TestComponent", 205 "package test;", 206 "", 207 "import dagger.Component;", 208 "", 209 "@Component", 210 "interface TestComponent {", 211 " Child child();", 212 "}"); 213 214 CompilerTests.daggerCompiler(childFile, parentFile, depFile, componentFile) 215 .withProcessingOptions(compilerMode.processorOptions()) 216 .compile( 217 subject -> { 218 subject.hasErrorCount(0); 219 subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); 220 }); 221 } 222 fieldAndMethodGenerics()223 @Test public void fieldAndMethodGenerics() { 224 Source file = 225 CompilerTests.javaSource( 226 "test.GenericClass", 227 "package test;", 228 "", 229 "import javax.inject.Inject;", 230 "", 231 "class GenericClass<A, B> {", 232 " @Inject A a;", 233 "", 234 " @Inject GenericClass() {}", 235 "", 236 " @Inject void register(B b) {}", 237 "}"); 238 239 CompilerTests.daggerCompiler(file) 240 .withProcessingOptions(compilerMode.processorOptions()) 241 .compile( 242 subject -> { 243 subject.hasErrorCount(0); 244 subject.generatedSource( 245 goldenFileRule.goldenSource("test/GenericClass_MembersInjector")); 246 }); 247 } 248 subclassedGenericMembersInjectors()249 @Test public void subclassedGenericMembersInjectors() { 250 Source a = 251 CompilerTests.javaSource( 252 "test.A", 253 "package test;", 254 "", 255 "import javax.inject.Inject;", 256 "", 257 "final class A {", 258 " @Inject A() {}", 259 "}"); 260 Source a2 = 261 CompilerTests.javaSource( 262 "test.A2", 263 "package test;", 264 "", 265 "import javax.inject.Inject;", 266 "", 267 "final class A2 {", 268 " @Inject A2() {}", 269 "}"); 270 Source parent = 271 CompilerTests.javaSource( 272 "test.Parent", 273 "package test;", 274 "", 275 "import javax.inject.Inject;", 276 "", 277 "class Parent<X, Y> {", 278 " @Inject X x;", 279 " @Inject Y y;", 280 " @Inject A2 a2;", 281 "", 282 " @Inject Parent() {}", 283 "}"); 284 Source child = 285 CompilerTests.javaSource( 286 "test.Child", 287 "package test;", 288 "", 289 "import javax.inject.Inject;", 290 "", 291 "class Child<T> extends Parent<T, A> {", 292 " @Inject A a;", 293 " @Inject T t;", 294 "", 295 " @Inject Child() {}", 296 "}"); 297 CompilerTests.daggerCompiler(a, a2, parent, child) 298 .withProcessingOptions(compilerMode.processorOptions()) 299 .compile( 300 subject -> { 301 subject.hasErrorCount(0); 302 subject.generatedSource(goldenFileRule.goldenSource("test/Child_MembersInjector")); 303 }); 304 } 305 fieldInjection()306 @Test public void fieldInjection() { 307 Source file = 308 CompilerTests.javaSource( 309 "test.FieldInjection", 310 "package test;", 311 "", 312 "import dagger.Lazy;", 313 "import javax.inject.Inject;", 314 "import javax.inject.Provider;", 315 "", 316 "class FieldInjection {", 317 " @Inject String string;", 318 " @Inject Lazy<String> lazyString;", 319 " @Inject Provider<String> stringProvider;", 320 "}"); 321 CompilerTests.daggerCompiler(file) 322 .withProcessingOptions(compilerMode.processorOptions()) 323 .compile( 324 subject -> { 325 subject.hasErrorCount(0); 326 subject.generatedSource( 327 goldenFileRule.goldenSource("test/FieldInjection_MembersInjector")); 328 }); 329 } 330 331 @Test fieldInjectionWithQualifier()332 public void fieldInjectionWithQualifier() { 333 Source file = 334 CompilerTests.javaSource( 335 "test.FieldInjectionWithQualifier", 336 "package test;", 337 "", 338 "import dagger.Lazy;", 339 "import javax.inject.Inject;", 340 "import javax.inject.Named;", 341 "import javax.inject.Provider;", 342 "", 343 "class FieldInjectionWithQualifier {", 344 " @Inject @Named(\"A\") String a;", 345 " @Inject @Named(\"B\") String b;", 346 "}"); 347 CompilerTests.daggerCompiler(file) 348 .withProcessingOptions(compilerMode.processorOptions()) 349 .compile( 350 subject -> { 351 subject.hasErrorCount(0); 352 subject.generatedSource( 353 goldenFileRule.goldenSource("test/FieldInjectionWithQualifier_MembersInjector")); 354 }); 355 } 356 methodInjection()357 @Test public void methodInjection() { 358 Source file = 359 CompilerTests.javaSource( 360 "test.MethodInjection", 361 "package test;", 362 "", 363 "import dagger.Lazy;", 364 "import javax.inject.Inject;", 365 "import javax.inject.Provider;", 366 "", 367 "class MethodInjection {", 368 " @Inject void noArgs() {}", 369 " @Inject void oneArg(String string) {}", 370 " @Inject void manyArgs(", 371 " String string, Lazy<String> lazyString, Provider<String> stringProvider) {}", 372 "}"); 373 CompilerTests.daggerCompiler(file) 374 .withProcessingOptions(compilerMode.processorOptions()) 375 .compile( 376 subject -> { 377 subject.hasErrorCount(0); 378 subject.generatedSource( 379 goldenFileRule.goldenSource("test/MethodInjection_MembersInjector")); 380 }); 381 } 382 383 @Test mixedMemberInjection()384 public void mixedMemberInjection() { 385 Source file = 386 CompilerTests.javaSource( 387 "test.MixedMemberInjection", 388 "package test;", 389 "", 390 "import dagger.Lazy;", 391 "import javax.inject.Inject;", 392 "import javax.inject.Provider;", 393 "", 394 "class MixedMemberInjection {", 395 " @Inject String string;", 396 " @Inject void setString(String s) {}", 397 " @Inject Object object;", 398 " @Inject void setObject(Object o) {}", 399 "}"); 400 CompilerTests.daggerCompiler(file) 401 .withProcessingOptions(compilerMode.processorOptions()) 402 .compile( 403 subject -> { 404 subject.hasErrorCount(0); 405 subject.generatedSource( 406 goldenFileRule.goldenSource("test/MixedMemberInjection_MembersInjector")); 407 }); 408 } 409 injectConstructorAndMembersInjection()410 @Test public void injectConstructorAndMembersInjection() { 411 Source file = 412 CompilerTests.javaSource( 413 "test.AllInjections", 414 "package test;", 415 "", 416 "import javax.inject.Inject;", 417 "", 418 "class AllInjections {", 419 " @Inject String s;", 420 " @Inject AllInjections(String s) {}", 421 " @Inject void s(String s) {}", 422 "}"); 423 CompilerTests.daggerCompiler(file) 424 .withProcessingOptions(compilerMode.processorOptions()) 425 .compile( 426 subject -> { 427 subject.hasErrorCount(0); 428 subject.generatedSource( 429 goldenFileRule.goldenSource("test/AllInjections_MembersInjector")); 430 }); 431 } 432 supertypeMembersInjection()433 @Test public void supertypeMembersInjection() { 434 Source aFile = 435 CompilerTests.javaSource( 436 "test.A", 437 "package test;", 438 "", 439 "class A {}"); 440 Source bFile = 441 CompilerTests.javaSource( 442 "test.B", 443 "package test;", 444 "", 445 "import javax.inject.Inject;", 446 "", 447 "class B extends A {", 448 " @Inject String s;", 449 "}"); 450 CompilerTests.daggerCompiler(aFile, bFile) 451 .withProcessingOptions(compilerMode.processorOptions()) 452 .compile( 453 subject -> { 454 subject.hasErrorCount(0); 455 subject.generatedSource(goldenFileRule.goldenSource("test/B_MembersInjector")); 456 }); 457 } 458 459 @Test simpleComponentWithNesting()460 public void simpleComponentWithNesting() { 461 Source nestedTypesFile = 462 CompilerTests.javaSource( 463 "test.OuterType", 464 "package test;", 465 "", 466 "import dagger.Component;", 467 "import javax.inject.Inject;", 468 "", 469 "final class OuterType {", 470 " static class A {", 471 " @Inject A() {}", 472 " }", 473 " static class B {", 474 " @Inject A a;", 475 " }", 476 " @Component interface SimpleComponent {", 477 " A a();", 478 " void inject(B b);", 479 " }", 480 "}"); 481 CompilerTests.daggerCompiler(nestedTypesFile) 482 .withProcessingOptions(compilerMode.processorOptions()) 483 .compile( 484 subject -> { 485 subject.hasErrorCount(0); 486 subject.generatedSource( 487 goldenFileRule.goldenSource("test/OuterType_B_MembersInjector")); 488 }); 489 } 490 491 @Test componentWithNestingAndGeneratedType()492 public void componentWithNestingAndGeneratedType() { 493 Source nestedTypesFile = 494 CompilerTests.javaSource( 495 "test.OuterType", 496 "package test;", 497 "", 498 "import dagger.Component;", 499 "import javax.inject.Inject;", 500 "", 501 "final class OuterType {", 502 " @Inject GeneratedInjectType generated;", 503 " static class A {", 504 " @Inject A() {}", 505 " }", 506 " static class B {", 507 " @Inject A a;", 508 " }", 509 " @Component interface SimpleComponent {", 510 " A a();", 511 " void inject(B b);", 512 " }", 513 "}"); 514 TypeSpec generatedInjectType = 515 TypeSpec.classBuilder("GeneratedInjectType") 516 .addMethod( 517 MethodSpec.constructorBuilder() 518 .addAnnotation(TypeNames.INJECT_JAVAX) 519 .build()) 520 .build(); 521 522 CompilerTests.daggerCompiler(nestedTypesFile) 523 .withProcessingOptions(compilerMode.processorOptions()) 524 .withProcessingSteps(() -> new GeneratingProcessingStep("test", generatedInjectType)) 525 .compile( 526 subject -> { 527 subject.hasErrorCount(0); 528 subject.generatedSource( 529 goldenFileRule.goldenSource("test/OuterType_B_MembersInjector")); 530 }); 531 } 532 533 @Test lowerCaseNamedMembersInjector_forLowerCaseType()534 public void lowerCaseNamedMembersInjector_forLowerCaseType() { 535 Source foo = 536 CompilerTests.javaSource( 537 "test.foo", 538 "package test;", 539 "", 540 "import javax.inject.Inject;", 541 "", 542 "class foo {", 543 " @Inject String string;", 544 "}"); 545 Source fooModule = 546 CompilerTests.javaSource( 547 "test.fooModule", 548 "package test;", 549 "", 550 "import dagger.Module;", 551 "import dagger.Provides;", 552 "", 553 "@Module", 554 "class fooModule {", 555 " @Provides String string() { return \"foo\"; }", 556 "}"); 557 Source fooComponent = 558 CompilerTests.javaSource( 559 "test.fooComponent", 560 "package test;", 561 "", 562 "import dagger.Component;", 563 "", 564 "@Component(modules = fooModule.class)", 565 "interface fooComponent {", 566 " void inject(foo target);", 567 "}"); 568 569 CompilerTests.daggerCompiler(foo, fooModule, fooComponent) 570 .withProcessingOptions(compilerMode.processorOptions()) 571 .compile( 572 subject -> { 573 subject.hasErrorCount(0); 574 subject.generatedSourceFileWithPath("test/foo_MembersInjector.java"); 575 }); 576 } 577 578 @Test fieldInjectionForShadowedMember()579 public void fieldInjectionForShadowedMember() { 580 Source foo = 581 CompilerTests.javaSource( 582 "test.Foo", 583 "package test;", 584 "", 585 "import javax.inject.Inject;", 586 "", 587 "class Foo {", 588 " @Inject Foo() {}", 589 "}"); 590 Source bar = 591 CompilerTests.javaSource( 592 "test.Bar", 593 "package test;", 594 "", 595 "import javax.inject.Inject;", 596 "", 597 "class Bar {", 598 " @Inject Bar() {}", 599 "}"); 600 Source parent = 601 CompilerTests.javaSource( 602 "test.Parent", 603 "package test;", 604 "", 605 "import javax.inject.Inject;", 606 "", 607 "class Parent { ", 608 " @Inject Foo object;", 609 "}"); 610 Source child = 611 CompilerTests.javaSource( 612 "test.Child", 613 "package test;", 614 "", 615 "import javax.inject.Inject;", 616 "", 617 "class Child extends Parent { ", 618 " @Inject Bar object;", 619 "}"); 620 Source component = 621 CompilerTests.javaSource( 622 "test.C", 623 "package test;", 624 "", 625 "import dagger.Component;", 626 "", 627 "@Component", 628 "interface C { ", 629 " void inject(Child child);", 630 "}"); 631 632 CompilerTests.daggerCompiler(foo, bar, parent, child, component) 633 .withProcessingOptions(compilerMode.processorOptions()) 634 .compile( 635 subject -> { 636 subject.hasErrorCount(0); 637 subject.generatedSource( 638 goldenFileRule.goldenSource("test/Child_MembersInjector")); 639 }); 640 } 641 privateNestedClassError()642 @Test public void privateNestedClassError() { 643 Source file = 644 CompilerTests.javaSource( 645 "test.OuterClass", 646 "package test;", 647 "", 648 "import javax.inject.Inject;", 649 "", 650 "final class OuterClass {", 651 " private static final class InnerClass {", 652 " @Inject int field;", 653 " }", 654 "}"); 655 CompilerTests.daggerCompiler(file) 656 .withProcessingOptions(compilerMode.processorOptions()) 657 .compile( 658 subject -> { 659 subject.hasErrorCount(1); 660 subject.hasErrorContaining("Dagger does not support injection into private classes") 661 .onSource(file) 662 .onLine(6); 663 }); 664 } 665 privateNestedClassWarning()666 @Test public void privateNestedClassWarning() { 667 Source file = 668 CompilerTests.javaSource( 669 "test.OuterClass", 670 "package test;", 671 "", 672 "import javax.inject.Inject;", 673 "", 674 "final class OuterClass {", 675 " private static final class InnerClass {", 676 " @Inject int field;", 677 " }", 678 "}"); 679 CompilerTests.daggerCompiler(file) 680 .withProcessingOptions( 681 ImmutableMap.<String, String>builder() 682 .putAll(compilerMode.processorOptions()) 683 .put("dagger.privateMemberValidation", "WARNING") 684 .buildOrThrow()) 685 .compile( 686 subject -> { 687 subject.hasErrorCount(0); 688 subject.hasWarningCount(1); 689 subject.hasWarningContaining("Dagger does not support injection into private classes") 690 .onSource(file) 691 .onLine(6); 692 }); 693 } 694 privateSuperclassIsOkIfNotInjectedInto()695 @Test public void privateSuperclassIsOkIfNotInjectedInto() { 696 Source file = 697 CompilerTests.javaSource( 698 "test.OuterClass", 699 "package test;", 700 "", 701 "import javax.inject.Inject;", 702 "", 703 "final class OuterClass {", 704 " private static class BaseClass {}", 705 "", 706 " static final class DerivedClass extends BaseClass {", 707 " @Inject int field;", 708 " }", 709 "}"); 710 CompilerTests.daggerCompiler(file) 711 .withProcessingOptions(compilerMode.processorOptions()) 712 .compile(subject -> subject.hasErrorCount(0)); 713 } 714 715 @Test rawFrameworkTypeField()716 public void rawFrameworkTypeField() { 717 Source file = 718 CompilerTests.javaSource( 719 "test.RawFrameworkTypes", 720 "package test;", 721 "", 722 "import javax.inject.Inject;", 723 "import javax.inject.Provider;", 724 "", 725 "class RawProviderField {", 726 " @Inject", 727 " Provider fieldWithRawProvider;", 728 "}"); 729 730 CompilerTests.daggerCompiler(file) 731 .withProcessingOptions(compilerMode.processorOptions()) 732 .compile( 733 subject -> { 734 subject.hasErrorCount(1); 735 subject.hasErrorContaining( 736 "Dagger does not support injecting raw type: javax.inject.Provider") 737 .onSource(file) 738 .onLineContaining("Provider fieldWithRawProvider"); 739 }); 740 } 741 742 @Test throwExceptionInjectedMethod()743 public void throwExceptionInjectedMethod() { 744 Source file = 745 CompilerTests.javaSource( 746 "test.", 747 "package test;", 748 "", 749 "import javax.inject.Inject;", 750 "class SomeClass {", 751 "@Inject void inject() throws Exception {}", 752 "}"); 753 754 CompilerTests.daggerCompiler(file) 755 .withProcessingOptions(compilerMode.processorOptions()) 756 .compile( 757 subject -> { 758 subject.hasErrorCount(1); 759 subject.hasErrorContaining( 760 "Methods with @Inject may not throw checked exceptions. " 761 + "Please wrap your exceptions in a RuntimeException instead.") 762 .onSource(file) 763 .onLineContaining("throws Exception"); 764 }); 765 } 766 767 @Test rawFrameworkMethodTypeParameter()768 public void rawFrameworkMethodTypeParameter() { 769 Source file = 770 CompilerTests.javaSource( 771 "test.RawFrameworkTypes", 772 "package test;", 773 "", 774 "import javax.inject.Inject;", 775 "import javax.inject.Provider;", 776 "", 777 "class RawProviderParameter {", 778 " @Inject", 779 " void methodInjection(", 780 " Provider rawProviderParameter) {}", 781 "}"); 782 783 CompilerTests.daggerCompiler(file) 784 .withProcessingOptions(compilerMode.processorOptions()) 785 .compile( 786 subject -> { 787 subject.hasErrorCount(1); 788 subject.hasErrorContaining( 789 "Dagger does not support injecting raw type: javax.inject.Provider") 790 .onSource(file) 791 .onLineContaining("Provider rawProviderParameter"); 792 }); 793 } 794 795 @Test rawFrameworkConstructorTypeParameter()796 public void rawFrameworkConstructorTypeParameter() { 797 Source file = 798 CompilerTests.javaSource( 799 "test.RawFrameworkTypes", 800 "package test;", 801 "", 802 "import dagger.Component;", 803 "import javax.inject.Inject;", 804 "import javax.inject.Provider;", 805 "", 806 "class RawProviderParameter {", 807 " @Inject", 808 " RawProviderParameter(", 809 " Provider rawProviderParameter) {}", 810 "}"); 811 812 CompilerTests.daggerCompiler(file) 813 .withProcessingOptions(compilerMode.processorOptions()) 814 .compile( 815 subject -> { 816 subject.hasErrorCount(1); 817 subject.hasErrorContaining( 818 "Dagger does not support injecting raw type: javax.inject.Provider") 819 .onSource(file) 820 .onLineContaining("Provider rawProviderParameter"); 821 }); 822 } 823 824 @Test injectsPrimitive()825 public void injectsPrimitive() throws Exception { 826 Source injectedType = 827 CompilerTests.javaSource( 828 "test.InjectedType", 829 "package test;", 830 "", 831 "import javax.inject.Inject;", 832 "", 833 "class InjectedType {", 834 " @Inject InjectedType() {}", 835 "", 836 " @Inject int primitiveInt;", 837 " @Inject Integer boxedInt;", 838 "}"); 839 840 CompilerTests.daggerCompiler(injectedType) 841 .withProcessingOptions(compilerMode.processorOptions()) 842 .compile( 843 subject -> { 844 subject.hasErrorCount(0); 845 subject.generatedSource( 846 goldenFileRule.goldenSource("test/InjectedType_MembersInjector")); 847 subject.generatedSource( 848 goldenFileRule.goldenSource("test/InjectedType_Factory")); 849 }); 850 } 851 852 @Test accessibility()853 public void accessibility() throws Exception { 854 Source foo = 855 CompilerTests.javaSource( 856 "other.Foo", 857 "package other;", 858 "", 859 "import javax.inject.Inject;", 860 "", 861 "class Foo {", 862 " @Inject Foo() {}", 863 "}"); 864 Source inaccessible = 865 CompilerTests.javaSource( 866 "other.Inaccessible", 867 "package other;", 868 "", 869 "import javax.inject.Inject;", 870 "", 871 "class Inaccessible {", 872 " @Inject Inaccessible() {}", 873 " @Inject Foo foo;", 874 " @Inject void method(Foo foo) {}", 875 "}"); 876 Source usesInaccessible = 877 CompilerTests.javaSource( 878 "other.UsesInaccessible", 879 "package other;", 880 "", 881 "import javax.inject.Inject;", 882 "", 883 "public class UsesInaccessible {", 884 " @Inject UsesInaccessible(Inaccessible inaccessible) {}", 885 "}"); 886 Source component = 887 CompilerTests.javaSource( 888 "test.TestComponent", 889 "package test;", 890 "", 891 "import dagger.Component;", 892 "import other.UsesInaccessible;", 893 "", 894 "@Component", 895 "interface TestComponent {", 896 " UsesInaccessible usesInaccessible();", 897 "}"); 898 899 CompilerTests.daggerCompiler(foo, inaccessible, usesInaccessible, component) 900 .withProcessingOptions(compilerMode.processorOptions()) 901 .compile( 902 subject -> { 903 subject.hasErrorCount(0); 904 subject.generatedSource( 905 goldenFileRule.goldenSource("other/Inaccessible_MembersInjector")); 906 subject.generatedSource( 907 goldenFileRule.goldenSource("test/DaggerTestComponent")); 908 }); 909 } 910 911 @Test accessibleRawType_ofInaccessibleType()912 public void accessibleRawType_ofInaccessibleType() throws Exception { 913 Source inaccessible = 914 CompilerTests.javaSource( 915 "other.Inaccessible", 916 "package other;", 917 "", 918 "class Inaccessible {}"); 919 Source inaccessiblesModule = 920 CompilerTests.javaSource( 921 "other.InaccessiblesModule", 922 "package other;", 923 "", 924 "import dagger.Module;", 925 "import dagger.Provides;", 926 "import java.util.ArrayList;", 927 "import java.util.List;", 928 "import javax.inject.Provider;", 929 "import javax.inject.Singleton;", 930 "", 931 "@Module", 932 "public class InaccessiblesModule {", 933 // force Provider initialization 934 " @Provides @Singleton static List<Inaccessible> inaccessibles() {", 935 " return new ArrayList<>();", 936 " }", 937 "}"); 938 Source usesInaccessibles = 939 CompilerTests.javaSource( 940 "other.UsesInaccessibles", 941 "package other;", 942 "", 943 "import java.util.List;", 944 "import javax.inject.Inject;", 945 "", 946 "public class UsesInaccessibles {", 947 " @Inject UsesInaccessibles() {}", 948 " @Inject List<Inaccessible> inaccessibles;", 949 "}"); 950 Source component = 951 CompilerTests.javaSource( 952 "test.TestComponent", 953 "package test;", 954 "", 955 "import dagger.Component;", 956 "import javax.inject.Singleton;", 957 "import other.UsesInaccessibles;", 958 "", 959 "@Singleton", 960 "@Component(modules = other.InaccessiblesModule.class)", 961 "interface TestComponent {", 962 " UsesInaccessibles usesInaccessibles();", 963 "}"); 964 965 CompilerTests.daggerCompiler(inaccessible, inaccessiblesModule, usesInaccessibles, component) 966 .withProcessingOptions(compilerMode.processorOptions()) 967 .compile( 968 subject -> { 969 subject.hasErrorCount(0); 970 subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); 971 }); 972 } 973 974 @Test publicSupertypeHiddenSubtype()975 public void publicSupertypeHiddenSubtype() throws Exception { 976 Source foo = 977 CompilerTests.javaSource( 978 "other.Foo", 979 "package other;", 980 "", 981 "import javax.inject.Inject;", 982 "", 983 "class Foo {", 984 " @Inject Foo() {}", 985 "}"); 986 Source supertype = 987 CompilerTests.javaSource( 988 "other.Supertype", 989 "package other;", 990 "", 991 "import javax.inject.Inject;", 992 "", 993 "public class Supertype<T> {", 994 " @Inject T t;", 995 "}"); 996 Source subtype = 997 CompilerTests.javaSource( 998 "other.Subtype", 999 "package other;", 1000 "", 1001 "import javax.inject.Inject;", 1002 "", 1003 "class Subtype extends Supertype<Foo> {", 1004 " @Inject Subtype() {}", 1005 "}"); 1006 Source injectsSubtype = 1007 CompilerTests.javaSource( 1008 "other.InjectsSubtype", 1009 "package other;", 1010 "", 1011 "import javax.inject.Inject;", 1012 "", 1013 "public class InjectsSubtype {", 1014 " @Inject InjectsSubtype(Subtype s) {}", 1015 "}"); 1016 Source component = 1017 CompilerTests.javaSource( 1018 "test.TestComponent", 1019 "package test;", 1020 "", 1021 "import dagger.Component;", 1022 "", 1023 "@Component", 1024 "interface TestComponent {", 1025 " other.InjectsSubtype injectsSubtype();", 1026 "}"); 1027 1028 CompilerTests.daggerCompiler(foo, supertype, subtype, injectsSubtype, component) 1029 .withProcessingOptions(compilerMode.processorOptions()) 1030 .compile( 1031 subject -> { 1032 subject.hasErrorCount(0); 1033 subject.generatedSource(goldenFileRule.goldenSource("test/DaggerTestComponent")); 1034 }); 1035 } 1036 1037 // Shows that we shouldn't create a members injector for a type that doesn't have 1038 // @Inject fields or @Inject constructor even if it extends and is extended by types that do. 1039 @Test middleClassNoFieldInjection()1040 public void middleClassNoFieldInjection() throws Exception { 1041 Source classA = 1042 CompilerTests.javaSource( 1043 "test.A", 1044 "package test;", 1045 "", 1046 "import javax.inject.Inject;", 1047 "", 1048 "class A extends B {", 1049 " @Inject String valueA;", 1050 "}"); 1051 Source classB = 1052 CompilerTests.javaSource( 1053 "test.B", 1054 "package test;", 1055 "", 1056 "class B extends C {", 1057 "}"); 1058 Source classC = 1059 CompilerTests.javaSource( 1060 "test.C", 1061 "package test;", 1062 "", 1063 "import javax.inject.Inject;", 1064 "", 1065 "class C { ", 1066 " @Inject String valueC;", 1067 "}"); 1068 1069 CompilerTests.daggerCompiler(classA, classB, classC) 1070 .withProcessingOptions(compilerMode.processorOptions()) 1071 .compile( 1072 subject -> { 1073 subject.hasErrorCount(0); 1074 subject.generatedSource(goldenFileRule.goldenSource("test/A_MembersInjector")); 1075 subject.generatedSource(goldenFileRule.goldenSource("test/C_MembersInjector")); 1076 1077 try { 1078 subject.generatedSourceFileWithPath("test/B_MembersInjector"); 1079 // Can't throw an assertion error since it would be caught. 1080 throw new IllegalStateException("Test generated a B_MembersInjector"); 1081 } catch (AssertionError expected) {} 1082 }); 1083 } 1084 1085 // Shows that we do generate a MembersInjector for a type that has an @Inject 1086 // constructor and that extends a type with @Inject fields, even if it has no local field 1087 // injection sites 1088 // TODO(erichang): Are these even used anymore? 1089 @Test testConstructorInjectedFieldInjection()1090 public void testConstructorInjectedFieldInjection() throws Exception { 1091 Source classA = 1092 CompilerTests.javaSource( 1093 "test.A", 1094 "package test;", 1095 "", 1096 "import javax.inject.Inject;", 1097 "", 1098 "class A extends B {", 1099 " @Inject A() {}", 1100 "}"); 1101 Source classB = 1102 CompilerTests.javaSource( 1103 "test.B", 1104 "package test;", 1105 "", 1106 "import javax.inject.Inject;", 1107 "", 1108 "class B { ", 1109 " @Inject String valueB;", 1110 "}"); 1111 1112 CompilerTests.daggerCompiler(classA, classB) 1113 .withProcessingOptions(compilerMode.processorOptions()) 1114 .compile( 1115 subject -> { 1116 subject.hasErrorCount(0); 1117 subject.generatedSource(goldenFileRule.goldenSource("test/A_MembersInjector")); 1118 subject.generatedSource(goldenFileRule.goldenSource("test/B_MembersInjector")); 1119 }); 1120 } 1121 1122 // Regression test for https://github.com/google/dagger/issues/3143 1123 @Test testMembersInjectionBindingExistsInParentComponent()1124 public void testMembersInjectionBindingExistsInParentComponent() throws Exception { 1125 Source component = 1126 CompilerTests.javaSource( 1127 "test.MyComponent", 1128 "package test;", 1129 "", 1130 "import dagger.Component;", 1131 "", 1132 "@Component(modules = MyComponentModule.class)", 1133 "public interface MyComponent {", 1134 " void inject(Bar bar);", 1135 "", 1136 " MySubcomponent subcomponent();", 1137 "}"); 1138 1139 Source subcomponent = 1140 CompilerTests.javaSource( 1141 "test.MySubcomponent", 1142 "package test;", 1143 "", 1144 "import dagger.Subcomponent;", 1145 "", 1146 "@Subcomponent(modules = MySubcomponentModule.class)", 1147 "interface MySubcomponent {", 1148 " Foo foo();", 1149 "}"); 1150 1151 Source foo = 1152 CompilerTests.javaSource( 1153 "test.Foo", 1154 "package test;", 1155 "", 1156 "import javax.inject.Inject;", 1157 "", 1158 "class Foo {", 1159 " @Inject Foo(Bar bar) {}", 1160 "}"); 1161 1162 Source bar = 1163 CompilerTests.javaSource( 1164 "test.Bar", 1165 "package test;", 1166 "", 1167 "import java.util.Set;", 1168 "import javax.inject.Inject;", 1169 "", 1170 "class Bar {", 1171 " @Inject Set<String> multibindingStrings;", 1172 " @Inject Bar() {}", 1173 "}"); 1174 1175 Source componentModule = 1176 CompilerTests.javaSource( 1177 "test.MyComponentModule", 1178 "package test;", 1179 "", 1180 "import dagger.Module;", 1181 "import dagger.Provides;", 1182 "import dagger.multibindings.IntoSet;", 1183 "", 1184 "@Module", 1185 "interface MyComponentModule {", 1186 " @Provides", 1187 " @IntoSet", 1188 " static String provideString() {", 1189 " return \"\";", 1190 " }", 1191 "}"); 1192 1193 Source subcomponentModule = 1194 CompilerTests.javaSource( 1195 "test.MySubcomponentModule", 1196 "package test;", 1197 "", 1198 "import dagger.Module;", 1199 "import dagger.Provides;", 1200 "import dagger.multibindings.IntoSet;", 1201 "", 1202 "@Module", 1203 "interface MySubcomponentModule {", 1204 " @Provides", 1205 " @IntoSet", 1206 " static String provideString() {", 1207 " return \"\";", 1208 " }", 1209 "}"); 1210 1211 CompilerTests.daggerCompiler( 1212 component, subcomponent, foo, bar, componentModule, subcomponentModule) 1213 .withProcessingOptions(compilerMode.processorOptions()) 1214 .compile( 1215 subject -> { 1216 subject.hasErrorCount(0); 1217 // Check that the injectBar() method is not shared across components. 1218 // We avoid sharing them in general because they may be different (e.g. in this case 1219 // we inject multibindings that are different across components). 1220 subject.generatedSource(goldenFileRule.goldenSource("test/DaggerMyComponent")); 1221 }); 1222 1223 } 1224 1225 // Test that if both a MembersInjectionBinding and ProvisionBinding both exist in the same 1226 // component they share the same inject methods rather than generating their own. 1227 @Test testMembersInjectionBindingSharesInjectMethodsWithProvisionBinding()1228 public void testMembersInjectionBindingSharesInjectMethodsWithProvisionBinding() 1229 throws Exception { 1230 Source component = 1231 CompilerTests.javaSource( 1232 "test.MyComponent", 1233 "package test;", 1234 "", 1235 "import dagger.Component;", 1236 "", 1237 "@Component", 1238 "public interface MyComponent {", 1239 " Foo foo();", 1240 "", 1241 " void inject(Foo foo);", 1242 "}"); 1243 1244 Source foo = 1245 CompilerTests.javaSource( 1246 "test.Foo", 1247 "package test;", 1248 "", 1249 "import javax.inject.Inject;", 1250 "", 1251 "class Foo {", 1252 " @Inject Bar bar;", 1253 " @Inject Foo() {}", 1254 "}"); 1255 1256 Source bar = 1257 CompilerTests.javaSource( 1258 "test.Bar", 1259 "package test;", 1260 "", 1261 "import javax.inject.Inject;", 1262 "", 1263 "class Bar {", 1264 " @Inject Bar() {}", 1265 "}"); 1266 CompilerTests.daggerCompiler(component, foo, bar) 1267 .withProcessingOptions(compilerMode.processorOptions()) 1268 .compile( 1269 subject -> { 1270 subject.hasErrorCount(0); 1271 subject.generatedSource(goldenFileRule.goldenSource("test/DaggerMyComponent")); 1272 }); 1273 } 1274 } 1275