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