1 /* 2 * Copyright (C) 2015 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.testing.compile.CompilationSubject.assertThat; 20 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; 21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; 22 import static dagger.internal.codegen.Compilers.daggerCompiler; 23 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION; 24 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION; 25 26 import com.google.testing.compile.Compilation; 27 import com.google.testing.compile.JavaFileObjects; 28 import java.util.Collection; 29 import javax.tools.JavaFileObject; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.Parameterized; 33 import org.junit.runners.Parameterized.Parameters; 34 35 @RunWith(Parameterized.class) 36 public class SubcomponentValidationTest { 37 @Parameters(name = "{0}") parameters()38 public static Collection<Object[]> parameters() { 39 return CompilerMode.TEST_PARAMETERS; 40 } 41 42 private final CompilerMode compilerMode; 43 SubcomponentValidationTest(CompilerMode compilerMode)44 public SubcomponentValidationTest(CompilerMode compilerMode) { 45 this.compilerMode = compilerMode; 46 } 47 factoryMethod_missingModulesWithParameters()48 @Test public void factoryMethod_missingModulesWithParameters() { 49 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 50 "package test;", 51 "", 52 "import dagger.Component;", 53 "", 54 "@Component", 55 "interface TestComponent {", 56 " ChildComponent newChildComponent();", 57 "}"); 58 JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", 59 "package test;", 60 "", 61 "import dagger.Subcomponent;", 62 "", 63 "@Subcomponent(modules = ModuleWithParameters.class)", 64 "interface ChildComponent {", 65 " Object object();", 66 "}"); 67 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ModuleWithParameters", 68 "package test;", 69 "", 70 "import dagger.Module;", 71 "import dagger.Provides;", 72 "", 73 "@Module", 74 "final class ModuleWithParameters {", 75 " private final Object object;", 76 "", 77 " ModuleWithParameters(Object object) {", 78 " this.object = object;", 79 " }", 80 "", 81 " @Provides Object object() {", 82 " return object;", 83 " }", 84 "}"); 85 Compilation compilation = 86 daggerCompiler() 87 .withOptions(compilerMode.javacopts()) 88 .compile(componentFile, childComponentFile, moduleFile); 89 assertThat(compilation).failed(); 90 assertThat(compilation) 91 .hadErrorContaining( 92 "test.ChildComponent requires modules which have no visible default constructors. " 93 + "Add the following modules as parameters to this method: " 94 + "test.ModuleWithParameters") 95 .inFile(componentFile) 96 .onLineContaining("ChildComponent newChildComponent();"); 97 } 98 99 @Test factoryMethod_grandchild()100 public void factoryMethod_grandchild() { 101 JavaFileObject component = 102 JavaFileObjects.forSourceLines( 103 "test.TestComponent", 104 "package test;", 105 "", 106 "import dagger.Component;", 107 "", 108 "@Component", 109 "interface TestComponent {", 110 " ChildComponent newChildComponent();", 111 "}"); 112 JavaFileObject childComponent = 113 JavaFileObjects.forSourceLines( 114 "test.ChildComponent", 115 "package test;", 116 "", 117 "import dagger.Subcomponent;", 118 "", 119 "@Subcomponent", 120 "interface ChildComponent {", 121 " GrandchildComponent newGrandchildComponent();", 122 "}"); 123 JavaFileObject grandchildComponent = 124 JavaFileObjects.forSourceLines( 125 "test.GrandchildComponent", 126 "package test;", 127 "", 128 "import dagger.Subcomponent;", 129 "", 130 "@Subcomponent(modules = GrandchildModule.class)", 131 "interface GrandchildComponent {", 132 " Object object();", 133 "}"); 134 JavaFileObject grandchildModule = 135 JavaFileObjects.forSourceLines( 136 "test.GrandchildModule", 137 "package test;", 138 "", 139 "import dagger.Module;", 140 "import dagger.Provides;", 141 "", 142 "@Module", 143 "final class GrandchildModule {", 144 " private final Object object;", 145 "", 146 " GrandchildModule(Object object) {", 147 " this.object = object;", 148 " }", 149 "", 150 " @Provides Object object() {", 151 " return object;", 152 " }", 153 "}"); 154 Compilation compilation = 155 daggerCompiler() 156 .withOptions(compilerMode.javacopts()) 157 .compile(component, childComponent, grandchildComponent, grandchildModule); 158 assertThat(compilation).failed(); 159 assertThat(compilation) 160 .hadErrorContaining( 161 "[test.ChildComponent.newGrandchildComponent()] " 162 + "test.GrandchildComponent requires modules which have no visible default " 163 + "constructors. Add the following modules as parameters to this method: " 164 + "test.GrandchildModule") 165 .inFile(component) 166 .onLineContaining("interface TestComponent"); 167 } 168 factoryMethod_nonModuleParameter()169 @Test public void factoryMethod_nonModuleParameter() { 170 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 171 "package test;", 172 "", 173 "import dagger.Component;", 174 "", 175 "@Component", 176 "interface TestComponent {", 177 " ChildComponent newChildComponent(String someRandomString);", 178 "}"); 179 JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", 180 "package test;", 181 "", 182 "import dagger.Subcomponent;", 183 "", 184 "@Subcomponent", 185 "interface ChildComponent {}"); 186 Compilation compilation = 187 daggerCompiler() 188 .withOptions(compilerMode.javacopts()) 189 .compile(componentFile, childComponentFile); 190 assertThat(compilation).failed(); 191 assertThat(compilation) 192 .hadErrorContaining( 193 "Subcomponent factory methods may only accept modules, but java.lang.String is not.") 194 .inFile(componentFile) 195 .onLine(7) 196 .atColumn(43); 197 } 198 factoryMethod_duplicateParameter()199 @Test public void factoryMethod_duplicateParameter() { 200 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", 201 "package test;", 202 "", 203 "import dagger.Module;", 204 "", 205 "@Module", 206 "final class TestModule {}"); 207 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 208 "package test;", 209 "", 210 "import dagger.Component;", 211 "", 212 "@Component", 213 "interface TestComponent {", 214 " ChildComponent newChildComponent(TestModule testModule1, TestModule testModule2);", 215 "}"); 216 JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", 217 "package test;", 218 "", 219 "import dagger.Subcomponent;", 220 "", 221 "@Subcomponent(modules = TestModule.class)", 222 "interface ChildComponent {}"); 223 Compilation compilation = 224 daggerCompiler() 225 .withOptions(compilerMode.javacopts()) 226 .compile(moduleFile, componentFile, childComponentFile); 227 assertThat(compilation).failed(); 228 assertThat(compilation) 229 .hadErrorContaining( 230 "A module may only occur once an an argument in a Subcomponent factory method, " 231 + "but test.TestModule was already passed.") 232 .inFile(componentFile) 233 .onLine(7) 234 .atColumn(71); 235 } 236 factoryMethod_superflouousModule()237 @Test public void factoryMethod_superflouousModule() { 238 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", 239 "package test;", 240 "", 241 "import dagger.Module;", 242 "", 243 "@Module", 244 "final class TestModule {}"); 245 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 246 "package test;", 247 "", 248 "import dagger.Component;", 249 "", 250 "@Component", 251 "interface TestComponent {", 252 " ChildComponent newChildComponent(TestModule testModule);", 253 "}"); 254 JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", 255 "package test;", 256 "", 257 "import dagger.Subcomponent;", 258 "", 259 "@Subcomponent", 260 "interface ChildComponent {}"); 261 Compilation compilation = 262 daggerCompiler() 263 .withOptions(compilerMode.javacopts()) 264 .compile(moduleFile, componentFile, childComponentFile); 265 assertThat(compilation).failed(); 266 assertThat(compilation) 267 .hadErrorContaining( 268 "test.TestModule is present as an argument to the test.ChildComponent factory method, " 269 + "but is not one of the modules used to implement the subcomponent.") 270 .inFile(componentFile) 271 .onLine(7); 272 } 273 missingBinding()274 @Test public void missingBinding() { 275 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule", 276 "package test;", 277 "", 278 "import dagger.Module;", 279 "import dagger.Provides;", 280 "", 281 "@Module", 282 "final class TestModule {", 283 " @Provides String provideString(int i) {", 284 " return Integer.toString(i);", 285 " }", 286 "}"); 287 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 288 "package test;", 289 "", 290 "import dagger.Component;", 291 "", 292 "@Component", 293 "interface TestComponent {", 294 " ChildComponent newChildComponent();", 295 "}"); 296 JavaFileObject childComponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", 297 "package test;", 298 "", 299 "import dagger.Subcomponent;", 300 "", 301 "@Subcomponent(modules = TestModule.class)", 302 "interface ChildComponent {", 303 " String getString();", 304 "}"); 305 Compilation compilation = 306 daggerCompiler() 307 .withOptions(compilerMode.javacopts()) 308 .compile(moduleFile, componentFile, childComponentFile); 309 assertThat(compilation).failed(); 310 assertThat(compilation) 311 .hadErrorContaining( 312 "java.lang.Integer cannot be provided without an @Inject constructor or an " 313 + "@Provides-annotated method") 314 .inFile(componentFile) 315 .onLineContaining("interface TestComponent"); 316 } 317 subcomponentOnConcreteType()318 @Test public void subcomponentOnConcreteType() { 319 JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.NotASubcomponent", 320 "package test;", 321 "", 322 "import dagger.Subcomponent;", 323 "", 324 "@Subcomponent", 325 "final class NotASubcomponent {}"); 326 Compilation compilation = 327 daggerCompiler().withOptions(compilerMode.javacopts()).compile(subcomponentFile); 328 assertThat(compilation).failed(); 329 assertThat(compilation).hadErrorContaining("interface"); 330 } 331 scopeMismatch()332 @Test public void scopeMismatch() { 333 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.ParentComponent", 334 "package test;", 335 "", 336 "import dagger.Component;", 337 "import javax.inject.Singleton;", 338 "", 339 "@Component", 340 "@Singleton", 341 "interface ParentComponent {", 342 " ChildComponent childComponent();", 343 "}"); 344 JavaFileObject subcomponentFile = JavaFileObjects.forSourceLines("test.ChildComponent", 345 "package test;", 346 "", 347 "import dagger.Subcomponent;", 348 "", 349 "@Subcomponent(modules = ChildModule.class)", 350 "interface ChildComponent {", 351 " Object getObject();", 352 "}"); 353 JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.ChildModule", 354 "package test;", 355 "", 356 "import dagger.Module;", 357 "import dagger.Provides;", 358 "import javax.inject.Singleton;", 359 "", 360 "@Module", 361 "final class ChildModule {", 362 " @Provides @Singleton Object provideObject() { return null; }", 363 "}"); 364 Compilation compilation = 365 daggerCompiler() 366 .withOptions(compilerMode.javacopts()) 367 .compile(componentFile, subcomponentFile, moduleFile); 368 assertThat(compilation).failed(); 369 assertThat(compilation).hadErrorContaining("@Singleton"); 370 } 371 372 @Test delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent()373 public void delegateFactoryNotCreatedForSubcomponentWhenProviderExistsInParent() { 374 JavaFileObject parentComponentFile = 375 JavaFileObjects.forSourceLines( 376 "test.ParentComponent", 377 "package test;", 378 "", 379 "import dagger.Component;", 380 "import javax.inject.Singleton;", 381 "", 382 "@Singleton", 383 "@Component", 384 "interface ParentComponent {", 385 " ChildComponent childComponent();", 386 " Dep1 getDep1();", 387 " Dep2 getDep2();", 388 "}"); 389 JavaFileObject childComponentFile = 390 JavaFileObjects.forSourceLines( 391 "test.ChildComponent", 392 "package test;", 393 "", 394 "import dagger.Subcomponent;", 395 "", 396 "@Subcomponent(modules = ChildModule.class)", 397 "interface ChildComponent {", 398 " Object getObject();", 399 "}"); 400 JavaFileObject childModuleFile = 401 JavaFileObjects.forSourceLines( 402 "test.ChildModule", 403 "package test;", 404 "", 405 "import dagger.Module;", 406 "import dagger.Provides;", 407 "", 408 "@Module", 409 "final class ChildModule {", 410 " @Provides Object provideObject(A a) { return null; }", 411 "}"); 412 JavaFileObject aFile = 413 JavaFileObjects.forSourceLines( 414 "test.A", 415 "package test;", 416 "", 417 "import javax.inject.Inject;", 418 "", 419 "final class A {", 420 " @Inject public A(NeedsDep1 a, Dep1 b, Dep2 c) { }", 421 " @Inject public void methodA() { }", 422 "}"); 423 JavaFileObject needsDep1File = 424 JavaFileObjects.forSourceLines( 425 "test.NeedsDep1", 426 "package test;", 427 "", 428 "import javax.inject.Inject;", 429 "", 430 "final class NeedsDep1 {", 431 " @Inject public NeedsDep1(Dep1 d) { }", 432 "}"); 433 JavaFileObject dep1File = 434 JavaFileObjects.forSourceLines( 435 "test.Dep1", 436 "package test;", 437 "", 438 "import javax.inject.Inject;", 439 "import javax.inject.Singleton;", 440 "", 441 "@Singleton", 442 "final class Dep1 {", 443 " @Inject public Dep1() { }", 444 " @Inject public void dep1Method() { }", 445 "}"); 446 JavaFileObject dep2File = 447 JavaFileObjects.forSourceLines( 448 "test.Dep2", 449 "package test;", 450 "", 451 "import javax.inject.Inject;", 452 "import javax.inject.Singleton;", 453 "", 454 "@Singleton", 455 "final class Dep2 {", 456 " @Inject public Dep2() { }", 457 " @Inject public void dep2Method() { }", 458 "}"); 459 460 JavaFileObject generatedComponent = 461 compilerMode 462 .javaFileBuilder("test.DaggerParentComponent") 463 .addLines( 464 "package test;", 465 "", 466 GENERATED_ANNOTATION, 467 "final class DaggerParentComponent implements ParentComponent {") 468 .addLinesIn( 469 DEFAULT_MODE, 470 " @SuppressWarnings(\"unchecked\")", 471 " private void initialize() {", 472 " this.dep1Provider = DoubleCheck.provider(Dep1_Factory.create());", 473 " this.dep2Provider = DoubleCheck.provider(Dep2_Factory.create());", 474 " }", 475 "") 476 .addLines( 477 " @Override", // 478 " public Dep1 getDep1() {") 479 .addLinesIn( 480 FAST_INIT_MODE, 481 " Object local = dep1;", 482 " if (local instanceof MemoizedSentinel) {", 483 " synchronized (local) {", 484 " local = dep1;", 485 " if (local instanceof MemoizedSentinel) {", 486 " local = injectDep1(Dep1_Factory.newInstance());", 487 " dep1 = DoubleCheck.reentrantCheck(dep1, local);", 488 " }", 489 " }", 490 " }", 491 " return (Dep1) local;") 492 .addLinesIn( 493 DEFAULT_MODE, // 494 " return dep1Provider.get();") 495 .addLines( 496 " }", // 497 "", 498 " @Override", 499 " public Dep2 getDep2() {") 500 .addLinesIn( 501 FAST_INIT_MODE, 502 " Object local = dep2;", 503 " if (local instanceof MemoizedSentinel) {", 504 " synchronized (local) {", 505 " local = dep2;", 506 " if (local instanceof MemoizedSentinel) {", 507 " local = injectDep2(Dep2_Factory.newInstance());", 508 " dep2 = DoubleCheck.reentrantCheck(dep2, local);", 509 " }", 510 " }", 511 " }", 512 " return (Dep2) local;") 513 .addLinesIn( 514 DEFAULT_MODE, // 515 " return dep2Provider.get();") 516 .addLines( 517 " }", 518 "", 519 " @Override", 520 " public ChildComponent childComponent() {", 521 " return new ChildComponentImpl();", 522 " }", 523 "") 524 .addLinesIn( 525 FAST_INIT_MODE, 526 " @CanIgnoreReturnValue", 527 " private Dep1 injectDep1(Dep1 instance) {", 528 " Dep1_MembersInjector.injectDep1Method(instance);", 529 " return instance;", 530 " }", 531 "", 532 " @CanIgnoreReturnValue", 533 " private Dep2 injectDep2(Dep2 instance) {", 534 " Dep2_MembersInjector.injectDep2Method(instance);", 535 " return instance;", 536 " }") 537 .addLines( 538 "", 539 " private final class ChildComponentImpl implements ChildComponent {", 540 " private final ChildModule childModule;", 541 "", 542 " private ChildComponentImpl() {", 543 " this.childModule = new ChildModule();", 544 " }", 545 "") 546 .addLinesIn( 547 DEFAULT_MODE, 548 " private NeedsDep1 getNeedsDep1() {", 549 " return new NeedsDep1(DaggerParentComponent.this.dep1Provider.get());", 550 " }") 551 .addLinesIn( 552 FAST_INIT_MODE, 553 " private NeedsDep1 getNeedsDep1() {", 554 " return new NeedsDep1(DaggerParentComponent.this.getDep1());", 555 " }") 556 .addLines( 557 " private A getA() {", 558 " return injectA(", 559 " A_Factory.newInstance(", 560 " getNeedsDep1(),") 561 .addLinesIn( 562 DEFAULT_MODE, 563 " DaggerParentComponent.this.dep1Provider.get(),", 564 " DaggerParentComponent.this.dep2Provider.get()));") 565 .addLinesIn( 566 FAST_INIT_MODE, 567 " DaggerParentComponent.this.getDep1(),", 568 " DaggerParentComponent.this.getDep2()));") 569 .addLines( 570 " }", 571 "", 572 " @Override", 573 " public Object getObject() {", 574 " return ChildModule_ProvideObjectFactory.provideObject(", 575 " childModule, getA());", 576 " }", 577 "", 578 " @CanIgnoreReturnValue", 579 " private A injectA(A instance) {", 580 " A_MembersInjector.injectMethodA(instance);", 581 " return instance;", 582 " }", 583 " }", 584 "}") 585 .build(); 586 587 Compilation compilation = 588 daggerCompiler() 589 .withOptions(compilerMode.javacopts()) 590 .compile( 591 parentComponentFile, 592 childComponentFile, 593 childModuleFile, 594 aFile, 595 needsDep1File, 596 dep1File, 597 dep2File); 598 assertThat(compilation).succeeded(); 599 assertThat(compilation) 600 .generatedSourceFile("test.DaggerParentComponent") 601 .containsElementsIn(generatedComponent); 602 } 603 604 @Test multipleSubcomponentsWithSameSimpleNamesCanExistInSameComponent()605 public void multipleSubcomponentsWithSameSimpleNamesCanExistInSameComponent() { 606 JavaFileObject parent = 607 JavaFileObjects.forSourceLines( 608 "test.ParentComponent", 609 "package test;", 610 "", 611 "import dagger.Component;", 612 "", 613 "@Component", 614 "interface ParentComponent {", 615 " Foo.Sub newInstanceSubcomponent();", 616 " NoConflict newNoConflictSubcomponent();", 617 "}"); 618 JavaFileObject foo = 619 JavaFileObjects.forSourceLines( 620 "test.Foo", 621 "package test;", 622 "", 623 "import dagger.Subcomponent;", 624 "", 625 "interface Foo {", 626 " @Subcomponent interface Sub {", 627 " Bar.Sub newBarSubcomponent();", 628 " }", 629 "}"); 630 JavaFileObject bar = 631 JavaFileObjects.forSourceLines( 632 "test.Bar", 633 "package test;", 634 "", 635 "import dagger.Subcomponent;", 636 "", 637 "interface Bar {", 638 " @Subcomponent interface Sub {", 639 " test.subpackage.Sub newSubcomponentInSubpackage();", 640 " }", 641 "}"); 642 JavaFileObject baz = 643 JavaFileObjects.forSourceLines( 644 "test.subpackage.Sub", 645 "package test.subpackage;", 646 "", 647 "import dagger.Subcomponent;", 648 "", 649 "@Subcomponent public interface Sub {}"); 650 JavaFileObject noConflict = 651 JavaFileObjects.forSourceLines( 652 "test.NoConflict", 653 "package test;", 654 "", 655 "import dagger.Subcomponent;", 656 "", 657 "@Subcomponent interface NoConflict {}"); 658 659 JavaFileObject componentGeneratedFile = 660 JavaFileObjects.forSourceLines( 661 "test.DaggerParentComponent", 662 "package test;", 663 "", 664 "import test.subpackage.Sub;", 665 "", 666 GENERATED_ANNOTATION, 667 "final class DaggerParentComponent implements ParentComponent {", 668 " @Override", 669 " public Foo.Sub newInstanceSubcomponent() {", 670 " return new F_SubImpl();", 671 " }", 672 "", 673 " @Override", 674 " public NoConflict newNoConflictSubcomponent() {", 675 " return new NoConflictImpl();", 676 " }", 677 "", 678 " static final class Builder {", 679 " public ParentComponent build() {", 680 " return new DaggerParentComponent();", 681 " }", 682 " }", 683 "", 684 " private final class F_SubImpl implements Foo.Sub {", 685 " @Override", 686 " public Bar.Sub newBarSubcomponent() {", 687 " return new B_SubImpl();", 688 " }", 689 "", 690 " private final class B_SubImpl implements Bar.Sub {", 691 " @Override", 692 " public Sub newSubcomponentInSubpackage() {", 693 " return new ts_SubImpl();", 694 " }", 695 "", 696 " private final class ts_SubImpl implements Sub {}", 697 " }", 698 " }", 699 "", 700 " private final class NoConflictImpl implements NoConflict {}", 701 "}"); 702 Compilation compilation = 703 daggerCompiler() 704 .withOptions(compilerMode.javacopts()) 705 .compile(parent, foo, bar, baz, noConflict); 706 assertThat(compilation).succeeded(); 707 assertThat(compilation) 708 .generatedSourceFile("test.DaggerParentComponent") 709 .containsElementsIn(componentGeneratedFile); 710 } 711 712 @Test subcomponentSimpleNamesDisambiguated()713 public void subcomponentSimpleNamesDisambiguated() { 714 JavaFileObject parent = 715 JavaFileObjects.forSourceLines( 716 "test.ParentComponent", 717 "package test;", 718 "", 719 "import dagger.Component;", 720 "", 721 "@Component", 722 "interface ParentComponent {", 723 " Sub newSubcomponent();", 724 "}"); 725 JavaFileObject sub = 726 JavaFileObjects.forSourceLines( 727 "test.Sub", 728 "package test;", 729 "", 730 "import dagger.Subcomponent;", 731 "", 732 "@Subcomponent interface Sub {", 733 " test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();", 734 "}"); 735 JavaFileObject deepSub = 736 JavaFileObjects.forSourceLines( 737 "test.deep.many.levels.that.match.test.Sub", 738 "package test.deep.many.levels.that.match.test;", 739 "", 740 "import dagger.Subcomponent;", 741 "", 742 "@Subcomponent public interface Sub {}"); 743 744 JavaFileObject componentGeneratedFile = 745 JavaFileObjects.forSourceLines( 746 "test.DaggerParentComponent", 747 "package test;", 748 "", 749 GENERATED_ANNOTATION, 750 "final class DaggerParentComponent implements ParentComponent {", 751 " @Override", 752 " public Sub newSubcomponent() {", 753 " return new t_SubImpl();", 754 " }", 755 "", 756 " static final class Builder {", 757 " public ParentComponent build() {", 758 " return new DaggerParentComponent();", 759 " }", 760 " }", 761 "", 762 " private final class t_SubImpl implements Sub {", 763 " @Override", 764 " public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {", 765 " return new tdmltmt_SubImpl();", 766 " }", 767 "", 768 " private final class tdmltmt_SubImpl implements ", 769 " test.deep.many.levels.that.match.test.Sub {", 770 " private tdmltmt_SubImpl() {}", 771 " }", 772 " }", 773 "}"); 774 Compilation compilation = 775 daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, sub, deepSub); 776 assertThat(compilation).succeeded(); 777 assertThat(compilation) 778 .generatedSourceFile("test.DaggerParentComponent") 779 .containsElementsIn(componentGeneratedFile); 780 } 781 782 @Test subcomponentSimpleNamesDisambiguatedInRoot()783 public void subcomponentSimpleNamesDisambiguatedInRoot() { 784 JavaFileObject parent = 785 JavaFileObjects.forSourceLines( 786 "ParentComponent", 787 "import dagger.Component;", 788 "", 789 "@Component", 790 "interface ParentComponent {", 791 " Sub newSubcomponent();", 792 "}"); 793 JavaFileObject sub = 794 JavaFileObjects.forSourceLines( 795 "Sub", 796 "import dagger.Subcomponent;", 797 "", 798 "@Subcomponent interface Sub {", 799 " test.deep.many.levels.that.match.test.Sub newDeepSubcomponent();", 800 "}"); 801 JavaFileObject deepSub = 802 JavaFileObjects.forSourceLines( 803 "test.deep.many.levels.that.match.test.Sub", 804 "package test.deep.many.levels.that.match.test;", 805 "", 806 "import dagger.Subcomponent;", 807 "", 808 "@Subcomponent public interface Sub {}"); 809 810 JavaFileObject componentGeneratedFile = 811 JavaFileObjects.forSourceLines( 812 "DaggerParentComponent", 813 "", 814 GENERATED_ANNOTATION, 815 "final class DaggerParentComponent implements ParentComponent {", 816 " @Override", 817 " public Sub newSubcomponent() {", 818 " return new $_SubImpl();", 819 " }", 820 "", 821 " private final class $_SubImpl implements Sub {", 822 " private $_SubImpl() {}", 823 "", 824 " @Override", 825 " public test.deep.many.levels.that.match.test.Sub newDeepSubcomponent() {", 826 " return new tdmltmt_SubImpl();", 827 " }", 828 "", 829 " private final class tdmltmt_SubImpl implements ", 830 " test.deep.many.levels.that.match.test.Sub {", 831 " private tdmltmt_SubImpl() {}", 832 " }", 833 " }", 834 "}", 835 ""); 836 837 Compilation compilation = 838 daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, sub, deepSub); 839 assertThat(compilation).succeeded(); 840 assertThat(compilation) 841 .generatedSourceFile("DaggerParentComponent") 842 .containsElementsIn(componentGeneratedFile); 843 } 844 845 @Test subcomponentImplNameUsesFullyQualifiedClassNameIfNecessary()846 public void subcomponentImplNameUsesFullyQualifiedClassNameIfNecessary() { 847 JavaFileObject parent = 848 JavaFileObjects.forSourceLines( 849 "test.ParentComponent", 850 "package test;", 851 "", 852 "import dagger.Component;", 853 "", 854 "@Component", 855 "interface ParentComponent {", 856 " top1.a.b.c.d.E.F.Sub top1();", 857 " top2.a.b.c.d.E.F.Sub top2();", 858 "}"); 859 JavaFileObject top1 = 860 JavaFileObjects.forSourceLines( 861 "top1.a.b.c.d.E", 862 "package top1.a.b.c.d;", 863 "", 864 "import dagger.Subcomponent;", 865 "", 866 "public interface E {", 867 " interface F {", 868 " @Subcomponent interface Sub {}", 869 " }", 870 "}"); 871 JavaFileObject top2 = 872 JavaFileObjects.forSourceLines( 873 "top2.a.b.c.d.E", 874 "package top2.a.b.c.d;", 875 "", 876 "import dagger.Subcomponent;", 877 "", 878 "public interface E {", 879 " interface F {", 880 " @Subcomponent interface Sub {}", 881 " }", 882 "}"); 883 884 JavaFileObject componentGeneratedFile = 885 JavaFileObjects.forSourceLines( 886 "test.DaggerParentComponent", 887 "package test;", 888 "", 889 "import top1.a.b.c.d.E;", 890 "", 891 GENERATED_ANNOTATION, 892 "final class DaggerParentComponent implements ParentComponent {", 893 " @Override", 894 " public E.F.Sub top1() {", 895 " return new F_SubImpl();", 896 " }", 897 "", 898 " @Override", 899 " public top2.a.b.c.d.E.F.Sub top2() {", 900 " return new F2_SubImpl();", 901 " }", 902 "", 903 " private final class F_SubImpl implements E.F.Sub {", 904 " private F_SubImpl() {}", 905 " }", 906 " private final class F2_SubImpl implements top2.a.b.c.d.E.F.Sub {", 907 " private F2_SubImpl() {}", 908 " }", 909 "}"); 910 911 Compilation compilation = 912 daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, top1, top2); 913 assertThat(compilation).succeeded(); 914 assertThat(compilation) 915 .generatedSourceFile("test.DaggerParentComponent") 916 .containsElementsIn(componentGeneratedFile); 917 } 918 919 @Test parentComponentNameShouldNotBeDisambiguatedWhenItConflictsWithASubcomponent()920 public void parentComponentNameShouldNotBeDisambiguatedWhenItConflictsWithASubcomponent() { 921 JavaFileObject parent = 922 JavaFileObjects.forSourceLines( 923 "test.C", 924 "package test;", 925 "", 926 "import dagger.Component;", 927 "", 928 "@Component", 929 "interface C {", 930 " test.Foo.C newInstanceC();", 931 "}"); 932 JavaFileObject subcomponentWithSameSimpleNameAsParent = 933 JavaFileObjects.forSourceLines( 934 "test.Foo", 935 "package test;", 936 "", 937 "import dagger.Subcomponent;", 938 "", 939 "interface Foo {", 940 " @Subcomponent interface C {}", 941 "}"); 942 943 JavaFileObject componentGeneratedFile = 944 JavaFileObjects.forSourceLines( 945 "test.DaggerC", 946 "package test;", 947 "", 948 GENERATED_ANNOTATION, 949 "final class DaggerC implements C {", 950 " @Override", 951 " public Foo.C newInstanceC() {", 952 " return new F_CImpl();", 953 " }", 954 "", 955 " private final class F_CImpl implements Foo.C {}", 956 "}"); 957 958 Compilation compilation = 959 daggerCompiler() 960 .withOptions(compilerMode.javacopts()) 961 .compile(parent, subcomponentWithSameSimpleNameAsParent); 962 assertThat(compilation).succeeded(); 963 assertThat(compilation) 964 .generatedSourceFile("test.DaggerC") 965 .containsElementsIn(componentGeneratedFile); 966 } 967 968 @Test subcomponentBuilderNamesShouldNotConflict()969 public void subcomponentBuilderNamesShouldNotConflict() { 970 JavaFileObject parent = 971 JavaFileObjects.forSourceLines( 972 "test.C", 973 "package test;", 974 "", 975 "import dagger.Component;", 976 "import dagger.Subcomponent;", 977 "", 978 "@Component", 979 "interface C {", 980 " Foo.Sub.Builder fooBuilder();", 981 " Bar.Sub.Builder barBuilder();", 982 "", 983 " interface Foo {", 984 " @Subcomponent", 985 " interface Sub {", 986 " @Subcomponent.Builder", 987 " interface Builder {", 988 " Sub build();", 989 " }", 990 " }", 991 " }", 992 "", 993 " interface Bar {", 994 " @Subcomponent", 995 " interface Sub {", 996 " @Subcomponent.Builder", 997 " interface Builder {", 998 " Sub build();", 999 " }", 1000 " }", 1001 " }", 1002 "}"); 1003 JavaFileObject componentGeneratedFile = 1004 JavaFileObjects.forSourceLines( 1005 "test.DaggerC", 1006 "package test;", 1007 "", 1008 IMPORT_GENERATED_ANNOTATION, 1009 "", 1010 GENERATED_ANNOTATION, 1011 "final class DaggerC implements C {", 1012 " @Override", 1013 " public C.Foo.Sub.Builder fooBuilder() {", 1014 " return new F_SubBuilder();", 1015 " }", 1016 "", 1017 " @Override", 1018 " public C.Bar.Sub.Builder barBuilder() {", 1019 " return new B_SubBuilder();", 1020 " }", 1021 "", 1022 // TODO(user): Reverse the order of subcomponent and builder so that subcomponent 1023 // comes first. 1024 " private final class F_SubBuilder implements C.Foo.Sub.Builder {", 1025 " @Override", 1026 " public C.Foo.Sub build() {", 1027 " return new F_SubImpl();", 1028 " }", 1029 " }", 1030 "", 1031 " private final class F_SubImpl implements C.Foo.Sub {", 1032 " private F_SubImpl() {}", 1033 " }", 1034 "", 1035 " private final class B_SubBuilder implements C.Bar.Sub.Builder {", 1036 " @Override", 1037 " public C.Bar.Sub build() {", 1038 " return new B_SubImpl();", 1039 " }", 1040 " }", 1041 "", 1042 " private final class B_SubImpl implements C.Bar.Sub {", 1043 " private B_SubImpl() {}", 1044 " }", 1045 "}"); 1046 Compilation compilation = 1047 daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent); 1048 assertThat(compilation).succeeded(); 1049 assertThat(compilation) 1050 .generatedSourceFile("test.DaggerC") 1051 .containsElementsIn(componentGeneratedFile); 1052 } 1053 1054 @Test duplicateBindingWithSubcomponentDeclaration()1055 public void duplicateBindingWithSubcomponentDeclaration() { 1056 JavaFileObject module = 1057 JavaFileObjects.forSourceLines( 1058 "test.TestModule", 1059 "package test;", 1060 "", 1061 "import dagger.Module;", 1062 "import dagger.Provides;", 1063 "", 1064 "@Module(subcomponents = Sub.class)", 1065 "class TestModule {", 1066 " @Provides Sub.Builder providesConflictsWithModuleSubcomponents() { return null; }", 1067 " @Provides Object usesSubcomponentBuilder(Sub.Builder builder) {", 1068 " return new Builder().toString();", 1069 " }", 1070 "}"); 1071 1072 JavaFileObject subcomponent = 1073 JavaFileObjects.forSourceLines( 1074 "test.Sub", 1075 "package test;", 1076 "", 1077 "import dagger.Subcomponent;", 1078 "", 1079 "@Subcomponent", 1080 "interface Sub {", 1081 " @Subcomponent.Builder", 1082 " interface Builder {", 1083 " Sub build();", 1084 " }", 1085 "}"); 1086 1087 JavaFileObject component = 1088 JavaFileObjects.forSourceLines( 1089 "test.Sub", 1090 "package test;", 1091 "", 1092 "import dagger.Component;", 1093 "", 1094 "@Component(modules = TestModule.class)", 1095 "interface C {", 1096 " Object dependsOnBuilder();", 1097 "}"); 1098 1099 Compilation compilation = 1100 daggerCompiler() 1101 .withOptions(compilerMode.javacopts()) 1102 .compile(module, component, subcomponent); 1103 assertThat(compilation).failed(); 1104 assertThat(compilation).hadErrorContaining("test.Sub.Builder is bound multiple times:"); 1105 assertThat(compilation) 1106 .hadErrorContaining( 1107 "@Provides test.Sub.Builder " 1108 + "test.TestModule.providesConflictsWithModuleSubcomponents()"); 1109 assertThat(compilation) 1110 .hadErrorContaining("@Module(subcomponents = test.Sub.class) for test.TestModule"); 1111 } 1112 1113 @Test subcomponentDependsOnGeneratedType()1114 public void subcomponentDependsOnGeneratedType() { 1115 JavaFileObject parent = 1116 JavaFileObjects.forSourceLines( 1117 "test.Parent", 1118 "package test;", 1119 "", 1120 "import dagger.Component;", 1121 "", 1122 "@Component", 1123 "interface Parent {", 1124 " Child.Builder childBuilder();", 1125 "}"); 1126 1127 JavaFileObject child = 1128 JavaFileObjects.forSourceLines( 1129 "test.Child", 1130 "package test;", 1131 "", 1132 "import dagger.Subcomponent;", 1133 "", 1134 "@Subcomponent", 1135 "interface Child extends ChildSupertype {", 1136 " @Subcomponent.Builder", 1137 " interface Builder {", 1138 " Child build();", 1139 " }", 1140 "}"); 1141 1142 JavaFileObject childSupertype = 1143 JavaFileObjects.forSourceLines( 1144 "test.ChildSupertype", 1145 "package test;", 1146 "", 1147 "interface ChildSupertype {", 1148 " GeneratedType generatedType();", 1149 "}"); 1150 1151 Compilation compilation = 1152 daggerCompiler( 1153 new GeneratingProcessor( 1154 "test.GeneratedType", 1155 "package test;", 1156 "", 1157 "import javax.inject.Inject;", 1158 "", 1159 "final class GeneratedType {", 1160 " @Inject GeneratedType() {}", 1161 "}")) 1162 .compile(parent, child, childSupertype); 1163 assertThat(compilation).succeededWithoutWarnings(); 1164 } 1165 } 1166