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