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 dagger.internal.codegen.CompilerMode.DEFAULT_MODE; 20 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER; 21 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY; 22 import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER; 23 import static dagger.internal.codegen.base.ComponentCreatorKind.FACTORY; 24 import static dagger.internal.codegen.base.ComponentKind.SUBCOMPONENT; 25 import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent; 26 import static dagger.internal.codegen.binding.ErrorMessages.componentMessagesFor; 27 28 import androidx.room.compiler.processing.util.Source; 29 import com.google.common.collect.ImmutableList; 30 import dagger.internal.codegen.base.ComponentCreatorAnnotation; 31 import dagger.testing.compile.CompilerTests; 32 import java.util.Collection; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.Parameterized; 36 import org.junit.runners.Parameterized.Parameters; 37 38 /** Tests for {@link dagger.Subcomponent.Builder} validation. */ 39 @RunWith(Parameterized.class) 40 public class SubcomponentCreatorValidationTest extends ComponentCreatorTestHelper { 41 @Parameters(name = "creatorKind={0}") parameters()42 public static Collection<Object[]> parameters() { 43 return ImmutableList.copyOf(new Object[][] {{SUBCOMPONENT_BUILDER}, {SUBCOMPONENT_FACTORY}}); 44 } 45 SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation)46 public SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation) { 47 super(DEFAULT_MODE, componentCreatorAnnotation); 48 } 49 50 @Test testRefSubcomponentAndSubCreatorFails()51 public void testRefSubcomponentAndSubCreatorFails() { 52 Source componentFile = 53 preprocessedJavaSource( 54 "test.ParentComponent", 55 "package test;", 56 "", 57 "import dagger.Component;", 58 "import javax.inject.Provider;", 59 "", 60 "@Component", 61 "interface ParentComponent {", 62 " ChildComponent child();", 63 " ChildComponent.Builder childComponentBuilder();", 64 "}"); 65 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 66 "package test;", 67 "", 68 "import dagger.Subcomponent;", 69 "", 70 "@Subcomponent", 71 "interface ChildComponent {", 72 " @Subcomponent.Builder", 73 " static interface Builder {", 74 " ChildComponent build();", 75 " }", 76 "}"); 77 CompilerTests.daggerCompiler(componentFile, childComponentFile) 78 .compile( 79 subject -> { 80 subject.hasErrorCount(1); 81 subject.hasErrorContaining( 82 String.format( 83 moreThanOneRefToSubcomponent(), 84 "test.ChildComponent", 85 process("[" 86 + "test.ParentComponent.child(), " 87 + "test.ParentComponent.childComponentBuilder()" 88 + "]"))) 89 .onSource(componentFile); 90 }); 91 } 92 93 @Test testRefSubCreatorTwiceFails()94 public void testRefSubCreatorTwiceFails() { 95 Source componentFile = preprocessedJavaSource("test.ParentComponent", 96 "package test;", 97 "", 98 "import dagger.Component;", 99 "import javax.inject.Provider;", 100 "", 101 "@Component", 102 "interface ParentComponent {", 103 " ChildComponent.Builder builder1();", 104 " ChildComponent.Builder builder2();", 105 "}"); 106 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 107 "package test;", 108 "", 109 "import dagger.Subcomponent;", 110 "", 111 "@Subcomponent", 112 "interface ChildComponent {", 113 " @Subcomponent.Builder", 114 " static interface Builder {", 115 " ChildComponent build();", 116 " }", 117 "}"); 118 CompilerTests.daggerCompiler(componentFile, childComponentFile) 119 .compile( 120 subject -> { 121 subject.hasErrorCount(1); 122 subject.hasErrorContaining( 123 String.format( 124 moreThanOneRefToSubcomponent(), 125 "test.ChildComponent", 126 process("[" 127 + "test.ParentComponent.builder1(), " 128 + "test.ParentComponent.builder2()" 129 + "]"))) 130 .onSource(componentFile); 131 }); 132 } 133 134 @Test testMoreThanOneCreatorFails()135 public void testMoreThanOneCreatorFails() { 136 Source componentFile = preprocessedJavaSource("test.ParentComponent", 137 "package test;", 138 "", 139 "import dagger.Component;", 140 "import javax.inject.Provider;", 141 "", 142 "@Component", 143 "interface ParentComponent {", 144 " ChildComponent.Builder1 build();", 145 "}"); 146 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 147 "package test;", 148 "", 149 "import dagger.Subcomponent;", 150 "", 151 "@Subcomponent", 152 "interface ChildComponent {", 153 " @Subcomponent.Builder", 154 " static interface Builder1 {", 155 " ChildComponent build();", 156 " }", 157 "", 158 " @Subcomponent.Builder", 159 " static interface Builder2 {", 160 " ChildComponent build();", 161 " }", 162 "}"); 163 CompilerTests.daggerCompiler(componentFile, childComponentFile) 164 .compile( 165 subject -> { 166 subject.hasErrorCount(1); 167 subject.hasErrorContaining( 168 String.format( 169 componentMessagesFor(SUBCOMPONENT).moreThanOne(), 170 process("[test.ChildComponent.Builder1, test.ChildComponent.Builder2]"))) 171 .onSource(childComponentFile); 172 }); 173 } 174 175 @Test testMoreThanOneCreatorFails_differentTypes()176 public void testMoreThanOneCreatorFails_differentTypes() { 177 Source componentFile = preprocessedJavaSource("test.ParentComponent", 178 "package test;", 179 "", 180 "import dagger.Component;", 181 "import javax.inject.Provider;", 182 "", 183 "@Component", 184 "interface ParentComponent {", 185 " ChildComponent.Builder build();", 186 "}"); 187 Source childComponentFile = 188 CompilerTests.javaSource("test.ChildComponent", 189 "package test;", 190 "", 191 "import dagger.Subcomponent;", 192 "", 193 "@Subcomponent", 194 "interface ChildComponent {", 195 " @Subcomponent.Builder", 196 " static interface Builder {", 197 " ChildComponent build();", 198 " }", 199 "", 200 " @Subcomponent.Factory", 201 " static interface Factory {", 202 " ChildComponent create();", 203 " }", 204 "}"); 205 CompilerTests.daggerCompiler(componentFile, childComponentFile) 206 .compile( 207 subject -> { 208 subject.hasErrorCount(1); 209 subject.hasErrorContaining( 210 String.format( 211 componentMessagesFor(SUBCOMPONENT).moreThanOne(), 212 "[test.ChildComponent.Builder, test.ChildComponent.Factory]")) 213 .onSource(childComponentFile); 214 }); 215 } 216 217 @Test testCreatorGenericsFails()218 public void testCreatorGenericsFails() { 219 Source componentFile = preprocessedJavaSource("test.ParentComponent", 220 "package test;", 221 "", 222 "import dagger.Component;", 223 "import javax.inject.Provider;", 224 "", 225 "@Component", 226 "interface ParentComponent {", 227 " ChildComponent.Builder build();", 228 "}"); 229 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 230 "package test;", 231 "", 232 "import dagger.Subcomponent;", 233 "", 234 "@Subcomponent", 235 "interface ChildComponent {", 236 " @Subcomponent.Builder", 237 " interface Builder<T> {", 238 " ChildComponent build();", 239 " }", 240 "}"); 241 CompilerTests.daggerCompiler(componentFile, childComponentFile) 242 .compile( 243 subject -> { 244 subject.hasErrorCount(1); 245 subject.hasErrorContaining(messages.generics()).onSource(childComponentFile); 246 }); 247 } 248 249 @Test testCreatorNotInComponentFails()250 public void testCreatorNotInComponentFails() { 251 Source builder = preprocessedJavaSource("test.Builder", 252 "package test;", 253 "", 254 "import dagger.Subcomponent;", 255 "", 256 "@Subcomponent.Builder", 257 "interface Builder {}"); 258 CompilerTests.daggerCompiler(builder) 259 .compile( 260 subject -> { 261 subject.hasErrorCount(1); 262 subject.hasErrorContaining(messages.mustBeInComponent()).onSource(builder); 263 }); 264 } 265 266 @Test testCreatorMissingFactoryMethodFails()267 public void testCreatorMissingFactoryMethodFails() { 268 Source componentFile = 269 preprocessedJavaSource( 270 "test.ParentComponent", 271 "package test;", 272 "", 273 "import dagger.Component;", 274 "import javax.inject.Provider;", 275 "", 276 "@Component", 277 "interface ParentComponent {", 278 " ChildComponent.Builder childComponentBuilder();", 279 "}"); 280 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 281 "package test;", 282 "", 283 "import dagger.Subcomponent;", 284 "", 285 "@Subcomponent", 286 "interface ChildComponent {", 287 " @Subcomponent.Builder", 288 " interface Builder {}", 289 "}"); 290 CompilerTests.daggerCompiler(componentFile, childComponentFile) 291 .compile( 292 subject -> { 293 subject.hasErrorCount(1); 294 subject.hasErrorContaining(messages.missingFactoryMethod()) 295 .onSource(childComponentFile); 296 }); 297 } 298 299 @Test testPrivateCreatorFails()300 public void testPrivateCreatorFails() { 301 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 302 "package test;", 303 "", 304 "import dagger.Subcomponent;", 305 "", 306 "@Subcomponent", 307 "abstract class ChildComponent {", 308 " @Subcomponent.Builder", 309 " private interface Builder {}", 310 "}"); 311 CompilerTests.daggerCompiler(childComponentFile) 312 .compile( 313 subject -> { 314 subject.hasErrorCount(1); 315 subject.hasErrorContaining(messages.isPrivate()).onSource(childComponentFile); 316 }); 317 } 318 319 @Test testNonStaticCreatorFails()320 public void testNonStaticCreatorFails() { 321 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 322 "package test;", 323 "", 324 "import dagger.Subcomponent;", 325 "", 326 "@Subcomponent", 327 "abstract class ChildComponent {", 328 " @Subcomponent.Builder", 329 " abstract class Builder {}", 330 "}"); 331 CompilerTests.daggerCompiler(childComponentFile) 332 .compile( 333 subject -> { 334 subject.hasErrorCount(1); 335 subject.hasErrorContaining(messages.mustBeStatic()).onSource(childComponentFile); 336 }); 337 } 338 339 @Test testNonAbstractCreatorFails()340 public void testNonAbstractCreatorFails() { 341 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 342 "package test;", 343 "", 344 "import dagger.Subcomponent;", 345 "", 346 "@Subcomponent", 347 "abstract class ChildComponent {", 348 " @Subcomponent.Builder", 349 " static class Builder {}", 350 "}"); 351 CompilerTests.daggerCompiler(childComponentFile) 352 .compile( 353 subject -> { 354 subject.hasErrorCount(1); 355 subject.hasErrorContaining(messages.mustBeAbstract()) 356 .onSource(childComponentFile); 357 }); 358 } 359 360 @Test testCreatorOneConstructorWithArgsFails()361 public void testCreatorOneConstructorWithArgsFails() { 362 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 363 "package test;", 364 "", 365 "import dagger.Subcomponent;", 366 "", 367 "@Subcomponent", 368 "abstract class ChildComponent {", 369 " @Subcomponent.Builder", 370 " static abstract class Builder {", 371 " Builder(String unused) {}", 372 " abstract ChildComponent build();", 373 " }", 374 "}"); 375 CompilerTests.daggerCompiler(childComponentFile) 376 .compile( 377 subject -> { 378 subject.hasErrorCount(1); 379 subject.hasErrorContaining(messages.invalidConstructor()) 380 .onSource(childComponentFile); 381 }); 382 } 383 384 @Test testCreatorMoreThanOneConstructorFails()385 public void testCreatorMoreThanOneConstructorFails() { 386 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 387 "package test;", 388 "", 389 "import dagger.Subcomponent;", 390 "", 391 "@Subcomponent", 392 "abstract class ChildComponent {", 393 " @Subcomponent.Builder", 394 " static abstract class Builder {", 395 " Builder() {}", 396 " Builder(String unused) {}", 397 " abstract ChildComponent build();", 398 " }", 399 "}"); 400 CompilerTests.daggerCompiler(childComponentFile) 401 .compile( 402 subject -> { 403 subject.hasErrorCount(1); 404 subject.hasErrorContaining(messages.invalidConstructor()) 405 .onSource(childComponentFile); 406 }); 407 } 408 409 @Test testCreatorEnumFails()410 public void testCreatorEnumFails() { 411 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 412 "package test;", 413 "", 414 "import dagger.Subcomponent;", 415 "", 416 "@Subcomponent", 417 "abstract class ChildComponent {", 418 " @Subcomponent.Builder", 419 " enum Builder {}", 420 "}"); 421 CompilerTests.daggerCompiler(childComponentFile) 422 .compile( 423 subject -> { 424 subject.hasErrorCount(1); 425 subject.hasErrorContaining(messages.mustBeClassOrInterface()) 426 .onSource(childComponentFile); 427 }); 428 } 429 430 @Test testCreatorFactoryMethodReturnsWrongTypeFails()431 public void testCreatorFactoryMethodReturnsWrongTypeFails() { 432 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 433 "package test;", 434 "", 435 "import dagger.Subcomponent;", 436 "", 437 "@Subcomponent", 438 "abstract class ChildComponent {", 439 " @Subcomponent.Builder", 440 " interface Builder {", 441 " String build();", 442 " }", 443 "}"); 444 CompilerTests.daggerCompiler(childComponentFile) 445 .compile( 446 subject -> { 447 subject.hasErrorCount(1); 448 subject.hasErrorContaining(messages.factoryMethodMustReturnComponentType()) 449 .onSource(childComponentFile) 450 .onLine(9); 451 }); 452 } 453 454 @Test testInheritedCreatorFactoryMethodReturnsWrongTypeFails()455 public void testInheritedCreatorFactoryMethodReturnsWrongTypeFails() { 456 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 457 "package test;", 458 "", 459 "import dagger.Subcomponent;", 460 "", 461 "@Subcomponent", 462 "abstract class ChildComponent {", 463 " interface Parent {", 464 " String build();", 465 " }", 466 "", 467 " @Subcomponent.Builder", 468 " interface Builder extends Parent {}", 469 "}"); 470 471 CompilerTests.daggerCompiler(childComponentFile) 472 .compile( 473 subject -> { 474 subject.hasErrorCount(1); 475 subject.hasErrorContaining( 476 String.format( 477 messages.inheritedFactoryMethodMustReturnComponentType(), 478 process("String test.ChildComponent.Parent.build()"))) 479 .onSource(childComponentFile) 480 .onLine(12); 481 }); 482 } 483 484 @Test testTwoFactoryMethodsFails()485 public void testTwoFactoryMethodsFails() { 486 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 487 "package test;", 488 "", 489 "import dagger.Subcomponent;", 490 "", 491 "@Subcomponent", 492 "abstract class ChildComponent {", 493 " @Subcomponent.Builder", 494 " interface Builder {", 495 " ChildComponent build();", 496 " ChildComponent build1();", 497 " }", 498 "}"); 499 500 CompilerTests.daggerCompiler(childComponentFile) 501 .compile( 502 subject -> { 503 subject.hasErrorCount(1); 504 subject.hasErrorContaining( 505 String.format( 506 messages.twoFactoryMethods(), 507 process("test.ChildComponent test.ChildComponent.Builder.build()"))) 508 .onSource(childComponentFile) 509 .onLine(10); 510 }); 511 } 512 513 @Test testInheritedTwoFactoryMethodsFails()514 public void testInheritedTwoFactoryMethodsFails() { 515 Source childComponentFile = preprocessedJavaSource("test.ChildComponent", 516 "package test;", 517 "", 518 "import dagger.Subcomponent;", 519 "", 520 "@Subcomponent", 521 "abstract class ChildComponent {", 522 " interface Parent {", 523 " ChildComponent build();", 524 " ChildComponent build1();", 525 " }", 526 "", 527 " @Subcomponent.Builder", 528 " interface Builder extends Parent {}", 529 "}"); 530 531 CompilerTests.daggerCompiler(childComponentFile) 532 .compile( 533 subject -> { 534 subject.hasErrorCount(1); 535 subject.hasErrorContaining( 536 String.format( 537 messages.inheritedTwoFactoryMethods(), 538 process("test.ChildComponent test.ChildComponent.Parent.build()"), 539 process("test.ChildComponent test.ChildComponent.Parent.build1()"))) 540 .onSource(childComponentFile) 541 .onLine(13); 542 }); 543 } 544 545 @Test testMultipleSettersPerTypeFails()546 public void testMultipleSettersPerTypeFails() { 547 Source moduleFile = 548 CompilerTests.javaSource( 549 "test.TestModule", 550 "package test;", 551 "", 552 "import dagger.Module;", 553 "import dagger.Provides;", 554 "", 555 "@Module", 556 "final class TestModule {", 557 " @Provides String s() { return \"\"; }", 558 "}"); 559 Source componentFile = 560 preprocessedJavaSource( 561 "test.ParentComponent", 562 "package test;", 563 "", 564 "import dagger.Component;", 565 "", 566 "@Component", 567 "interface ParentComponent {", 568 " ChildComponent.Builder childComponentBuilder();", 569 "}"); 570 Source childComponentFile = 571 javaFileBuilder("test.ChildComponent") 572 .addLines( 573 "package test;", 574 "", 575 "import dagger.Subcomponent;", 576 "import javax.inject.Provider;", 577 "", 578 "@Subcomponent(modules = TestModule.class)", 579 "abstract class ChildComponent {", 580 " abstract String s();", 581 "") 582 .addLinesIf( 583 BUILDER, 584 " @Subcomponent.Builder", 585 " interface Builder {", 586 " ChildComponent build();", 587 " void set1(TestModule s);", 588 " void set2(TestModule s);", 589 " }") 590 .addLinesIf( 591 FACTORY, 592 " @Subcomponent.Factory", 593 " interface Factory {", 594 " ChildComponent create(TestModule m1, TestModule m2);", 595 " }") 596 .addLines( // 597 "}") 598 .buildSource(); 599 600 String elements = 601 creatorKind.equals(BUILDER) 602 ? "[void test.ChildComponent.Builder.set1(test.TestModule), " 603 + "void test.ChildComponent.Builder.set2(test.TestModule)]" 604 : "[test.TestModule m1, test.TestModule m2]"; 605 CompilerTests.daggerCompiler(moduleFile, componentFile, childComponentFile) 606 .compile( 607 subject -> { 608 subject.hasErrorCount(1); 609 subject.hasErrorContaining( 610 String.format( 611 messages.multipleSettersForModuleOrDependencyType(), 612 "test.TestModule", 613 elements)) 614 .onSource(childComponentFile) 615 .onLine(11); 616 }); 617 } 618 619 @Test testMultipleSettersPerTypeIncludingResolvedGenericsFails()620 public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() { 621 Source moduleFile = 622 CompilerTests.javaSource( 623 "test.TestModule", 624 "package test;", 625 "", 626 "import dagger.Module;", 627 "import dagger.Provides;", 628 "", 629 "@Module", 630 "final class TestModule {", 631 " @Provides String s() { return \"\"; }", 632 "}"); 633 Source componentFile = 634 preprocessedJavaSource( 635 "test.ParentComponent", 636 "package test;", 637 "", 638 "import dagger.Component;", 639 "", 640 "@Component", 641 "interface ParentComponent {", 642 " ChildComponent.Builder childComponentBuilder();", 643 "}"); 644 Source childComponentFile = 645 javaFileBuilder("test.ChildComponent") 646 .addLines( 647 "package test;", 648 "", 649 "import dagger.Subcomponent;", 650 "import javax.inject.Provider;", 651 "", 652 "@Subcomponent(modules = TestModule.class)", 653 "abstract class ChildComponent {", 654 " abstract String s();", 655 "") 656 .addLinesIf( 657 BUILDER, 658 " interface Parent<T> {", 659 " void set1(T t);", 660 " }", 661 "", 662 " @Subcomponent.Builder", 663 " interface Builder extends Parent<TestModule> {", 664 " ChildComponent build();", 665 " void set2(TestModule s);", 666 " }") 667 .addLinesIf( 668 FACTORY, 669 " interface Parent<C, T> {", 670 " C create(TestModule m1, T t);", 671 " }", 672 "", 673 " @Subcomponent.Factory", 674 " interface Factory extends Parent<ChildComponent, TestModule> {}") 675 .addLines( // 676 "}") 677 .buildSource(); 678 679 String elements = 680 creatorKind.equals(BUILDER) 681 ? "[void test.ChildComponent.Builder.set1(test.TestModule), " 682 + "void test.ChildComponent.Builder.set2(test.TestModule)]" 683 : "[test.TestModule m1, test.TestModule t]"; 684 CompilerTests.daggerCompiler(moduleFile, componentFile, childComponentFile) 685 .compile( 686 subject -> { 687 subject.hasErrorCount(1); 688 subject.hasErrorContaining( 689 String.format( 690 messages.multipleSettersForModuleOrDependencyType(), 691 "test.TestModule", 692 elements)) 693 .onSource(childComponentFile) 694 .onLine(15); 695 }); 696 } 697 698 @Test testMultipleSettersPerBoundInstanceTypeFails()699 public void testMultipleSettersPerBoundInstanceTypeFails() { 700 Source componentFile = 701 preprocessedJavaSource( 702 "test.ParentComponent", 703 "package test;", 704 "", 705 "import dagger.Component;", 706 "", 707 "@Component", 708 "interface ParentComponent {", 709 " ChildComponent.Builder childComponentBuilder();", 710 "}"); 711 Source childComponentFile = 712 javaFileBuilder("test.ChildComponent") 713 .addLines( 714 "package test;", 715 "", 716 "import dagger.BindsInstance;", 717 "import dagger.Subcomponent;", 718 "", 719 "@Subcomponent", 720 "abstract class ChildComponent {", 721 " abstract String s();", 722 "") 723 .addLinesIf( 724 BUILDER, 725 " @Subcomponent.Builder", 726 " interface Builder {", 727 " ChildComponent build();", 728 " @BindsInstance void set1(String s);", 729 " @BindsInstance void set2(String s);", 730 " }") 731 .addLinesIf( 732 FACTORY, 733 " @Subcomponent.Factory", 734 " interface Factory {", 735 " ChildComponent create(", 736 " @BindsInstance String s1, @BindsInstance String s2);", 737 " }") 738 .addLines( // 739 "}") 740 .buildSource(); 741 742 String firstBinding = creatorKind.equals(FACTORY) 743 ? "ChildComponent.Factory.create(s1, …)" 744 : "@BindsInstance void ChildComponent.Builder.set1(String)"; 745 String secondBinding = creatorKind.equals(FACTORY) 746 ? "ChildComponent.Factory.create(…, s2)" 747 : "@BindsInstance void ChildComponent.Builder.set2(String)"; 748 CompilerTests.daggerCompiler(componentFile, childComponentFile) 749 .compile( 750 subject -> { 751 subject.hasErrorCount(1); 752 subject.hasErrorContaining("String is bound multiple times:"); 753 subject.hasErrorContaining(" " + firstBinding); 754 subject.hasErrorContaining(" " + secondBinding); 755 subject.hasErrorContaining(" in component: [ParentComponent → ChildComponent]") 756 .onSource(componentFile) 757 .onLineContaining("interface ParentComponent {"); 758 }); 759 } 760 761 @Test testExtraSettersFails()762 public void testExtraSettersFails() { 763 Source componentFile = preprocessedJavaSource("test.ParentComponent", 764 "package test;", 765 "", 766 "import dagger.Component;", 767 "import javax.inject.Provider;", 768 "", 769 "@Component", 770 "interface ParentComponent {", 771 " ChildComponent.Builder build();", 772 "}"); 773 Source childComponentFile = 774 javaFileBuilder("test.ChildComponent") 775 .addLines( 776 "package test;", 777 "", 778 "import dagger.Subcomponent;", 779 "import javax.inject.Provider;", 780 "", 781 "@Subcomponent", 782 "abstract class ChildComponent {") 783 .addLinesIf( 784 BUILDER, 785 " @Subcomponent.Builder", 786 " interface Builder {", 787 " ChildComponent build();", 788 " void set1(String s);", 789 " void set2(Integer s);", 790 " }") 791 .addLinesIf( 792 FACTORY, 793 " @Subcomponent.Factory", 794 " interface Factory {", 795 " ChildComponent create(String s, Integer i);", 796 " }") 797 .addLines("}") 798 .buildSource(); 799 800 String elements = 801 creatorKind.equals(FACTORY) 802 ? "[String s, Integer i]" 803 : "[void test.ChildComponent.Builder.set1(String)," 804 + " void test.ChildComponent.Builder.set2(Integer)]"; 805 CompilerTests.daggerCompiler(componentFile, childComponentFile) 806 .compile( 807 subject -> { 808 subject.hasErrorCount(1); 809 subject.hasErrorContaining(String.format(messages.extraSetters(), elements)) 810 .onSource(childComponentFile) 811 .onLine(9); 812 }); 813 } 814 815 @Test testMissingSettersFail()816 public void testMissingSettersFail() { 817 Source moduleFile = 818 CompilerTests.javaSource( 819 "test.TestModule", 820 "package test;", 821 "", 822 "import dagger.Module;", 823 "import dagger.Provides;", 824 "", 825 "@Module", 826 "final class TestModule {", 827 " TestModule(String unused) {}", 828 " @Provides String s() { return null; }", 829 "}"); 830 Source module2File = 831 CompilerTests.javaSource( 832 "test.Test2Module", 833 "package test;", 834 "", 835 "import dagger.Module;", 836 "import dagger.Provides;", 837 "", 838 "@Module", 839 "final class Test2Module {", 840 " @Provides Integer i() { return null; }", 841 "}"); 842 Source module3File = 843 CompilerTests.javaSource( 844 "test.Test3Module", 845 "package test;", 846 "", 847 "import dagger.Module;", 848 "import dagger.Provides;", 849 "", 850 "@Module", 851 "final class Test3Module {", 852 " Test3Module(String unused) {}", 853 " @Provides Double d() { return null; }", 854 "}"); 855 Source componentFile = 856 preprocessedJavaSource( 857 "test.ParentComponent", 858 "package test;", 859 "", 860 "import dagger.Component;", 861 "import javax.inject.Provider;", 862 "", 863 "@Component", 864 "interface ParentComponent {", 865 " ChildComponent.Builder build();", 866 "}"); 867 Source childComponentFile = 868 preprocessedJavaSource( 869 "test.ChildComponent", 870 "package test;", 871 "", 872 "import dagger.Subcomponent;", 873 "", 874 "@Subcomponent(modules = {TestModule.class, Test2Module.class, Test3Module.class})", 875 "interface ChildComponent {", 876 " String string();", 877 " Integer integer();", 878 "", 879 " @Subcomponent.Builder", 880 " interface Builder {", 881 " ChildComponent build();", 882 " }", 883 "}"); 884 885 CompilerTests.daggerCompiler( 886 moduleFile, module2File, module3File, componentFile, childComponentFile) 887 .compile( 888 subject -> { 889 subject.hasErrorCount(1); 890 subject.hasErrorContaining( 891 // Ignores Test2Module because we can construct it ourselves. 892 // TODO(sameb): Ignore Test3Module because it's not used within transitive 893 // dependencies. 894 String.format( 895 messages.missingSetters(), 896 "[test.TestModule, test.Test3Module]")) 897 .onSource(childComponentFile) 898 .onLine(11); 899 }); 900 } 901 902 @Test covariantFactoryMethodReturnType()903 public void covariantFactoryMethodReturnType() { 904 Source foo = 905 CompilerTests.javaSource( 906 "test.Foo", 907 "package test;", 908 "", 909 "import javax.inject.Inject;", 910 "", 911 "class Foo {", 912 " @Inject Foo() {}", 913 "}"); 914 Source supertype = 915 CompilerTests.javaSource( 916 "test.Supertype", 917 "package test;", 918 "", 919 "interface Supertype {", 920 " Foo foo();", 921 "}"); 922 923 Source subcomponent = 924 preprocessedJavaSource( 925 "test.HasSupertype", 926 "package test;", 927 "", 928 "import dagger.Subcomponent;", 929 "", 930 "@Subcomponent", 931 "interface HasSupertype extends Supertype {", 932 " @Subcomponent.Builder", 933 " interface Builder {", 934 " Supertype build();", 935 " }", 936 "}"); 937 938 CompilerTests.daggerCompiler(foo, supertype, subcomponent) 939 .compile( 940 subject -> { 941 subject.hasErrorCount(0); 942 subject.hasWarningCount(0); 943 }); 944 } 945 946 @Test covariantFactoryMethodReturnType_hasNewMethod()947 public void covariantFactoryMethodReturnType_hasNewMethod() { 948 Source foo = 949 CompilerTests.javaSource( 950 "test.Foo", 951 "package test;", 952 "", 953 "import javax.inject.Inject;", 954 "", 955 "class Foo {", 956 " @Inject Foo() {}", 957 "}"); 958 Source bar = 959 CompilerTests.javaSource( 960 "test.Bar", 961 "package test;", 962 "", 963 "import javax.inject.Inject;", 964 "", 965 "class Bar {", 966 " @Inject Bar() {}", 967 "}"); 968 Source supertype = 969 CompilerTests.javaSource( 970 "test.Supertype", 971 "package test;", 972 "", 973 "interface Supertype {", 974 " Foo foo();", 975 "}"); 976 977 Source subcomponent = 978 preprocessedJavaSource( 979 "test.HasSupertype", 980 "package test;", 981 "", 982 "import dagger.Subcomponent;", 983 "", 984 "@Subcomponent", 985 "interface HasSupertype extends Supertype {", 986 " Bar bar();", 987 "", 988 " @Subcomponent.Builder", 989 " interface Builder {", 990 " Supertype build();", 991 " }", 992 "}"); 993 994 CompilerTests.daggerCompiler(foo, bar, supertype, subcomponent) 995 .compile( 996 subject -> { 997 subject.hasErrorCount(0); 998 subject.hasWarningCount(1); 999 subject.hasWarningContaining( 1000 process( 1001 "test.HasSupertype.Builder.build() returns test.Supertype, but " 1002 + "test.HasSupertype declares additional component method(s): bar(). " 1003 + "In order to provide type-safe access to these methods, override " 1004 + "build() to return test.HasSupertype")) 1005 .onSource(subcomponent) 1006 .onLine(11); 1007 }); 1008 } 1009 1010 @Test covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited()1011 public void covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited() { 1012 Source foo = 1013 CompilerTests.javaSource( 1014 "test.Foo", 1015 "package test;", 1016 "", 1017 "import javax.inject.Inject;", 1018 "", 1019 "class Foo {", 1020 " @Inject Foo() {}", 1021 "}"); 1022 Source bar = 1023 CompilerTests.javaSource( 1024 "test.Bar", 1025 "package test;", 1026 "", 1027 "import javax.inject.Inject;", 1028 "", 1029 "class Bar {", 1030 " @Inject Bar() {}", 1031 "}"); 1032 Source supertype = 1033 CompilerTests.javaSource( 1034 "test.Supertype", 1035 "package test;", 1036 "", 1037 "interface Supertype {", 1038 " Foo foo();", 1039 "}"); 1040 1041 Source creatorSupertype = 1042 preprocessedJavaSource( 1043 "test.CreatorSupertype", 1044 "package test;", 1045 "", 1046 "interface CreatorSupertype {", 1047 " Supertype build();", 1048 "}"); 1049 1050 Source subcomponent = 1051 preprocessedJavaSource( 1052 "test.HasSupertype", 1053 "package test;", 1054 "", 1055 "import dagger.Subcomponent;", 1056 "", 1057 "@Subcomponent", 1058 "interface HasSupertype extends Supertype {", 1059 " Bar bar();", 1060 "", 1061 " @Subcomponent.Builder", 1062 " interface Builder extends CreatorSupertype {}", 1063 "}"); 1064 1065 CompilerTests.daggerCompiler(foo, bar, supertype, creatorSupertype, subcomponent) 1066 .compile( 1067 subject -> { 1068 subject.hasErrorCount(0); 1069 subject.hasWarningCount(1); 1070 subject.hasWarningContaining( 1071 process( 1072 "[test.CreatorSupertype.build()] test.HasSupertype.Builder.build() returns " 1073 + "test.Supertype, but test.HasSupertype declares additional component " 1074 + "method(s): bar(). In order to provide type-safe access to these " 1075 + "methods, override build() to return test.HasSupertype")); 1076 }); 1077 } 1078 } 1079