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