1 /* 2 * Copyright (C) 2014 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.CompilerMode.DEFAULT_MODE; 22 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; 23 import static dagger.internal.codegen.Compilers.daggerCompiler; 24 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION; 25 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION; 26 import static dagger.internal.codegen.GeneratedLines.NPE_FROM_COMPONENT_METHOD; 27 import static dagger.internal.codegen.GeneratedLines.NPE_FROM_PROVIDES_METHOD; 28 29 import com.google.auto.common.MoreElements; 30 import com.google.common.base.Predicate; 31 import com.google.common.base.Predicates; 32 import com.google.common.collect.Sets; 33 import com.google.testing.compile.Compilation; 34 import com.google.testing.compile.JavaFileObjects; 35 import dagger.MembersInjector; 36 import java.lang.annotation.Annotation; 37 import java.util.Collection; 38 import java.util.Set; 39 import javax.annotation.processing.AbstractProcessor; 40 import javax.annotation.processing.ProcessingEnvironment; 41 import javax.annotation.processing.RoundEnvironment; 42 import javax.inject.Inject; 43 import javax.lang.model.SourceVersion; 44 import javax.lang.model.element.Element; 45 import javax.lang.model.element.TypeElement; 46 import javax.tools.JavaFileObject; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.junit.runners.Parameterized; 50 import org.junit.runners.Parameterized.Parameters; 51 52 @RunWith(Parameterized.class) 53 public class ComponentProcessorTest { 54 @Parameters(name = "{0}") parameters()55 public static Collection<Object[]> parameters() { 56 return CompilerMode.TEST_PARAMETERS; 57 } 58 59 private final CompilerMode compilerMode; 60 ComponentProcessorTest(CompilerMode compilerMode)61 public ComponentProcessorTest(CompilerMode compilerMode) { 62 this.compilerMode = compilerMode; 63 } 64 doubleBindingFromResolvedModules()65 @Test public void doubleBindingFromResolvedModules() { 66 JavaFileObject parent = JavaFileObjects.forSourceLines("test.ParentModule", 67 "package test;", 68 "", 69 "import dagger.Module;", 70 "import dagger.Provides;", 71 "import java.util.List;", 72 "", 73 "@Module", 74 "abstract class ParentModule<A> {", 75 " @Provides List<A> provideListB(A a) { return null; }", 76 "}"); 77 JavaFileObject child = JavaFileObjects.forSourceLines("test.ChildModule", 78 "package test;", 79 "", 80 "import dagger.Module;", 81 "import dagger.Provides;", 82 "", 83 "@Module", 84 "class ChildNumberModule extends ParentModule<Integer> {", 85 " @Provides Integer provideInteger() { return null; }", 86 "}"); 87 JavaFileObject another = JavaFileObjects.forSourceLines("test.AnotherModule", 88 "package test;", 89 "", 90 "import dagger.Module;", 91 "import dagger.Provides;", 92 "import java.util.List;", 93 "", 94 "@Module", 95 "class AnotherModule {", 96 " @Provides List<Integer> provideListOfInteger() { return null; }", 97 "}"); 98 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent", 99 "package test;", 100 "", 101 "import dagger.Component;", 102 "import java.util.List;", 103 "", 104 "@Component(modules = {ChildNumberModule.class, AnotherModule.class})", 105 "interface BadComponent {", 106 " List<Integer> listOfInteger();", 107 "}"); 108 109 Compilation compilation = 110 daggerCompiler() 111 .withOptions(compilerMode.javacopts()) 112 .compile(parent, child, another, componentFile); 113 assertThat(compilation).failed(); 114 assertThat(compilation) 115 .hadErrorContaining("java.util.List<java.lang.Integer> is bound multiple times"); 116 assertThat(compilation) 117 .hadErrorContaining("@Provides List<Integer> test.ChildNumberModule.provideListB(Integer)"); 118 assertThat(compilation) 119 .hadErrorContaining("@Provides List<Integer> test.AnotherModule.provideListOfInteger()"); 120 } 121 privateNestedClassWithWarningThatIsAnErrorInComponent()122 @Test public void privateNestedClassWithWarningThatIsAnErrorInComponent() { 123 JavaFileObject outerClass = JavaFileObjects.forSourceLines("test.OuterClass", 124 "package test;", 125 "", 126 "import javax.inject.Inject;", 127 "", 128 "final class OuterClass {", 129 " @Inject OuterClass(InnerClass innerClass) {}", 130 "", 131 " private static final class InnerClass {", 132 " @Inject InnerClass() {}", 133 " }", 134 "}"); 135 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent", 136 "package test;", 137 "", 138 "import dagger.Component;", 139 "", 140 "@Component", 141 "interface BadComponent {", 142 " OuterClass outerClass();", 143 "}"); 144 Compilation compilation = 145 daggerCompiler() 146 .withOptions( 147 compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING")) 148 .compile(outerClass, componentFile); 149 assertThat(compilation).failed(); 150 assertThat(compilation) 151 .hadErrorContaining("Dagger does not support injection into private classes"); 152 } 153 simpleComponent()154 @Test public void simpleComponent() { 155 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 156 "package test;", 157 "", 158 "import javax.inject.Inject;", 159 "", 160 "final class SomeInjectableType {", 161 " @Inject SomeInjectableType() {}", 162 "}"); 163 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 164 "package test;", 165 "", 166 "import dagger.Component;", 167 "import dagger.Lazy;", 168 "import javax.inject.Provider;", 169 "", 170 "@Component", 171 "interface SimpleComponent {", 172 " SomeInjectableType someInjectableType();", 173 " Lazy<SomeInjectableType> lazySomeInjectableType();", 174 " Provider<SomeInjectableType> someInjectableTypeProvider();", 175 "}"); 176 177 JavaFileObject generatedComponent = 178 compilerMode 179 .javaFileBuilder("test.DaggerSimpleComponent") 180 .addLines( 181 "package test;", 182 "", 183 "import dagger.Lazy;", 184 "import dagger.internal.DoubleCheck;", 185 IMPORT_GENERATED_ANNOTATION, 186 "import javax.inject.Provider;", 187 "", 188 GENERATED_ANNOTATION, 189 "final class DaggerSimpleComponent implements SimpleComponent {") 190 .addLinesIn( 191 FAST_INIT_MODE, 192 " private volatile Provider<SomeInjectableType> someInjectableTypeProvider;") 193 .addLines( 194 " private DaggerSimpleComponent() {}", 195 "", 196 " public static Builder builder() {", 197 " return new Builder();", 198 " }", 199 "", 200 " public static SimpleComponent create() {", 201 " return new Builder().build();", 202 " }", 203 "", 204 " @Override", 205 " public SomeInjectableType someInjectableType() {", 206 " return new SomeInjectableType();", 207 " }", 208 "", 209 " @Override", 210 " public Lazy<SomeInjectableType> lazySomeInjectableType() {") 211 .addLinesIn( 212 DEFAULT_MODE, // 213 " return DoubleCheck.lazy(SomeInjectableType_Factory.create());") 214 .addLinesIn( 215 FAST_INIT_MODE, 216 " return DoubleCheck.lazy(someInjectableTypeProvider());") 217 .addLines( 218 " }", 219 "", 220 " @Override", 221 " public Provider<SomeInjectableType> someInjectableTypeProvider() {") 222 .addLinesIn( 223 DEFAULT_MODE, // 224 " return SomeInjectableType_Factory.create();") 225 .addLinesIn( 226 FAST_INIT_MODE, // 227 " Object local = someInjectableTypeProvider;", 228 " if (local == null) {", 229 " local = new SwitchingProvider<>(0);", 230 " someInjectableTypeProvider = (Provider<SomeInjectableType>) local;", 231 " }", 232 " return (Provider<SomeInjectableType>) local;") 233 .addLines( 234 " }", 235 "", 236 " static final class Builder {", 237 " private Builder() {}", 238 "", 239 " public SimpleComponent build() {", 240 " return new DaggerSimpleComponent();", 241 " }", 242 " }") 243 .addLinesIn( 244 FAST_INIT_MODE, 245 " private final class SwitchingProvider<T> implements Provider<T> {", 246 " private final int id;", 247 "", 248 " SwitchingProvider(int id) {", 249 " this.id = id;", 250 " }", 251 "", 252 " @SuppressWarnings(\"unchecked\")", 253 " @Override", 254 " public T get() {", 255 " switch (id) {", 256 " case 0: return (T) new SomeInjectableType();", 257 " default: throw new AssertionError(id);", 258 " }", 259 " }", 260 " }") 261 .build(); 262 263 Compilation compilation = 264 daggerCompiler() 265 .withOptions(compilerMode.javacopts()) 266 .compile(injectableTypeFile, componentFile); 267 assertThat(compilation).succeeded(); 268 assertThat(compilation) 269 .generatedSourceFile("test.DaggerSimpleComponent") 270 .hasSourceEquivalentTo(generatedComponent); 271 } 272 componentWithScope()273 @Test public void componentWithScope() { 274 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 275 "package test;", 276 "", 277 "import javax.inject.Inject;", 278 "import javax.inject.Singleton;", 279 "", 280 "@Singleton", 281 "final class SomeInjectableType {", 282 " @Inject SomeInjectableType() {}", 283 "}"); 284 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 285 "package test;", 286 "", 287 "import dagger.Component;", 288 "import dagger.Lazy;", 289 "import javax.inject.Provider;", 290 "import javax.inject.Singleton;", 291 "", 292 "@Singleton", 293 "@Component", 294 "interface SimpleComponent {", 295 " SomeInjectableType someInjectableType();", 296 " Lazy<SomeInjectableType> lazySomeInjectableType();", 297 " Provider<SomeInjectableType> someInjectableTypeProvider();", 298 "}"); 299 JavaFileObject generatedComponent = 300 compilerMode 301 .javaFileBuilder("test.DaggerSimpleComponent") 302 .addLines( 303 "package test;", 304 "", 305 GENERATED_ANNOTATION, 306 "final class DaggerSimpleComponent implements SimpleComponent {") 307 .addLinesIn( 308 FAST_INIT_MODE, 309 " private volatile Object someInjectableType = new MemoizedSentinel();", 310 " private volatile Provider<SomeInjectableType> someInjectableTypeProvider;") 311 .addLinesIn( 312 DEFAULT_MODE, 313 " private Provider<SomeInjectableType> someInjectableTypeProvider;", 314 "", 315 " @SuppressWarnings(\"unchecked\")", 316 " private void initialize() {", 317 " this.someInjectableTypeProvider =", 318 " DoubleCheck.provider(SomeInjectableType_Factory.create());", 319 " }", 320 "") 321 .addLines( 322 " @Override", // 323 " public SomeInjectableType someInjectableType() {") 324 .addLinesIn( 325 FAST_INIT_MODE, 326 " Object local = someInjectableType;", 327 " if (local instanceof MemoizedSentinel) {", 328 " synchronized (local) {", 329 " local = someInjectableType;", 330 " if (local instanceof MemoizedSentinel) {", 331 " local = new SomeInjectableType();", 332 " someInjectableType =", 333 " DoubleCheck.reentrantCheck(someInjectableType, local);", 334 " }", 335 " }", 336 " }", 337 " return (SomeInjectableType) local;") 338 .addLinesIn( 339 DEFAULT_MODE, // 340 " return someInjectableTypeProvider.get();") 341 .addLines( 342 " }", 343 "", 344 " @Override", 345 " public Lazy<SomeInjectableType> lazySomeInjectableType() {") 346 .addLinesIn( 347 DEFAULT_MODE, // 348 " return DoubleCheck.lazy(someInjectableTypeProvider);") 349 .addLinesIn( 350 FAST_INIT_MODE, 351 " return DoubleCheck.lazy(someInjectableTypeProvider());") 352 .addLines( 353 " }", 354 "", 355 " @Override", 356 " public Provider<SomeInjectableType> someInjectableTypeProvider() {") 357 .addLinesIn( 358 FAST_INIT_MODE, // 359 " Object local = someInjectableTypeProvider;", 360 " if (local == null) {", 361 " local = new SwitchingProvider<>(0);", 362 " someInjectableTypeProvider = (Provider<SomeInjectableType>) local;", 363 " }", 364 " return (Provider<SomeInjectableType>) local;") 365 .addLinesIn( 366 DEFAULT_MODE, // 367 " return someInjectableTypeProvider;") 368 .addLines( // 369 " }") 370 .addLinesIn( 371 FAST_INIT_MODE, 372 " private final class SwitchingProvider<T> implements Provider<T> {", 373 " private final int id;", 374 "", 375 " SwitchingProvider(int id) {", 376 " this.id = id;", 377 " }", 378 "", 379 " @SuppressWarnings(\"unchecked\")", 380 " @Override", 381 " public T get() {", 382 " switch (id) {", 383 " case 0: return (T) DaggerSimpleComponent.this.someInjectableType();", 384 " default: throw new AssertionError(id);", 385 " }", 386 " }", 387 " }") 388 .build(); 389 Compilation compilation = 390 daggerCompiler() 391 .withOptions(compilerMode.javacopts()) 392 .compile(injectableTypeFile, componentFile); 393 assertThat(compilation).succeeded(); 394 assertThat(compilation) 395 .generatedSourceFile("test.DaggerSimpleComponent") 396 .containsElementsIn(generatedComponent); 397 } 398 simpleComponentWithNesting()399 @Test public void simpleComponentWithNesting() { 400 JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType", 401 "package test;", 402 "", 403 "import dagger.Component;", 404 "import javax.inject.Inject;", 405 "", 406 "final class OuterType {", 407 " static class A {", 408 " @Inject A() {}", 409 " }", 410 " static class B {", 411 " @Inject A a;", 412 " }", 413 " @Component interface SimpleComponent {", 414 " A a();", 415 " void inject(B b);", 416 " }", 417 "}"); 418 419 JavaFileObject generatedComponent = 420 compilerMode 421 .javaFileBuilder("test.DaggerOuterType_SimpleComponent") 422 .addLines( 423 "package test;", 424 "", 425 GENERATED_ANNOTATION, 426 "final class DaggerOuterType_SimpleComponent", 427 " implements OuterType.SimpleComponent {", 428 " private DaggerOuterType_SimpleComponent() {}", 429 "", 430 " @Override", 431 " public OuterType.A a() {", 432 " return new OuterType.A();", 433 " }", 434 "", 435 " @Override", 436 " public void inject(OuterType.B b) {", 437 " injectB(b);", 438 " }", 439 "", 440 " @CanIgnoreReturnValue", 441 " private OuterType.B injectB(OuterType.B instance) {", 442 " OuterType_B_MembersInjector.injectA(instance, new OuterType.A());", 443 " return instance;", 444 " }", 445 "}") 446 .build(); 447 448 Compilation compilation = 449 daggerCompiler().withOptions(compilerMode.javacopts()).compile(nestedTypesFile); 450 assertThat(compilation).succeeded(); 451 assertThat(compilation) 452 .generatedSourceFile("test.DaggerOuterType_SimpleComponent") 453 .containsElementsIn(generatedComponent); 454 } 455 componentWithModule()456 @Test public void componentWithModule() { 457 JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A", 458 "package test;", 459 "", 460 "import javax.inject.Inject;", 461 "", 462 "final class A {", 463 " @Inject A(B b) {}", 464 "}"); 465 JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B", 466 "package test;", 467 "", 468 "interface B {}"); 469 JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C", 470 "package test;", 471 "", 472 "import javax.inject.Inject;", 473 "", 474 "final class C {", 475 " @Inject C() {}", 476 "}"); 477 478 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", 479 "package test;", 480 "", 481 "import dagger.Module;", 482 "import dagger.Provides;", 483 "", 484 "@Module", 485 "final class TestModule {", 486 " @Provides B b(C c) { return null; }", 487 "}"); 488 489 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 490 "package test;", 491 "", 492 "import dagger.Component;", 493 "import javax.inject.Provider;", 494 "", 495 "@Component(modules = TestModule.class)", 496 "interface TestComponent {", 497 " A a();", 498 "}"); 499 500 JavaFileObject generatedComponent = 501 compilerMode 502 .javaFileBuilder("test.DaggerTestComponent") 503 .addLines( 504 "package test;", 505 "", 506 "import dagger.internal.Preconditions;", 507 IMPORT_GENERATED_ANNOTATION, 508 "", 509 GENERATED_ANNOTATION, 510 "final class DaggerTestComponent implements TestComponent {", 511 " private final TestModule testModule;", 512 "", 513 " private DaggerTestComponent(TestModule testModuleParam) {", 514 " this.testModule = testModuleParam;", 515 " }", 516 "", 517 " private B getB() {", 518 " return TestModule_BFactory.b(testModule, new C());", 519 " }", 520 "", 521 " @Override", 522 " public A a() {", 523 " return new A(getB());", 524 " }", 525 "", 526 " static final class Builder {", 527 " private TestModule testModule;", 528 "", 529 " public Builder testModule(TestModule testModule) {", 530 " this.testModule = Preconditions.checkNotNull(testModule);", 531 " return this;", 532 " }", 533 "", 534 " public TestComponent build() {", 535 " if (testModule == null) {", 536 " this.testModule = new TestModule();", 537 " }", 538 " return new DaggerTestComponent(testModule);", 539 " }", 540 " }", 541 "}") 542 .build(); 543 544 Compilation compilation = 545 daggerCompiler() 546 .withOptions(compilerMode.javacopts()) 547 .compile(aFile, bFile, cFile, moduleFile, componentFile); 548 assertThat(compilation).succeeded(); 549 assertThat(compilation) 550 .generatedSourceFile("test.DaggerTestComponent") 551 .containsElementsIn(generatedComponent); 552 } 553 554 @Test componentWithAbstractModule()555 public void componentWithAbstractModule() { 556 JavaFileObject aFile = 557 JavaFileObjects.forSourceLines( 558 "test.A", 559 "package test;", 560 "", 561 "import javax.inject.Inject;", 562 "", 563 "final class A {", 564 " @Inject A(B b) {}", 565 "}"); 566 JavaFileObject bFile = 567 JavaFileObjects.forSourceLines("test.B", 568 "package test;", 569 "", 570 "interface B {}"); 571 JavaFileObject cFile = 572 JavaFileObjects.forSourceLines( 573 "test.C", 574 "package test;", 575 "", 576 "import javax.inject.Inject;", 577 "", 578 "final class C {", 579 " @Inject C() {}", 580 "}"); 581 582 JavaFileObject moduleFile = 583 JavaFileObjects.forSourceLines( 584 "test.TestModule", 585 "package test;", 586 "", 587 "import dagger.Module;", 588 "import dagger.Provides;", 589 "", 590 "@Module", 591 "abstract class TestModule {", 592 " @Provides static B b(C c) { return null; }", 593 "}"); 594 595 JavaFileObject componentFile = 596 JavaFileObjects.forSourceLines( 597 "test.TestComponent", 598 "package test;", 599 "", 600 "import dagger.Component;", 601 "", 602 "@Component(modules = TestModule.class)", 603 "interface TestComponent {", 604 " A a();", 605 "}"); 606 607 JavaFileObject generatedComponent = 608 compilerMode 609 .javaFileBuilder("test.DaggerTestComponent") 610 .addLines( 611 "package test;", 612 "", 613 GENERATED_ANNOTATION, 614 "final class DaggerTestComponent implements TestComponent {", 615 " private B getB() {", 616 " return TestModule_BFactory.b(new C());", 617 " }", 618 "", 619 " @Override", 620 " public A a() {", 621 " return new A(getB());", 622 " }", 623 "}") 624 .build(); 625 626 Compilation compilation = 627 daggerCompiler() 628 .withOptions(compilerMode.javacopts()) 629 .compile(aFile, bFile, cFile, moduleFile, componentFile); 630 assertThat(compilation).succeeded(); 631 assertThat(compilation) 632 .generatedSourceFile("test.DaggerTestComponent") 633 .containsElementsIn(generatedComponent); 634 } 635 transitiveModuleDeps()636 @Test public void transitiveModuleDeps() { 637 JavaFileObject always = JavaFileObjects.forSourceLines("test.AlwaysIncluded", 638 "package test;", 639 "", 640 "import dagger.Module;", 641 "", 642 "@Module", 643 "final class AlwaysIncluded {}"); 644 JavaFileObject testModule = JavaFileObjects.forSourceLines("test.TestModule", 645 "package test;", 646 "", 647 "import dagger.Module;", 648 "", 649 "@Module(includes = {DepModule.class, AlwaysIncluded.class})", 650 "final class TestModule extends ParentTestModule {}"); 651 JavaFileObject parentTest = JavaFileObjects.forSourceLines("test.ParentTestModule", 652 "package test;", 653 "", 654 "import dagger.Module;", 655 "", 656 "@Module(includes = {ParentTestIncluded.class, AlwaysIncluded.class})", 657 "class ParentTestModule {}"); 658 JavaFileObject parentTestIncluded = JavaFileObjects.forSourceLines("test.ParentTestIncluded", 659 "package test;", 660 "", 661 "import dagger.Module;", 662 "", 663 "@Module(includes = AlwaysIncluded.class)", 664 "final class ParentTestIncluded {}"); 665 JavaFileObject depModule = JavaFileObjects.forSourceLines("test.TestModule", 666 "package test;", 667 "", 668 "import dagger.Module;", 669 "", 670 "@Module(includes = {RefByDep.class, AlwaysIncluded.class})", 671 "final class DepModule extends ParentDepModule {}"); 672 JavaFileObject refByDep = JavaFileObjects.forSourceLines("test.RefByDep", 673 "package test;", 674 "", 675 "import dagger.Module;", 676 "", 677 "@Module(includes = AlwaysIncluded.class)", 678 "final class RefByDep extends ParentDepModule {}"); 679 JavaFileObject parentDep = JavaFileObjects.forSourceLines("test.ParentDepModule", 680 "package test;", 681 "", 682 "import dagger.Module;", 683 "", 684 "@Module(includes = {ParentDepIncluded.class, AlwaysIncluded.class})", 685 "class ParentDepModule {}"); 686 JavaFileObject parentDepIncluded = JavaFileObjects.forSourceLines("test.ParentDepIncluded", 687 "package test;", 688 "", 689 "import dagger.Module;", 690 "", 691 "@Module(includes = AlwaysIncluded.class)", 692 "final class ParentDepIncluded {}"); 693 694 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 695 "package test;", 696 "", 697 "import dagger.Component;", 698 "", 699 "@Component(modules = TestModule.class)", 700 "interface TestComponent {", 701 "}"); 702 // Generated code includes all includes, but excludes the parent modules. 703 // The "always" module should only be listed once. 704 JavaFileObject generatedComponent = JavaFileObjects.forSourceLines( 705 "test.DaggerTestComponent", 706 "package test;", 707 "", 708 "import dagger.internal.Preconditions;", 709 IMPORT_GENERATED_ANNOTATION, 710 "", 711 GENERATED_ANNOTATION, 712 "final class DaggerTestComponent implements TestComponent {", 713 " static final class Builder {", 714 "", 715 " @Deprecated", 716 " public Builder testModule(TestModule testModule) {", 717 " Preconditions.checkNotNull(testModule)", 718 " return this;", 719 " }", 720 "", 721 " @Deprecated", 722 " public Builder parentTestIncluded(ParentTestIncluded parentTestIncluded) {", 723 " Preconditions.checkNotNull(parentTestIncluded)", 724 " return this;", 725 " }", 726 "", 727 " @Deprecated", 728 " public Builder alwaysIncluded(AlwaysIncluded alwaysIncluded) {", 729 " Preconditions.checkNotNull(alwaysIncluded)", 730 " return this;", 731 " }", 732 "", 733 " @Deprecated", 734 " public Builder depModule(DepModule depModule) {", 735 " Preconditions.checkNotNull(depModule)", 736 " return this;", 737 " }", 738 "", 739 " @Deprecated", 740 " public Builder parentDepIncluded(ParentDepIncluded parentDepIncluded) {", 741 " Preconditions.checkNotNull(parentDepIncluded)", 742 " return this;", 743 " }", 744 "", 745 " @Deprecated", 746 " public Builder refByDep(RefByDep refByDep) {", 747 " Preconditions.checkNotNull(refByDep)", 748 " return this;", 749 " }", 750 "", 751 " public TestComponent build() {", 752 " return new DaggerTestComponent();", 753 " }", 754 " }", 755 "}"); 756 Compilation compilation = 757 daggerCompiler() 758 .withOptions(compilerMode.javacopts()) 759 .compile( 760 always, 761 testModule, 762 parentTest, 763 parentTestIncluded, 764 depModule, 765 refByDep, 766 parentDep, 767 parentDepIncluded, 768 componentFile); 769 assertThat(compilation).succeeded(); 770 assertThat(compilation) 771 .generatedSourceFile("test.DaggerTestComponent") 772 .containsElementsIn(generatedComponent); 773 } 774 775 @Test generatedTransitiveModule()776 public void generatedTransitiveModule() { 777 JavaFileObject rootModule = JavaFileObjects.forSourceLines("test.RootModule", 778 "package test;", 779 "", 780 "import dagger.Module;", 781 "", 782 "@Module(includes = GeneratedModule.class)", 783 "final class RootModule {}"); 784 JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent", 785 "package test;", 786 "", 787 "import dagger.Component;", 788 "", 789 "@Component(modules = RootModule.class)", 790 "interface TestComponent {}"); 791 assertThat( 792 daggerCompiler().withOptions(compilerMode.javacopts()).compile(rootModule, component)) 793 .failed(); 794 assertThat( 795 daggerCompiler( 796 new GeneratingProcessor( 797 "test.GeneratedModule", 798 "package test;", 799 "", 800 "import dagger.Module;", 801 "", 802 "@Module", 803 "final class GeneratedModule {}")) 804 .compile(rootModule, component)) 805 .succeeded(); 806 } 807 808 @Test generatedModuleInSubcomponent()809 public void generatedModuleInSubcomponent() { 810 JavaFileObject subcomponent = 811 JavaFileObjects.forSourceLines( 812 "test.ChildComponent", 813 "package test;", 814 "", 815 "import dagger.Subcomponent;", 816 "", 817 "@Subcomponent(modules = GeneratedModule.class)", 818 "interface ChildComponent {}"); 819 JavaFileObject component = 820 JavaFileObjects.forSourceLines( 821 "test.TestComponent", 822 "package test;", 823 "", 824 "import dagger.Component;", 825 "", 826 "@Component", 827 "interface TestComponent {", 828 " ChildComponent childComponent();", 829 "}"); 830 assertThat( 831 daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponent, component)) 832 .failed(); 833 assertThat( 834 daggerCompiler( 835 new GeneratingProcessor( 836 "test.GeneratedModule", 837 "package test;", 838 "", 839 "import dagger.Module;", 840 "", 841 "@Module", 842 "final class GeneratedModule {}")) 843 .compile(subcomponent, component)) 844 .succeeded(); 845 } 846 847 @Test subcomponentNotGeneratedIfNotUsedInGraph()848 public void subcomponentNotGeneratedIfNotUsedInGraph() { 849 JavaFileObject component = 850 JavaFileObjects.forSourceLines( 851 "test.Parent", 852 "package test;", 853 "", 854 "import dagger.Component;", 855 "", 856 "@Component(modules = ParentModule.class)", 857 "interface Parent {", 858 " String notSubcomponent();", 859 "}"); 860 JavaFileObject module = 861 JavaFileObjects.forSourceLines( 862 "test.Parent", 863 "package test;", 864 "", 865 "import dagger.Module;", 866 "import dagger.Provides;", 867 "", 868 "@Module(subcomponents = Child.class)", 869 "class ParentModule {", 870 " @Provides static String notSubcomponent() { return new String(); }", 871 "}"); 872 873 JavaFileObject subcomponent = 874 JavaFileObjects.forSourceLines( 875 "test.Child", 876 "package test;", 877 "", 878 "import dagger.Subcomponent;", 879 "", 880 "@Subcomponent", 881 "interface Child {", 882 " @Subcomponent.Builder", 883 " interface Builder {", 884 " Child build();", 885 " }", 886 "}"); 887 888 JavaFileObject generatedComponent = 889 JavaFileObjects.forSourceLines( 890 "test.DaggerParent", 891 "package test;", 892 "", 893 "import dagger.internal.Preconditions;", 894 IMPORT_GENERATED_ANNOTATION, 895 "", 896 GENERATED_ANNOTATION, 897 "final class DaggerParent implements Parent {", 898 "", 899 " private DaggerParent() {}", 900 "", 901 " public static Builder builder() {", 902 " return new Builder();", 903 " }", 904 "", 905 " public static Parent create() {", 906 " return new Builder().build();", 907 " }", 908 "", 909 " @Override", 910 " public String notSubcomponent() {", 911 " return ParentModule_NotSubcomponentFactory.notSubcomponent();", 912 " }", 913 "", 914 " static final class Builder {", 915 "", 916 " private Builder() {}", 917 "", 918 " @Deprecated", 919 " public Builder parentModule(ParentModule parentModule) {", 920 " Preconditions.checkNotNull(parentModule);", 921 " return this;", 922 " }", 923 "", 924 " public Parent build() {", 925 " return new DaggerParent();", 926 " }", 927 " }", 928 "}"); 929 Compilation compilation = 930 daggerCompiler() 931 .withOptions(compilerMode.javacopts()) 932 .compile(component, module, subcomponent); 933 assertThat(compilation).succeeded(); 934 assertThat(compilation) 935 .generatedSourceFile("test.DaggerParent") 936 .hasSourceEquivalentTo(generatedComponent); 937 } 938 939 @Test testDefaultPackage()940 public void testDefaultPackage() { 941 JavaFileObject aClass = JavaFileObjects.forSourceLines("AClass", "class AClass {}"); 942 JavaFileObject bClass = JavaFileObjects.forSourceLines("BClass", 943 "import javax.inject.Inject;", 944 "", 945 "class BClass {", 946 " @Inject BClass(AClass a) {}", 947 "}"); 948 JavaFileObject aModule = JavaFileObjects.forSourceLines("AModule", 949 "import dagger.Module;", 950 "import dagger.Provides;", 951 "", 952 "@Module class AModule {", 953 " @Provides AClass aClass() {", 954 " return new AClass();", 955 " }", 956 "}"); 957 JavaFileObject component = JavaFileObjects.forSourceLines("SomeComponent", 958 "import dagger.Component;", 959 "", 960 "@Component(modules = AModule.class)", 961 "interface SomeComponent {", 962 " BClass bClass();", 963 "}"); 964 assertThat( 965 daggerCompiler() 966 .withOptions(compilerMode.javacopts()) 967 .compile(aModule, aClass, bClass, component)) 968 .succeeded(); 969 } 970 membersInjection()971 @Test public void membersInjection() { 972 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 973 "package test;", 974 "", 975 "import javax.inject.Inject;", 976 "", 977 "final class SomeInjectableType {", 978 " @Inject SomeInjectableType() {}", 979 "}"); 980 JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType", 981 "package test;", 982 "", 983 "import javax.inject.Inject;", 984 "", 985 "final class SomeInjectedType {", 986 " @Inject SomeInjectableType injectedField;", 987 " SomeInjectedType() {}", 988 "}"); 989 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 990 "package test;", 991 "", 992 "import dagger.Component;", 993 "import dagger.Lazy;", 994 "import javax.inject.Provider;", 995 "", 996 "@Component", 997 "interface SimpleComponent {", 998 " void inject(SomeInjectedType instance);", 999 " SomeInjectedType injectAndReturn(SomeInjectedType instance);", 1000 "}"); 1001 1002 JavaFileObject generatedComponent = 1003 compilerMode 1004 .javaFileBuilder("test.DaggerSimpleComponent") 1005 .addLines( 1006 "package test;", 1007 "", 1008 "import com.google.errorprone.annotations.CanIgnoreReturnValue;", 1009 "", 1010 GENERATED_ANNOTATION, 1011 "final class DaggerSimpleComponent implements SimpleComponent {", 1012 " @Override", 1013 " public void inject(SomeInjectedType instance) {", 1014 " injectSomeInjectedType(instance);", 1015 " }", 1016 "", 1017 " @Override", 1018 " public SomeInjectedType injectAndReturn(SomeInjectedType instance) {", 1019 " return injectSomeInjectedType(instance);", 1020 " }", 1021 "", 1022 " @CanIgnoreReturnValue", 1023 " private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {", 1024 " SomeInjectedType_MembersInjector.injectInjectedField(", 1025 " instance, new SomeInjectableType());", 1026 " return instance;", 1027 " }", 1028 "}") 1029 .build(); 1030 1031 Compilation compilation = 1032 daggerCompiler() 1033 .withOptions(compilerMode.javacopts()) 1034 .compile(injectableTypeFile, injectedTypeFile, componentFile); 1035 assertThat(compilation).succeeded(); 1036 assertThat(compilation) 1037 .generatedSourceFile("test.DaggerSimpleComponent") 1038 .containsElementsIn(generatedComponent); 1039 } 1040 componentInjection()1041 @Test public void componentInjection() { 1042 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 1043 "package test;", 1044 "", 1045 "import javax.inject.Inject;", 1046 "", 1047 "final class SomeInjectableType {", 1048 " @Inject SomeInjectableType(SimpleComponent component) {}", 1049 "}"); 1050 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 1051 "package test;", 1052 "", 1053 "import dagger.Component;", 1054 "import dagger.Lazy;", 1055 "import javax.inject.Provider;", 1056 "", 1057 "@Component", 1058 "interface SimpleComponent {", 1059 " SomeInjectableType someInjectableType();", 1060 " Provider<SimpleComponent> selfProvider();", 1061 "}"); 1062 JavaFileObject generatedComponent = 1063 JavaFileObjects.forSourceLines( 1064 "test.DaggerSimpleComponent", 1065 "package test;", 1066 "", 1067 GENERATED_ANNOTATION, 1068 "final class DaggerSimpleComponent implements SimpleComponent {", 1069 " private Provider<SimpleComponent> simpleComponentProvider;", 1070 "", 1071 " @SuppressWarnings(\"unchecked\")", 1072 " private void initialize() {", 1073 " this.simpleComponentProvider = InstanceFactory.create((SimpleComponent) this);", 1074 " }", 1075 "", 1076 " @Override", 1077 " public SomeInjectableType someInjectableType() {", 1078 " return new SomeInjectableType(this)", 1079 " }", 1080 "", 1081 " @Override", 1082 " public Provider<SimpleComponent> selfProvider() {", 1083 " return simpleComponentProvider;", 1084 " }", 1085 "}"); 1086 Compilation compilation = 1087 daggerCompiler() 1088 .withOptions(compilerMode.javacopts()) 1089 .compile(injectableTypeFile, componentFile); 1090 assertThat(compilation).succeeded(); 1091 assertThat(compilation) 1092 .generatedSourceFile("test.DaggerSimpleComponent") 1093 .containsElementsIn(generatedComponent); 1094 } 1095 membersInjectionInsideProvision()1096 @Test public void membersInjectionInsideProvision() { 1097 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 1098 "package test;", 1099 "", 1100 "import javax.inject.Inject;", 1101 "", 1102 "final class SomeInjectableType {", 1103 " @Inject SomeInjectableType() {}", 1104 "}"); 1105 JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType", 1106 "package test;", 1107 "", 1108 "import javax.inject.Inject;", 1109 "", 1110 "final class SomeInjectedType {", 1111 " @Inject SomeInjectableType injectedField;", 1112 " @Inject SomeInjectedType() {}", 1113 "}"); 1114 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 1115 "package test;", 1116 "", 1117 "import dagger.Component;", 1118 "", 1119 "@Component", 1120 "interface SimpleComponent {", 1121 " SomeInjectedType createAndInject();", 1122 "}"); 1123 1124 JavaFileObject generatedComponent = 1125 compilerMode 1126 .javaFileBuilder("test.DaggerSimpleComponent") 1127 .addLines( 1128 "package test;", 1129 "", 1130 "import com.google.errorprone.annotations.CanIgnoreReturnValue;", 1131 "", 1132 GENERATED_ANNOTATION, 1133 "final class DaggerSimpleComponent implements SimpleComponent {", 1134 " @Override", 1135 " public SomeInjectedType createAndInject() {", 1136 " return injectSomeInjectedType(", 1137 " SomeInjectedType_Factory.newInstance());", 1138 " }", 1139 "", 1140 " @CanIgnoreReturnValue", 1141 " private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {", 1142 " SomeInjectedType_MembersInjector.injectInjectedField(", 1143 " instance, new SomeInjectableType());", 1144 " return instance;", 1145 " }", 1146 "}") 1147 .build(); 1148 1149 Compilation compilation = 1150 daggerCompiler() 1151 .withOptions(compilerMode.javacopts()) 1152 .compile(injectableTypeFile, injectedTypeFile, componentFile); 1153 assertThat(compilation).succeeded(); 1154 assertThat(compilation) 1155 .generatedSourceFile("test.DaggerSimpleComponent") 1156 .containsElementsIn(generatedComponent); 1157 } 1158 componentDependency()1159 @Test public void componentDependency() { 1160 JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A", 1161 "package test;", 1162 "", 1163 "import javax.inject.Inject;", 1164 "", 1165 "final class A {", 1166 " @Inject A() {}", 1167 "}"); 1168 JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B", 1169 "package test;", 1170 "", 1171 "import javax.inject.Inject;", 1172 "import javax.inject.Provider;", 1173 "", 1174 "final class B {", 1175 " @Inject B(Provider<A> a) {}", 1176 "}"); 1177 JavaFileObject aComponentFile = JavaFileObjects.forSourceLines("test.AComponent", 1178 "package test;", 1179 "", 1180 "import dagger.Component;", 1181 "", 1182 "@Component", 1183 "interface AComponent {", 1184 " A a();", 1185 "}"); 1186 JavaFileObject bComponentFile = JavaFileObjects.forSourceLines("test.AComponent", 1187 "package test;", 1188 "", 1189 "import dagger.Component;", 1190 "", 1191 "@Component(dependencies = AComponent.class)", 1192 "interface BComponent {", 1193 " B b();", 1194 "}"); 1195 JavaFileObject generatedComponent = 1196 compilerMode 1197 .javaFileBuilder("test.DaggerBComponent") 1198 .addLines( 1199 "package test;", 1200 "", 1201 GENERATED_ANNOTATION, 1202 "final class DaggerBComponent implements BComponent {") 1203 .addLinesIn( 1204 DEFAULT_MODE, 1205 " private Provider<A> aProvider;") 1206 .addLinesIn( 1207 FAST_INIT_MODE, 1208 " private final AComponent aComponent;", 1209 " private volatile Provider<A> aProvider;", 1210 "", 1211 " private DaggerBComponent(AComponent aComponentParam) {", 1212 " this.aComponent = aComponentParam;", 1213 " }", 1214 "", 1215 " private Provider<A> getAProvider() {", 1216 " Object local = aProvider;", 1217 " if (local == null) {", 1218 " local = new SwitchingProvider<>(0);", 1219 " aProvider = (Provider<A>) local;", 1220 " }", 1221 " return (Provider<A>) local;", 1222 " }") 1223 .addLinesIn( 1224 DEFAULT_MODE, 1225 " @SuppressWarnings(\"unchecked\")", 1226 " private void initialize(final AComponent aComponentParam) {", 1227 " this.aProvider = new test_AComponent_a(aComponentParam);", 1228 " }") 1229 .addLines( 1230 "", 1231 " @Override", 1232 " public B b() {") 1233 .addLinesIn( 1234 DEFAULT_MODE, 1235 " return new B(aProvider);") 1236 .addLinesIn( 1237 FAST_INIT_MODE, 1238 " return new B(getAProvider());") 1239 .addLines( 1240 " }", 1241 "", 1242 " static final class Builder {", 1243 " private AComponent aComponent;", 1244 "", 1245 " public Builder aComponent(AComponent aComponent) {", 1246 " this.aComponent = Preconditions.checkNotNull(aComponent);", 1247 " return this;", 1248 " }", 1249 "", 1250 " public BComponent build() {", 1251 " Preconditions.checkBuilderRequirement(aComponent, AComponent.class);", 1252 " return new DaggerBComponent(aComponent);", 1253 " }", 1254 " }") 1255 .addLinesIn( 1256 DEFAULT_MODE, 1257 " private static class test_AComponent_a implements Provider<A> {", 1258 " private final AComponent aComponent;", 1259 " ", 1260 " test_AComponent_a(AComponent aComponent) {", 1261 " this.aComponent = aComponent;", 1262 " }", 1263 " ", 1264 " @Override()", 1265 " public A get() {", 1266 " return Preconditions.checkNotNull(", 1267 " aComponent.a(), " + NPE_FROM_COMPONENT_METHOD + ");", 1268 " }", 1269 " }", 1270 "}") 1271 .addLinesIn( 1272 FAST_INIT_MODE, 1273 " private final class SwitchingProvider<T> implements Provider<T> {", 1274 " @SuppressWarnings(\"unchecked\")", 1275 " @Override", 1276 " public T get() {", 1277 " switch (id) {", 1278 " case 0:", 1279 " return (T)", 1280 " Preconditions.checkNotNull(", 1281 " DaggerBComponent.this.aComponent.a(),", 1282 " " + NPE_FROM_COMPONENT_METHOD + ");", 1283 " default:", 1284 " throw new AssertionError(id);", 1285 " }", 1286 " }", 1287 " }") 1288 .build(); 1289 Compilation compilation = 1290 daggerCompiler() 1291 .withOptions(compilerMode.javacopts()) 1292 .compile(aFile, bFile, aComponentFile, bComponentFile); 1293 assertThat(compilation).succeeded(); 1294 assertThat(compilation) 1295 .generatedSourceFile("test.DaggerBComponent") 1296 .containsElementsIn(generatedComponent); 1297 } 1298 moduleNameCollision()1299 @Test public void moduleNameCollision() { 1300 JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A", 1301 "package test;", 1302 "", 1303 "public final class A {}"); 1304 JavaFileObject otherAFile = JavaFileObjects.forSourceLines("other.test.A", 1305 "package other.test;", 1306 "", 1307 "public final class A {}"); 1308 1309 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", 1310 "package test;", 1311 "", 1312 "import dagger.Module;", 1313 "import dagger.Provides;", 1314 "", 1315 "@Module", 1316 "public final class TestModule {", 1317 " @Provides A a() { return null; }", 1318 "}"); 1319 JavaFileObject otherModuleFile = JavaFileObjects.forSourceLines("other.test.TestModule", 1320 "package other.test;", 1321 "", 1322 "import dagger.Module;", 1323 "import dagger.Provides;", 1324 "", 1325 "@Module", 1326 "public final class TestModule {", 1327 " @Provides A a() { return null; }", 1328 "}"); 1329 1330 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 1331 "package test;", 1332 "", 1333 "import dagger.Component;", 1334 "import javax.inject.Provider;", 1335 "", 1336 "@Component(modules = {TestModule.class, other.test.TestModule.class})", 1337 "interface TestComponent {", 1338 " A a();", 1339 " other.test.A otherA();", 1340 "}"); 1341 JavaFileObject generatedComponent = 1342 JavaFileObjects.forSourceLines( 1343 "test.DaggerTestComponent", 1344 "package test;", 1345 "", 1346 GENERATED_ANNOTATION, 1347 "final class DaggerTestComponent implements TestComponent {", 1348 " private final TestModule testModule;", 1349 " private final other.test.TestModule testModule2;", 1350 "", 1351 " private DaggerTestComponent(", 1352 " TestModule testModuleParam,", 1353 " other.test.TestModule testModule2Param) {", 1354 " this.testModule = testModuleParam;", 1355 " this.testModule2 = testModule2Param;", 1356 " }", 1357 "", 1358 " @Override", 1359 " public A a() {", 1360 " return TestModule_AFactory.a(testModule);", 1361 " }", 1362 "", 1363 " @Override", 1364 " public other.test.A otherA() {", 1365 " return other.test.TestModule_AFactory.a(testModule2);", 1366 " }", 1367 "", 1368 " static final class Builder {", 1369 " private TestModule testModule;", 1370 " private other.test.TestModule testModule2;", 1371 "", 1372 " public Builder testModule(TestModule testModule) {", 1373 " this.testModule = Preconditions.checkNotNull(testModule);", 1374 " return this;", 1375 " }", 1376 "", 1377 " public Builder testModule(other.test.TestModule testModule) {", 1378 " this.testModule2 = Preconditions.checkNotNull(testModule);", 1379 " return this;", 1380 " }", 1381 "", 1382 " public TestComponent build() {", 1383 " if (testModule == null) {", 1384 " this.testModule = new TestModule();", 1385 " }", 1386 " if (testModule2 == null) {", 1387 " this.testModule2 = new other.test.TestModule();", 1388 " }", 1389 " return new DaggerTestComponent(testModule, testModule2);", 1390 " }", 1391 " }", 1392 "}"); 1393 Compilation compilation = 1394 daggerCompiler() 1395 .withOptions(compilerMode.javacopts()) 1396 .compile(aFile, otherAFile, moduleFile, otherModuleFile, componentFile); 1397 assertThat(compilation).succeeded(); 1398 assertThat(compilation) 1399 .generatedSourceFile("test.DaggerTestComponent") 1400 .containsElementsIn(generatedComponent); 1401 } 1402 ignoresDependencyMethodsFromObject()1403 @Test public void ignoresDependencyMethodsFromObject() { 1404 JavaFileObject injectedTypeFile = 1405 JavaFileObjects.forSourceLines( 1406 "test.InjectedType", 1407 "package test;", 1408 "", 1409 "import javax.inject.Inject;", 1410 "import javax.inject.Provider;", 1411 "", 1412 "final class InjectedType {", 1413 " @Inject InjectedType(", 1414 " String stringInjection,", 1415 " int intInjection,", 1416 " AComponent aComponent,", 1417 " Class<AComponent> aClass) {}", 1418 "}"); 1419 JavaFileObject aComponentFile = 1420 JavaFileObjects.forSourceLines( 1421 "test.AComponent", 1422 "package test;", 1423 "", 1424 "class AComponent {", 1425 " String someStringInjection() {", 1426 " return \"injectedString\";", 1427 " }", 1428 "", 1429 " int someIntInjection() {", 1430 " return 123;", 1431 " }", 1432 "", 1433 " Class<AComponent> someClassInjection() {", 1434 " return AComponent.class;", 1435 " }", 1436 "", 1437 " @Override", 1438 " public String toString() {", 1439 " return null;", 1440 " }", 1441 "", 1442 " @Override", 1443 " public int hashCode() {", 1444 " return 456;", 1445 " }", 1446 "", 1447 " @Override", 1448 " public AComponent clone() {", 1449 " return null;", 1450 " }", 1451 "}"); 1452 JavaFileObject bComponentFile = 1453 JavaFileObjects.forSourceLines( 1454 "test.AComponent", 1455 "package test;", 1456 "", 1457 "import dagger.Component;", 1458 "", 1459 "@Component(dependencies = AComponent.class)", 1460 "interface BComponent {", 1461 " InjectedType injectedType();", 1462 "}"); 1463 1464 JavaFileObject generatedComponent = 1465 JavaFileObjects.forSourceLines( 1466 "test.DaggerBComponent", 1467 "package test;", 1468 "", 1469 "import dagger.internal.Preconditions;", 1470 IMPORT_GENERATED_ANNOTATION, 1471 "", 1472 GENERATED_ANNOTATION, 1473 "final class DaggerBComponent implements BComponent {", 1474 " private final AComponent aComponent;", 1475 "", 1476 " private DaggerBComponent(AComponent aComponentParam) {", 1477 " this.aComponent = aComponentParam;", 1478 " }", 1479 "", 1480 " @Override", 1481 " public InjectedType injectedType() {", 1482 " return new InjectedType(", 1483 " Preconditions.checkNotNull(", 1484 " aComponent.someStringInjection(),", 1485 " \"Cannot return null from a non-@Nullable component method\"),", 1486 " aComponent.someIntInjection(),", 1487 " aComponent,", 1488 " Preconditions.checkNotNull(", 1489 " aComponent.someClassInjection(),", 1490 " \"Cannot return null from a non-@Nullable component method\"));", 1491 " }", 1492 "}"); 1493 1494 Compilation compilation = 1495 daggerCompiler() 1496 .withOptions(compilerMode.javacopts()) 1497 .compile(injectedTypeFile, aComponentFile, bComponentFile); 1498 assertThat(compilation).succeeded(); 1499 assertThat(compilation) 1500 .generatedSourceFile("test.DaggerBComponent") 1501 .containsElementsIn(generatedComponent); 1502 } 1503 resolutionOrder()1504 @Test public void resolutionOrder() { 1505 JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A", 1506 "package test;", 1507 "", 1508 "import javax.inject.Inject;", 1509 "", 1510 "final class A {", 1511 " @Inject A(B b) {}", 1512 "}"); 1513 JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B", 1514 "package test;", 1515 "", 1516 "import javax.inject.Inject;", 1517 "", 1518 "final class B {", 1519 " @Inject B(C c) {}", 1520 "}"); 1521 JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C", 1522 "package test;", 1523 "", 1524 "import javax.inject.Inject;", 1525 "", 1526 "final class C {", 1527 " @Inject C() {}", 1528 "}"); 1529 JavaFileObject xFile = JavaFileObjects.forSourceLines("test.X", 1530 "package test;", 1531 "", 1532 "import javax.inject.Inject;", 1533 "", 1534 "final class X {", 1535 " @Inject X(C c) {}", 1536 "}"); 1537 1538 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 1539 "package test;", 1540 "", 1541 "import dagger.Component;", 1542 "import javax.inject.Provider;", 1543 "", 1544 "@Component", 1545 "interface TestComponent {", 1546 " A a();", 1547 " C c();", 1548 " X x();", 1549 "}"); 1550 1551 JavaFileObject generatedComponent = 1552 compilerMode 1553 .javaFileBuilder("test.DaggerTestComponent") 1554 .addLines( 1555 "package test;", 1556 "", 1557 IMPORT_GENERATED_ANNOTATION, 1558 "", 1559 GENERATED_ANNOTATION, 1560 "final class DaggerTestComponent implements TestComponent {", 1561 " private B getB() {", 1562 " return new B(new C());", 1563 " }", 1564 "", 1565 " @Override", 1566 " public A a() {", 1567 " return new A(getB());", 1568 " }", 1569 "", 1570 " @Override", 1571 " public C c() {", 1572 " return new C();", 1573 " }", 1574 "", 1575 " @Override", 1576 " public X x() {", 1577 " return new X(new C());", 1578 " }", 1579 "}") 1580 .build(); 1581 1582 Compilation compilation = 1583 daggerCompiler() 1584 .withOptions(compilerMode.javacopts()) 1585 .compile(aFile, bFile, cFile, xFile, componentFile); 1586 assertThat(compilation).succeeded(); 1587 assertThat(compilation) 1588 .generatedSourceFile("test.DaggerTestComponent") 1589 .containsElementsIn(generatedComponent); 1590 } 1591 simpleComponent_redundantComponentMethod()1592 @Test public void simpleComponent_redundantComponentMethod() { 1593 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 1594 "package test;", 1595 "", 1596 "import javax.inject.Inject;", 1597 "", 1598 "final class SomeInjectableType {", 1599 " @Inject SomeInjectableType() {}", 1600 "}"); 1601 JavaFileObject componentSupertypeAFile = JavaFileObjects.forSourceLines("test.SupertypeA", 1602 "package test;", 1603 "", 1604 "import dagger.Component;", 1605 "import dagger.Lazy;", 1606 "import javax.inject.Provider;", 1607 "", 1608 "@Component", 1609 "interface SupertypeA {", 1610 " SomeInjectableType someInjectableType();", 1611 "}"); 1612 JavaFileObject componentSupertypeBFile = JavaFileObjects.forSourceLines("test.SupertypeB", 1613 "package test;", 1614 "", 1615 "import dagger.Component;", 1616 "import dagger.Lazy;", 1617 "import javax.inject.Provider;", 1618 "", 1619 "@Component", 1620 "interface SupertypeB {", 1621 " SomeInjectableType someInjectableType();", 1622 "}"); 1623 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 1624 "package test;", 1625 "", 1626 "import dagger.Component;", 1627 "import dagger.Lazy;", 1628 "import javax.inject.Provider;", 1629 "", 1630 "@Component", 1631 "interface SimpleComponent extends SupertypeA, SupertypeB {", 1632 "}"); 1633 JavaFileObject generatedComponent = 1634 JavaFileObjects.forSourceLines( 1635 "test.DaggerSimpleComponent", 1636 "package test;", 1637 "", 1638 IMPORT_GENERATED_ANNOTATION, 1639 "", 1640 GENERATED_ANNOTATION, 1641 "final class DaggerSimpleComponent implements SimpleComponent {", 1642 " private DaggerSimpleComponent() {}", 1643 "", 1644 " public static Builder builder() {", 1645 " return new Builder();", 1646 " }", 1647 "", 1648 " public static SimpleComponent create() {", 1649 " return new Builder().build();", 1650 " }", 1651 "", 1652 " @Override", 1653 " public SomeInjectableType someInjectableType() {", 1654 " return new SomeInjectableType();", 1655 " }", 1656 "", 1657 " static final class Builder {", 1658 " private Builder() {}", 1659 "", 1660 " public SimpleComponent build() {", 1661 " return new DaggerSimpleComponent();", 1662 " }", 1663 " }", 1664 "}"); 1665 Compilation compilation = 1666 daggerCompiler() 1667 .withOptions(compilerMode.javacopts()) 1668 .compile( 1669 injectableTypeFile, 1670 componentSupertypeAFile, 1671 componentSupertypeBFile, 1672 componentFile); 1673 assertThat(compilation).succeeded(); 1674 assertThat(compilation) 1675 .generatedSourceFile("test.DaggerSimpleComponent") 1676 .hasSourceEquivalentTo(generatedComponent); 1677 } 1678 simpleComponent_inheritedComponentMethodDep()1679 @Test public void simpleComponent_inheritedComponentMethodDep() { 1680 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 1681 "package test;", 1682 "", 1683 "import javax.inject.Inject;", 1684 "", 1685 "final class SomeInjectableType {", 1686 " @Inject SomeInjectableType() {}", 1687 "}"); 1688 JavaFileObject componentSupertype = JavaFileObjects.forSourceLines("test.Supertype", 1689 "package test;", 1690 "", 1691 "import dagger.Component;", 1692 "", 1693 "@Component", 1694 "interface Supertype {", 1695 " SomeInjectableType someInjectableType();", 1696 "}"); 1697 JavaFileObject depComponentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 1698 "package test;", 1699 "", 1700 "import dagger.Component;", 1701 "", 1702 "@Component", 1703 "interface SimpleComponent extends Supertype {", 1704 "}"); 1705 JavaFileObject generatedComponent = 1706 JavaFileObjects.forSourceLines( 1707 "test.DaggerSimpleComponent", 1708 "package test;", 1709 "", 1710 GENERATED_ANNOTATION, 1711 "final class DaggerSimpleComponent implements SimpleComponent {", 1712 " @Override", 1713 " public SomeInjectableType someInjectableType() {", 1714 " return new SomeInjectableType();", 1715 " }", 1716 "}"); 1717 Compilation compilation = 1718 daggerCompiler() 1719 .withOptions(compilerMode.javacopts()) 1720 .compile(injectableTypeFile, componentSupertype, depComponentFile); 1721 assertThat(compilation).succeeded(); 1722 assertThat(compilation) 1723 .generatedSourceFile("test.DaggerSimpleComponent") 1724 .containsElementsIn(generatedComponent); 1725 } 1726 wildcardGenericsRequiresAtProvides()1727 @Test public void wildcardGenericsRequiresAtProvides() { 1728 JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A", 1729 "package test;", 1730 "", 1731 "import javax.inject.Inject;", 1732 "", 1733 "final class A {", 1734 " @Inject A() {}", 1735 "}"); 1736 JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B", 1737 "package test;", 1738 "", 1739 "import javax.inject.Inject;", 1740 "import javax.inject.Provider;", 1741 "", 1742 "final class B<T> {", 1743 " @Inject B(T t) {}", 1744 "}"); 1745 JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C", 1746 "package test;", 1747 "", 1748 "import javax.inject.Inject;", 1749 "import javax.inject.Provider;", 1750 "", 1751 "final class C {", 1752 " @Inject C(B<? extends A> bA) {}", 1753 "}"); 1754 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 1755 "package test;", 1756 "", 1757 "import dagger.Component;", 1758 "import dagger.Lazy;", 1759 "import javax.inject.Provider;", 1760 "", 1761 "@Component", 1762 "interface SimpleComponent {", 1763 " C c();", 1764 "}"); 1765 Compilation compilation = 1766 daggerCompiler() 1767 .withOptions(compilerMode.javacopts()) 1768 .compile(aFile, bFile, cFile, componentFile); 1769 assertThat(compilation).failed(); 1770 assertThat(compilation) 1771 .hadErrorContaining( 1772 "test.B<? extends test.A> cannot be provided without an @Provides-annotated method"); 1773 } 1774 1775 // https://github.com/google/dagger/issues/630 1776 @Test arrayKeyRequiresAtProvides()1777 public void arrayKeyRequiresAtProvides() { 1778 JavaFileObject component = 1779 JavaFileObjects.forSourceLines( 1780 "test.TestComponent", 1781 "package test;", 1782 "", 1783 "import dagger.Component;", 1784 "", 1785 "@Component", 1786 "interface TestComponent {", 1787 " String[] array();", 1788 "}"); 1789 Compilation compilation = 1790 daggerCompiler().withOptions(compilerMode.javacopts()).compile(component); 1791 assertThat(compilation).failed(); 1792 assertThat(compilation) 1793 .hadErrorContaining("String[] cannot be provided without an @Provides-annotated method"); 1794 } 1795 1796 @Test componentImplicitlyDependsOnGeneratedType()1797 public void componentImplicitlyDependsOnGeneratedType() { 1798 JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType", 1799 "package test;", 1800 "", 1801 "import javax.inject.Inject;", 1802 "", 1803 "final class SomeInjectableType {", 1804 " @Inject SomeInjectableType(GeneratedType generatedType) {}", 1805 "}"); 1806 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent", 1807 "package test;", 1808 "", 1809 "import dagger.Component;", 1810 "", 1811 "@Component", 1812 "interface SimpleComponent {", 1813 " SomeInjectableType someInjectableType();", 1814 "}"); 1815 Compilation compilation = 1816 daggerCompiler( 1817 new GeneratingProcessor( 1818 "test.GeneratedType", 1819 "package test;", 1820 "", 1821 "import javax.inject.Inject;", 1822 "", 1823 "final class GeneratedType {", 1824 " @Inject GeneratedType() {}", 1825 "}")) 1826 .withOptions(compilerMode.javacopts()) 1827 .compile(injectableTypeFile, componentFile); 1828 assertThat(compilation).succeeded(); 1829 assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent"); 1830 } 1831 1832 @Test componentSupertypeDependsOnGeneratedType()1833 public void componentSupertypeDependsOnGeneratedType() { 1834 JavaFileObject componentFile = 1835 JavaFileObjects.forSourceLines( 1836 "test.SimpleComponent", 1837 "package test;", 1838 "", 1839 "import dagger.Component;", 1840 "", 1841 "@Component", 1842 "interface SimpleComponent extends SimpleComponentInterface {}"); 1843 JavaFileObject interfaceFile = 1844 JavaFileObjects.forSourceLines( 1845 "test.SimpleComponentInterface", 1846 "package test;", 1847 "", 1848 "interface SimpleComponentInterface {", 1849 " GeneratedType generatedType();", 1850 "}"); 1851 Compilation compilation = 1852 daggerCompiler( 1853 new GeneratingProcessor( 1854 "test.GeneratedType", 1855 "package test;", 1856 "", 1857 "import javax.inject.Inject;", 1858 "", 1859 "final class GeneratedType {", 1860 " @Inject GeneratedType() {}", 1861 "}")) 1862 .withOptions(compilerMode.javacopts()) 1863 .compile(componentFile, interfaceFile); 1864 assertThat(compilation).succeeded(); 1865 assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent"); 1866 } 1867 1868 /** 1869 * We warn when generating a {@link MembersInjector} for a type post-hoc (i.e., if Dagger wasn't 1870 * invoked when compiling the type). But Dagger only generates {@link MembersInjector}s for types 1871 * with {@link Inject @Inject} constructors if they have any injection sites, and it only 1872 * generates them for types without {@link Inject @Inject} constructors if they have local 1873 * (non-inherited) injection sites. So make sure we warn in only those cases where running the 1874 * Dagger processor actually generates a {@link MembersInjector}. 1875 */ 1876 @Test unprocessedMembersInjectorNotes()1877 public void unprocessedMembersInjectorNotes() { 1878 Compilation compilation = 1879 javac() 1880 .withOptions( 1881 compilerMode 1882 .javacopts() 1883 .append( 1884 "-Xlint:-processing", 1885 "-Adagger.warnIfInjectionFactoryNotGeneratedUpstream=enabled")) 1886 .withProcessors( 1887 new ElementFilteringComponentProcessor( 1888 Predicates.not( 1889 element -> 1890 MoreElements.getPackage(element) 1891 .getQualifiedName() 1892 .contentEquals("test.inject")))) 1893 .compile( 1894 JavaFileObjects.forSourceLines( 1895 "test.TestComponent", 1896 "package test;", 1897 "", 1898 "import dagger.Component;", 1899 "", 1900 "@Component(modules = TestModule.class)", 1901 "interface TestComponent {", 1902 " void inject(test.inject.NoInjectMemberNoConstructor object);", 1903 " void inject(test.inject.NoInjectMemberWithConstructor object);", 1904 " void inject(test.inject.LocalInjectMemberNoConstructor object);", 1905 " void inject(test.inject.LocalInjectMemberWithConstructor object);", 1906 " void inject(test.inject.ParentInjectMemberNoConstructor object);", 1907 " void inject(test.inject.ParentInjectMemberWithConstructor object);", 1908 "}"), 1909 JavaFileObjects.forSourceLines( 1910 "test.TestModule", 1911 "package test;", 1912 "", 1913 "import dagger.Module;", 1914 "import dagger.Provides;", 1915 "", 1916 "@Module", 1917 "class TestModule {", 1918 " @Provides static Object object() {", 1919 " return \"object\";", 1920 " }", 1921 "}"), 1922 JavaFileObjects.forSourceLines( 1923 "test.inject.NoInjectMemberNoConstructor", 1924 "package test.inject;", 1925 "", 1926 "public class NoInjectMemberNoConstructor {", 1927 "}"), 1928 JavaFileObjects.forSourceLines( 1929 "test.inject.NoInjectMemberWithConstructor", 1930 "package test.inject;", 1931 "", 1932 "import javax.inject.Inject;", 1933 "", 1934 "public class NoInjectMemberWithConstructor {", 1935 " @Inject NoInjectMemberWithConstructor() {}", 1936 "}"), 1937 JavaFileObjects.forSourceLines( 1938 "test.inject.LocalInjectMemberNoConstructor", 1939 "package test.inject;", 1940 "", 1941 "import javax.inject.Inject;", 1942 "", 1943 "public class LocalInjectMemberNoConstructor {", 1944 " @Inject Object object;", 1945 "}"), 1946 JavaFileObjects.forSourceLines( 1947 "test.inject.LocalInjectMemberWithConstructor", 1948 "package test.inject;", 1949 "", 1950 "import javax.inject.Inject;", 1951 "", 1952 "public class LocalInjectMemberWithConstructor {", 1953 " @Inject LocalInjectMemberWithConstructor() {}", 1954 " @Inject Object object;", 1955 "}"), 1956 JavaFileObjects.forSourceLines( 1957 "test.inject.ParentInjectMemberNoConstructor", 1958 "package test.inject;", 1959 "", 1960 "import javax.inject.Inject;", 1961 "", 1962 "public class ParentInjectMemberNoConstructor", 1963 " extends LocalInjectMemberNoConstructor {}"), 1964 JavaFileObjects.forSourceLines( 1965 "test.inject.ParentInjectMemberWithConstructor", 1966 "package test.inject;", 1967 "", 1968 "import javax.inject.Inject;", 1969 "", 1970 "public class ParentInjectMemberWithConstructor", 1971 " extends LocalInjectMemberNoConstructor {", 1972 " @Inject ParentInjectMemberWithConstructor() {}", 1973 "}")); 1974 1975 assertThat(compilation).succeededWithoutWarnings(); 1976 assertThat(compilation) 1977 .hadNoteContaining( 1978 "Generating a MembersInjector for " 1979 + "test.inject.LocalInjectMemberNoConstructor. " 1980 + "Prefer to run the dagger processor over that class instead."); 1981 assertThat(compilation) 1982 .hadNoteContaining( 1983 "Generating a MembersInjector for " 1984 + "test.inject.LocalInjectMemberWithConstructor. " 1985 + "Prefer to run the dagger processor over that class instead."); 1986 assertThat(compilation) 1987 .hadNoteContaining( 1988 "Generating a MembersInjector for " 1989 + "test.inject.ParentInjectMemberWithConstructor. " 1990 + "Prefer to run the dagger processor over that class instead."); 1991 assertThat(compilation).hadNoteCount(3); 1992 } 1993 1994 @Test scopeAnnotationOnInjectConstructorNotValid()1995 public void scopeAnnotationOnInjectConstructorNotValid() { 1996 JavaFileObject aScope = 1997 JavaFileObjects.forSourceLines( 1998 "test.AScope", 1999 "package test;", 2000 "", 2001 "import javax.inject.Scope;", 2002 "", 2003 "@Scope", 2004 "@interface AScope {}"); 2005 JavaFileObject aClass = 2006 JavaFileObjects.forSourceLines( 2007 "test.AClass", 2008 "package test;", 2009 "", 2010 "import javax.inject.Inject;", 2011 "", 2012 "final class AClass {", 2013 " @Inject @AScope AClass() {}", 2014 "}"); 2015 Compilation compilation = 2016 daggerCompiler().withOptions(compilerMode.javacopts()).compile(aScope, aClass); 2017 assertThat(compilation).failed(); 2018 assertThat(compilation) 2019 .hadErrorContaining("@Scope annotations are not allowed on @Inject constructors") 2020 .inFile(aClass) 2021 .onLine(6); 2022 } 2023 2024 @Test unusedSubcomponents_dontResolveExtraBindingsInParentComponents()2025 public void unusedSubcomponents_dontResolveExtraBindingsInParentComponents() { 2026 JavaFileObject foo = 2027 JavaFileObjects.forSourceLines( 2028 "test.Foo", 2029 "package test;", 2030 "", 2031 "import javax.inject.Inject;", 2032 "import javax.inject.Singleton;", 2033 "", 2034 "@Singleton", 2035 "class Foo {", 2036 " @Inject Foo() {}", 2037 "}"); 2038 2039 JavaFileObject module = 2040 JavaFileObjects.forSourceLines( 2041 "test.TestModule", 2042 "package test;", 2043 "", 2044 "import dagger.Module;", 2045 "", 2046 "@Module(subcomponents = Pruned.class)", 2047 "class TestModule {}"); 2048 2049 JavaFileObject component = 2050 JavaFileObjects.forSourceLines( 2051 "test.Parent", 2052 "package test;", 2053 "", 2054 "import dagger.Component;", 2055 "import javax.inject.Singleton;", 2056 "", 2057 "@Singleton", 2058 "@Component(modules = TestModule.class)", 2059 "interface Parent {}"); 2060 2061 JavaFileObject prunedSubcomponent = 2062 JavaFileObjects.forSourceLines( 2063 "test.Pruned", 2064 "package test;", 2065 "", 2066 "import dagger.Subcomponent;", 2067 "", 2068 "@Subcomponent", 2069 "interface Pruned {", 2070 " @Subcomponent.Builder", 2071 " interface Builder {", 2072 " Pruned build();", 2073 " }", 2074 "", 2075 " Foo foo();", 2076 "}"); 2077 JavaFileObject generated = 2078 JavaFileObjects.forSourceLines( 2079 "test.DaggerParent", 2080 "package test;", 2081 "", 2082 "import dagger.internal.Preconditions;", 2083 IMPORT_GENERATED_ANNOTATION, 2084 "", 2085 GENERATED_ANNOTATION, 2086 "final class DaggerParent implements Parent {", 2087 " private DaggerParent() {", 2088 " }", 2089 "", 2090 " public static Builder builder() {", 2091 " return new Builder();", 2092 " }", 2093 "", 2094 " public static Parent create() {", 2095 " return new Builder().build();", 2096 " }", 2097 "", 2098 " static final class Builder {", 2099 " private Builder() {}", 2100 "", 2101 " @Deprecated", 2102 " public Builder testModule(TestModule testModule) {", 2103 " Preconditions.checkNotNull(testModule);", 2104 " return this;", 2105 " }", 2106 "", 2107 " public Parent build() {", 2108 " return new DaggerParent();", 2109 " }", 2110 " }", 2111 "}"); 2112 2113 Compilation compilation = 2114 daggerCompiler() 2115 .withOptions(compilerMode.javacopts()) 2116 .compile(foo, module, component, prunedSubcomponent); 2117 assertThat(compilation).succeeded(); 2118 assertThat(compilation) 2119 .generatedSourceFile("test.DaggerParent") 2120 .hasSourceEquivalentTo(generated); 2121 } 2122 2123 @Test bindsToDuplicateBinding_bindsKeyIsNotDuplicated()2124 public void bindsToDuplicateBinding_bindsKeyIsNotDuplicated() { 2125 JavaFileObject firstModule = 2126 JavaFileObjects.forSourceLines( 2127 "test.FirstModule", 2128 "package test;", 2129 "", 2130 "import dagger.Module;", 2131 "import dagger.Provides;", 2132 "", 2133 "@Module", 2134 "abstract class FirstModule {", 2135 " @Provides static String first() { return \"first\"; }", 2136 "}"); 2137 JavaFileObject secondModule = 2138 JavaFileObjects.forSourceLines( 2139 "test.SecondModule", 2140 "package test;", 2141 "", 2142 "import dagger.Module;", 2143 "import dagger.Provides;", 2144 "", 2145 "@Module", 2146 "abstract class SecondModule {", 2147 " @Provides static String second() { return \"second\"; }", 2148 "}"); 2149 JavaFileObject bindsModule = 2150 JavaFileObjects.forSourceLines( 2151 "test.BindsModule", 2152 "package test;", 2153 "", 2154 "import dagger.Binds;", 2155 "import dagger.Module;", 2156 "", 2157 "@Module", 2158 "abstract class BindsModule {", 2159 " @Binds abstract Object bindToDuplicateBinding(String duplicate);", 2160 "}"); 2161 JavaFileObject component = 2162 JavaFileObjects.forSourceLines( 2163 "test.TestComponent", 2164 "package test;", 2165 "", 2166 "import dagger.Component;", 2167 "", 2168 "@Component(modules = {FirstModule.class, SecondModule.class, BindsModule.class})", 2169 "interface TestComponent {", 2170 " Object notDuplicated();", 2171 "}"); 2172 2173 Compilation compilation = 2174 daggerCompiler().compile(firstModule, secondModule, bindsModule, component); 2175 assertThat(compilation).failed(); 2176 assertThat(compilation).hadErrorCount(1); 2177 assertThat(compilation) 2178 .hadErrorContaining("java.lang.String is bound multiple times") 2179 .inFile(component) 2180 .onLineContaining("interface TestComponent"); 2181 } 2182 2183 @Test nullIncorrectlyReturnedFromNonNullableInlinedProvider()2184 public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() { 2185 Compilation compilation = 2186 daggerCompiler() 2187 .withOptions(compilerMode.javacopts()) 2188 .compile( 2189 JavaFileObjects.forSourceLines( 2190 "test.TestModule", 2191 "package test;", 2192 "", 2193 "import dagger.Module;", 2194 "import dagger.Provides;", 2195 "", 2196 "@Module", 2197 "public abstract class TestModule {", 2198 " @Provides static String nonNullableString() { return \"string\"; }", 2199 "}"), 2200 JavaFileObjects.forSourceLines( 2201 "test.InjectsMember", 2202 "package test;", 2203 "", 2204 "import javax.inject.Inject;", 2205 "", 2206 "public class InjectsMember {", 2207 " @Inject String member;", 2208 "}"), 2209 JavaFileObjects.forSourceLines( 2210 "test.TestComponent", 2211 "package test;", 2212 "", 2213 "import dagger.Component;", 2214 "", 2215 "@Component(modules = TestModule.class)", 2216 "interface TestComponent {", 2217 " String nonNullableString();", 2218 " void inject(InjectsMember member);", 2219 "}")); 2220 assertThat(compilation).succeededWithoutWarnings(); 2221 assertThat(compilation) 2222 .generatedSourceFile("test.TestModule_NonNullableStringFactory") 2223 .containsElementsIn( 2224 JavaFileObjects.forSourceLines( 2225 "test.TestModule_NonNullableStringFactory", 2226 "package test;", 2227 "", 2228 GENERATED_ANNOTATION, 2229 "public final class TestModule_NonNullableStringFactory", 2230 " implements Factory<String> {", 2231 " @Override", 2232 " public String get() {", 2233 " return nonNullableString();", 2234 " }", 2235 "", 2236 " public static String nonNullableString() {", 2237 " return Preconditions.checkNotNull(", 2238 " TestModule.nonNullableString(), " + NPE_FROM_PROVIDES_METHOD + ");", 2239 " }", 2240 "}")); 2241 2242 JavaFileObject generatedComponent = 2243 compilerMode 2244 .javaFileBuilder("test.DaggerTestComponent") 2245 .addLines( 2246 "package test;", 2247 "", 2248 GENERATED_ANNOTATION, 2249 "final class DaggerTestComponent implements TestComponent {", 2250 " @Override", 2251 " public String nonNullableString() {", 2252 " return TestModule_NonNullableStringFactory.nonNullableString());", 2253 " }", 2254 "", 2255 " @Override", 2256 " public void inject(InjectsMember member) {", 2257 " injectInjectsMember(member);", 2258 " }", 2259 "", 2260 " @CanIgnoreReturnValue", 2261 " private InjectsMember injectInjectsMember(InjectsMember instance) {", 2262 " InjectsMember_MembersInjector.injectMember(instance,", 2263 " TestModule_NonNullableStringFactory.nonNullableString());", 2264 " return instance;", 2265 " }", 2266 "}") 2267 .build(); 2268 2269 assertThat(compilation) 2270 .generatedSourceFile("test.DaggerTestComponent") 2271 .containsElementsIn(generatedComponent); 2272 } 2273 2274 @Test nullCheckingIgnoredWhenProviderReturnsPrimitive()2275 public void nullCheckingIgnoredWhenProviderReturnsPrimitive() { 2276 Compilation compilation = 2277 daggerCompiler() 2278 .withOptions(compilerMode.javacopts()) 2279 .compile( 2280 JavaFileObjects.forSourceLines( 2281 "test.TestModule", 2282 "package test;", 2283 "", 2284 "import dagger.Module;", 2285 "import dagger.Provides;", 2286 "", 2287 "@Module", 2288 "public abstract class TestModule {", 2289 " @Provides static int primitiveInteger() { return 1; }", 2290 "}"), 2291 JavaFileObjects.forSourceLines( 2292 "test.InjectsMember", 2293 "package test;", 2294 "", 2295 "import javax.inject.Inject;", 2296 "", 2297 "public class InjectsMember {", 2298 " @Inject Integer member;", 2299 "}"), 2300 JavaFileObjects.forSourceLines( 2301 "test.TestComponent", 2302 "package test;", 2303 "", 2304 "import dagger.Component;", 2305 "", 2306 "@Component(modules = TestModule.class)", 2307 "interface TestComponent {", 2308 " Integer nonNullableInteger();", 2309 " void inject(InjectsMember member);", 2310 "}")); 2311 assertThat(compilation).succeededWithoutWarnings(); 2312 assertThat(compilation) 2313 .generatedSourceFile("test.TestModule_PrimitiveIntegerFactory") 2314 .containsElementsIn( 2315 JavaFileObjects.forSourceLines( 2316 "test.TestModule_PrimitiveIntegerFactory", 2317 "package test;", 2318 "", 2319 GENERATED_ANNOTATION, 2320 "public final class TestModule_PrimitiveIntegerFactory", 2321 " implements Factory<Integer> {", 2322 "", 2323 " @Override", 2324 " public Integer get() {", 2325 " return primitiveInteger();", 2326 " }", 2327 "", 2328 " public static int primitiveInteger() {", 2329 " return TestModule.primitiveInteger();", 2330 " }", 2331 "}")); 2332 2333 JavaFileObject generatedComponent = 2334 compilerMode 2335 .javaFileBuilder("test.DaggerTestComponent") 2336 .addLines( 2337 "package test;", 2338 "", 2339 GENERATED_ANNOTATION, 2340 "final class DaggerTestComponent implements TestComponent {", 2341 " @Override", 2342 " public Integer nonNullableInteger() {", 2343 " return TestModule.primitiveInteger();", 2344 " }", 2345 "", 2346 " @Override", 2347 " public void inject(InjectsMember member) {", 2348 " injectInjectsMember(member);", 2349 " }", 2350 "", 2351 " @CanIgnoreReturnValue", 2352 " private InjectsMember injectInjectsMember(InjectsMember instance) {", 2353 " InjectsMember_MembersInjector.injectMember(", 2354 " instance, TestModule.primitiveInteger());", 2355 " return instance;", 2356 " }", 2357 "}") 2358 .build(); 2359 2360 assertThat(compilation) 2361 .generatedSourceFile("test.DaggerTestComponent") 2362 .containsElementsIn(generatedComponent); 2363 } 2364 2365 @Test privateMethodUsedOnlyInChildDoesNotUseQualifiedThis()2366 public void privateMethodUsedOnlyInChildDoesNotUseQualifiedThis() { 2367 JavaFileObject parent = 2368 JavaFileObjects.forSourceLines( 2369 "test.Parent", 2370 "package test;", 2371 "", 2372 "import dagger.Component;", 2373 "import javax.inject.Singleton;", 2374 "", 2375 "@Singleton", 2376 "@Component(modules=TestModule.class)", 2377 "interface Parent {", 2378 " Child child();", 2379 "}"); 2380 JavaFileObject testModule = 2381 JavaFileObjects.forSourceLines( 2382 "test.TestModule", 2383 "package test;", 2384 "", 2385 "import dagger.Module;", 2386 "import dagger.Provides;", 2387 "import javax.inject.Singleton;", 2388 "", 2389 "@Module", 2390 "abstract class TestModule {", 2391 " @Provides @Singleton static Number number() {", 2392 " return 3;", 2393 " }", 2394 "", 2395 " @Provides static String string(Number number) {", 2396 " return number.toString();", 2397 " }", 2398 "}"); 2399 JavaFileObject child = 2400 JavaFileObjects.forSourceLines( 2401 "test.Child", 2402 "package test;", 2403 "", 2404 "import dagger.Subcomponent;", 2405 "", 2406 "@Subcomponent", 2407 "interface Child {", 2408 " String string();", 2409 "}"); 2410 2411 JavaFileObject expectedPattern = 2412 JavaFileObjects.forSourceLines( 2413 "test.DaggerParent", 2414 "package test;", 2415 GENERATED_ANNOTATION, 2416 "final class DaggerParent implements Parent {", 2417 " private String getString() {", 2418 " return TestModule_StringFactory.string(numberProvider.get());", 2419 " }", 2420 "}"); 2421 2422 Compilation compilation = daggerCompiler().compile(parent, testModule, child); 2423 assertThat(compilation).succeededWithoutWarnings(); 2424 assertThat(compilation) 2425 .generatedSourceFile("test.DaggerParent") 2426 .containsElementsIn(expectedPattern); 2427 } 2428 2429 @Test componentMethodInChildCallsComponentMethodInParent()2430 public void componentMethodInChildCallsComponentMethodInParent() { 2431 JavaFileObject supertype = 2432 JavaFileObjects.forSourceLines( 2433 "test.Supertype", 2434 "package test;", 2435 "", 2436 "interface Supertype {", 2437 " String string();", 2438 "}"); 2439 JavaFileObject parent = 2440 JavaFileObjects.forSourceLines( 2441 "test.Parent", 2442 "package test;", 2443 "", 2444 "import dagger.Component;", 2445 "import javax.inject.Singleton;", 2446 "", 2447 "@Singleton", 2448 "@Component(modules=TestModule.class)", 2449 "interface Parent extends Supertype {", 2450 " Child child();", 2451 "}"); 2452 JavaFileObject testModule = 2453 JavaFileObjects.forSourceLines( 2454 "test.TestModule", 2455 "package test;", 2456 "", 2457 "import dagger.Module;", 2458 "import dagger.Provides;", 2459 "import javax.inject.Singleton;", 2460 "", 2461 "@Module", 2462 "abstract class TestModule {", 2463 " @Provides @Singleton static Number number() {", 2464 " return 3;", 2465 " }", 2466 "", 2467 " @Provides static String string(Number number) {", 2468 " return number.toString();", 2469 " }", 2470 "}"); 2471 JavaFileObject child = 2472 JavaFileObjects.forSourceLines( 2473 "test.Child", 2474 "package test;", 2475 "", 2476 "import dagger.Subcomponent;", 2477 "", 2478 "@Subcomponent", 2479 "interface Child extends Supertype {}"); 2480 2481 JavaFileObject expectedPattern = 2482 JavaFileObjects.forSourceLines( 2483 "test.DaggerParent", 2484 "package test;", 2485 GENERATED_ANNOTATION, 2486 "final class DaggerParent implements Parent {", 2487 " private final class ChildImpl implements Child {", 2488 " @Override", 2489 " public String string() {", 2490 " return DaggerParent.this.string();", 2491 " }", 2492 " }", 2493 "}"); 2494 2495 Compilation compilation = daggerCompiler().compile(supertype, parent, testModule, child); 2496 assertThat(compilation).succeededWithoutWarnings(); 2497 assertThat(compilation) 2498 .generatedSourceFile("test.DaggerParent") 2499 .containsElementsIn(expectedPattern); 2500 } 2501 2502 @Test justInTimeAtInjectConstructor_hasGeneratedQualifier()2503 public void justInTimeAtInjectConstructor_hasGeneratedQualifier() { 2504 JavaFileObject injected = 2505 JavaFileObjects.forSourceLines( 2506 "test.Injected", 2507 "package test;", 2508 "", 2509 "import javax.inject.Inject;", 2510 "", 2511 "class Injected {", 2512 " @Inject Injected(@GeneratedQualifier String string) {}", 2513 "}"); 2514 JavaFileObject module = 2515 JavaFileObjects.forSourceLines( 2516 "test.TestModule", 2517 "package test;", 2518 "", 2519 "import dagger.Module;", 2520 "import dagger.Provides;", 2521 "", 2522 "@Module", 2523 "interface TestModule {", 2524 " @Provides", 2525 " static String unqualified() {", 2526 " return new String();", 2527 " }", 2528 "", 2529 " @Provides", 2530 " @GeneratedQualifier", 2531 " static String qualified() {", 2532 " return new String();", 2533 " }", 2534 "}"); 2535 JavaFileObject component = 2536 JavaFileObjects.forSourceLines( 2537 "test.TestComponent", 2538 "package test;", 2539 "", 2540 "import dagger.Component;", 2541 "", 2542 "@Component(modules = TestModule.class)", 2543 "interface TestComponent {", 2544 " Injected injected();", 2545 "}"); 2546 2547 JavaFileObject generatedComponent = 2548 JavaFileObjects.forSourceLines( 2549 "test.DaggerTestComponent", 2550 "package test;", 2551 "", 2552 GENERATED_ANNOTATION, 2553 "final class DaggerTestComponent implements TestComponent {", 2554 " @Override", 2555 " public Injected injected() {", 2556 // Ensure that the qualified @Provides method is used. It's also probably more likely 2557 // that if the qualifier type hasn't been generated, a duplicate binding error will be 2558 // reported, since the annotation won't be recognized as a qualifier and instead as an 2559 // ordinary annotation. 2560 " return new Injected(TestModule_QualifiedFactory.qualified());", 2561 " }", 2562 "}"); 2563 2564 Compilation compilation = 2565 daggerCompiler( 2566 new GeneratingProcessor( 2567 "test.GeneratedQualifier", 2568 "package test;", 2569 "", 2570 "import static java.lang.annotation.RetentionPolicy.RUNTIME;", 2571 "", 2572 "import java.lang.annotation.Retention;", 2573 "import javax.inject.Qualifier;", 2574 "", 2575 "@Retention(RUNTIME)", 2576 "@Qualifier", 2577 "@interface GeneratedQualifier {}")) 2578 .compile(injected, module, component); 2579 assertThat(compilation).succeededWithoutWarnings(); 2580 assertThat(compilation) 2581 .generatedSourceFile("test.DaggerTestComponent") 2582 .containsElementsIn(generatedComponent); 2583 } 2584 2585 @Test moduleHasGeneratedQualifier()2586 public void moduleHasGeneratedQualifier() { 2587 JavaFileObject module = 2588 JavaFileObjects.forSourceLines( 2589 "test.TestModule", 2590 "package test;", 2591 "", 2592 "import dagger.Module;", 2593 "import dagger.Provides;", 2594 "", 2595 "@Module", 2596 "interface TestModule {", 2597 " @Provides", 2598 " static String unqualified() {", 2599 " return new String();", 2600 " }", 2601 "", 2602 " @Provides", 2603 " @GeneratedQualifier", 2604 " static String qualified() {", 2605 " return new String();", 2606 " }", 2607 "}"); 2608 JavaFileObject component = 2609 JavaFileObjects.forSourceLines( 2610 "test.TestComponent", 2611 "package test;", 2612 "", 2613 "import dagger.Component;", 2614 "", 2615 "@Component(modules = TestModule.class)", 2616 "interface TestComponent {", 2617 " String unqualified();", 2618 "}"); 2619 2620 JavaFileObject generatedComponent = 2621 JavaFileObjects.forSourceLines( 2622 "test.DaggerTestComponent", 2623 "package test;", 2624 "", 2625 GENERATED_ANNOTATION, 2626 "final class DaggerTestComponent implements TestComponent {", 2627 " @Override", 2628 " public String unqualified() {", 2629 // Ensure that the unqualified @Provides method is used. It's also probably more likely 2630 // if the qualifier hasn't been generated, a duplicate binding exception will be thrown 2631 // since the annotation won't be considered a qualifier 2632 " return TestModule_UnqualifiedFactory.unqualified();", 2633 " }", 2634 "}"); 2635 2636 Compilation compilation = 2637 daggerCompiler( 2638 new GeneratingProcessor( 2639 "test.GeneratedQualifier", 2640 "package test;", 2641 "", 2642 "import static java.lang.annotation.RetentionPolicy.RUNTIME;", 2643 "", 2644 "import java.lang.annotation.Retention;", 2645 "import javax.inject.Qualifier;", 2646 "", 2647 "@Retention(RUNTIME)", 2648 "@Qualifier", 2649 "@interface GeneratedQualifier {}")) 2650 .compile(module, component); 2651 assertThat(compilation).succeededWithoutWarnings(); 2652 assertThat(compilation) 2653 .generatedSourceFile("test.DaggerTestComponent") 2654 .containsElementsIn(generatedComponent); 2655 } 2656 2657 @Test publicComponentType()2658 public void publicComponentType() { 2659 JavaFileObject publicComponent = 2660 JavaFileObjects.forSourceLines( 2661 "test.PublicComponent", 2662 "package test;", 2663 "", 2664 "import dagger.Component;", 2665 "", 2666 "@Component", 2667 "public interface PublicComponent {}"); 2668 Compilation compilation = daggerCompiler().compile(publicComponent); 2669 assertThat(compilation).succeeded(); 2670 assertThat(compilation) 2671 .generatedSourceFile("test.DaggerPublicComponent") 2672 .hasSourceEquivalentTo( 2673 JavaFileObjects.forSourceLines( 2674 "test.DaggerPublicComponent", 2675 "package test;", 2676 "", 2677 IMPORT_GENERATED_ANNOTATION, 2678 "", 2679 GENERATED_ANNOTATION, 2680 "public final class DaggerPublicComponent implements PublicComponent {", 2681 " private DaggerPublicComponent() {}", 2682 "", 2683 " public static Builder builder() {", 2684 " return new Builder();", 2685 " }", 2686 "", 2687 " public static PublicComponent create() {", 2688 " return new Builder().build();", 2689 " }", 2690 "", 2691 " public static final class Builder {", 2692 " private Builder() {}", 2693 "", 2694 " public PublicComponent build() {", 2695 " return new DaggerPublicComponent();", 2696 " }", 2697 " }", 2698 "}")); 2699 } 2700 2701 /** 2702 * A {@link ComponentProcessor} that excludes elements using a {@link Predicate}. 2703 */ 2704 private static final class ElementFilteringComponentProcessor extends AbstractProcessor { 2705 private final ComponentProcessor componentProcessor = new ComponentProcessor(); 2706 private final Predicate<? super Element> filter; 2707 2708 /** 2709 * Creates a {@link ComponentProcessor} that only processes elements that match {@code filter}. 2710 */ ElementFilteringComponentProcessor(Predicate<? super Element> filter)2711 public ElementFilteringComponentProcessor(Predicate<? super Element> filter) { 2712 this.filter = filter; 2713 } 2714 2715 @Override init(ProcessingEnvironment processingEnv)2716 public synchronized void init(ProcessingEnvironment processingEnv) { 2717 super.init(processingEnv); 2718 componentProcessor.init(processingEnv); 2719 } 2720 2721 @Override getSupportedAnnotationTypes()2722 public Set<String> getSupportedAnnotationTypes() { 2723 return componentProcessor.getSupportedAnnotationTypes(); 2724 } 2725 2726 @Override getSupportedSourceVersion()2727 public SourceVersion getSupportedSourceVersion() { 2728 return componentProcessor.getSupportedSourceVersion(); 2729 } 2730 2731 @Override getSupportedOptions()2732 public Set<String> getSupportedOptions() { 2733 return componentProcessor.getSupportedOptions(); 2734 } 2735 2736 @Override process( Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv)2737 public boolean process( 2738 Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) { 2739 return componentProcessor.process( 2740 annotations, 2741 new RoundEnvironment() { 2742 @Override 2743 public boolean processingOver() { 2744 return roundEnv.processingOver(); 2745 } 2746 2747 @Override 2748 public Set<? extends Element> getRootElements() { 2749 return Sets.filter(roundEnv.getRootElements(), filter); 2750 } 2751 2752 @Override 2753 public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) { 2754 return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter); 2755 } 2756 2757 @Override 2758 public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) { 2759 return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter); 2760 } 2761 2762 @Override 2763 public boolean errorRaised() { 2764 return roundEnv.errorRaised(); 2765 } 2766 }); 2767 } 2768 } 2769 } 2770