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