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.common.collect.Sets.immutableEnumSet; 20 import static com.google.testing.compile.CompilationSubject.assertThat; 21 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; 22 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; 23 import static dagger.internal.codegen.Compilers.daggerCompiler; 24 import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_BUILDER; 25 import static dagger.internal.codegen.ComponentCreatorAnnotation.COMPONENT_FACTORY; 26 import static dagger.internal.codegen.ComponentCreatorKind.BUILDER; 27 import static dagger.internal.codegen.ComponentCreatorKind.FACTORY; 28 import static dagger.internal.codegen.ComponentKind.COMPONENT; 29 import static dagger.internal.codegen.ErrorMessages.componentMessagesFor; 30 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION; 31 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION; 32 33 import com.google.common.collect.ImmutableList; 34 import com.google.common.collect.Iterables; 35 import com.google.common.collect.Sets; 36 import com.google.testing.compile.Compilation; 37 import com.google.testing.compile.JavaFileObjects; 38 import java.util.Collection; 39 import java.util.List; 40 import java.util.Set; 41 import javax.tools.JavaFileObject; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.junit.runners.Parameterized; 45 import org.junit.runners.Parameterized.Parameters; 46 47 /** Tests for properties of component creators shared by both builders and factories. */ 48 @RunWith(Parameterized.class) 49 public class ComponentCreatorTest extends ComponentCreatorTestHelper { 50 @Parameters(name = "compilerMode={0}, creatorKind={1}") parameters()51 public static Collection<Object[]> parameters() { 52 Set<List<Object>> params = 53 Sets.<Object>cartesianProduct( 54 immutableEnumSet(DEFAULT_MODE, FAST_INIT_MODE), 55 immutableEnumSet(COMPONENT_BUILDER, COMPONENT_FACTORY)); 56 return ImmutableList.copyOf(Iterables.transform(params, Collection::toArray)); 57 } 58 ComponentCreatorTest( CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation)59 public ComponentCreatorTest( 60 CompilerMode compilerMode, ComponentCreatorAnnotation componentCreatorAnnotation) { 61 super(compilerMode, componentCreatorAnnotation); 62 } 63 64 @Test testEmptyCreator()65 public void testEmptyCreator() { 66 JavaFileObject injectableTypeFile = 67 JavaFileObjects.forSourceLines( 68 "test.SomeInjectableType", 69 "package test;", 70 "", 71 "import javax.inject.Inject;", 72 "", 73 "final class SomeInjectableType {", 74 " @Inject SomeInjectableType() {}", 75 "}"); 76 JavaFileObject componentFile = 77 preprocessedJavaFile( 78 "test.SimpleComponent", 79 "package test;", 80 "", 81 "import dagger.Component;", 82 "import javax.inject.Provider;", 83 "", 84 "@Component", 85 "interface SimpleComponent {", 86 " SomeInjectableType someInjectableType();", 87 "", 88 " @Component.Builder", 89 " static interface Builder {", 90 " SimpleComponent build();", 91 " }", 92 "}"); 93 JavaFileObject generatedComponent = 94 preprocessedJavaFile( 95 "test.DaggerSimpleComponent", 96 "package test;", 97 "", 98 GENERATED_ANNOTATION, 99 "final class DaggerSimpleComponent implements SimpleComponent {", 100 " private static final class Builder implements SimpleComponent.Builder {", 101 " @Override", 102 " public SimpleComponent build() {", 103 " return new DaggerSimpleComponent();", 104 " }", 105 " }", 106 "}"); 107 Compilation compilation = compile(injectableTypeFile, componentFile); 108 assertThat(compilation).succeeded(); 109 assertThat(compilation) 110 .generatedSourceFile("test.DaggerSimpleComponent") 111 .containsElementsIn(generatedComponent); 112 } 113 114 @Test testCanInstantiateModulesUserCannotSet()115 public void testCanInstantiateModulesUserCannotSet() { 116 JavaFileObject module = 117 JavaFileObjects.forSourceLines( 118 "test.TestModule", 119 "package test;", 120 "", 121 "import dagger.Module;", 122 "import dagger.Provides;", 123 "", 124 "@Module", 125 "final class TestModule {", 126 " @Provides String string() { return null; }", 127 "}"); 128 129 JavaFileObject componentFile = 130 preprocessedJavaFile( 131 "test.TestComponent", 132 "package test;", 133 "", 134 "import dagger.Component;", 135 "", 136 "@Component(modules = TestModule.class)", 137 "interface TestComponent {", 138 " String string();", 139 "", 140 " @Component.Builder", 141 " interface Builder {", 142 " TestComponent build();", 143 " }", 144 "}"); 145 JavaFileObject generatedComponent = 146 preprocessedJavaFile( 147 "test.DaggerTestComponent", 148 "package test;", 149 "", 150 IMPORT_GENERATED_ANNOTATION, 151 "", 152 GENERATED_ANNOTATION, 153 "final class DaggerTestComponent implements TestComponent {", 154 " private final TestModule testModule;", 155 "", 156 " private DaggerTestComponent(TestModule testModuleParam) {", 157 " this.testModule = testModuleParam;", 158 " }", 159 "", 160 " public static TestComponent.Builder builder() {", 161 " return new Builder();", 162 " }", 163 "", 164 " public static TestComponent create() {", 165 " return new Builder().build();", 166 " }", 167 "", 168 " @Override", 169 " public String string() {", 170 " return TestModule_StringFactory.string(testModule);", 171 " }", 172 "", 173 " private static final class Builder implements TestComponent.Builder {", 174 " @Override", 175 " public TestComponent build() {", 176 " return new DaggerTestComponent(new TestModule());", 177 " }", 178 " }", 179 "}"); 180 Compilation compilation = compile(module, componentFile); 181 assertThat(compilation).succeeded(); 182 assertThat(compilation) 183 .generatedSourceFile("test.DaggerTestComponent") 184 .hasSourceEquivalentTo(generatedComponent); 185 } 186 187 @Test testMoreThanOneCreatorOfSameTypeFails()188 public void testMoreThanOneCreatorOfSameTypeFails() { 189 JavaFileObject componentFile = 190 preprocessedJavaFile( 191 "test.SimpleComponent", 192 "package test;", 193 "", 194 "import dagger.Component;", 195 "import javax.inject.Provider;", 196 "", 197 "@Component", 198 "interface SimpleComponent {", 199 " @Component.Builder", 200 " static interface Builder {", 201 " SimpleComponent build();", 202 " }", 203 "", 204 " @Component.Builder", 205 " interface Builder2 {", 206 " SimpleComponent build();", 207 " }", 208 "}"); 209 Compilation compilation = compile(componentFile); 210 assertThat(compilation).failed(); 211 assertThat(compilation) 212 .hadErrorContaining( 213 String.format( 214 componentMessagesFor(COMPONENT).moreThanOne(), 215 process("[test.SimpleComponent.Builder, test.SimpleComponent.Builder2]"))) 216 .inFile(componentFile); 217 } 218 219 @Test testBothBuilderAndFactoryFails()220 public void testBothBuilderAndFactoryFails() { 221 JavaFileObject componentFile = 222 JavaFileObjects.forSourceLines( 223 "test.SimpleComponent", 224 "package test;", 225 "", 226 "import dagger.Component;", 227 "import javax.inject.Provider;", 228 "", 229 "@Component", 230 "interface SimpleComponent {", 231 " @Component.Builder", 232 " static interface Builder {", 233 " SimpleComponent build();", 234 " }", 235 "", 236 " @Component.Factory", 237 " interface Factory {", 238 " SimpleComponent create();", 239 " }", 240 "}"); 241 Compilation compilation = compile(componentFile); 242 assertThat(compilation).failed(); 243 assertThat(compilation) 244 .hadErrorContaining( 245 String.format( 246 componentMessagesFor(COMPONENT).moreThanOne(), 247 "[test.SimpleComponent.Builder, test.SimpleComponent.Factory]")) 248 .inFile(componentFile); 249 } 250 251 @Test testGenericCreatorTypeFails()252 public void testGenericCreatorTypeFails() { 253 JavaFileObject componentFile = 254 preprocessedJavaFile( 255 "test.SimpleComponent", 256 "package test;", 257 "", 258 "import dagger.Component;", 259 "import javax.inject.Provider;", 260 "", 261 "@Component", 262 "interface SimpleComponent {", 263 " @Component.Builder", 264 " interface Builder<T> {", 265 " SimpleComponent build();", 266 " }", 267 "}"); 268 Compilation compilation = compile(componentFile); 269 assertThat(compilation).failed(); 270 assertThat(compilation).hadErrorContaining(messages.generics()).inFile(componentFile); 271 } 272 273 @Test testCreatorNotInComponentFails()274 public void testCreatorNotInComponentFails() { 275 JavaFileObject builder = 276 preprocessedJavaFile( 277 "test.Builder", 278 "package test;", 279 "", 280 "import dagger.Component;", 281 "", 282 "@Component.Builder", 283 "interface Builder {}"); 284 Compilation compilation = compile(builder); 285 assertThat(compilation).failed(); 286 assertThat(compilation).hadErrorContaining(messages.mustBeInComponent()).inFile(builder); 287 } 288 289 @Test testCreatorMissingFactoryMethodFails()290 public void testCreatorMissingFactoryMethodFails() { 291 JavaFileObject componentFile = 292 preprocessedJavaFile( 293 "test.SimpleComponent", 294 "package test;", 295 "", 296 "import dagger.Component;", 297 "import javax.inject.Provider;", 298 "", 299 "@Component", 300 "interface SimpleComponent {", 301 " @Component.Builder", 302 " interface Builder {}", 303 "}"); 304 Compilation compilation = compile(componentFile); 305 assertThat(compilation).failed(); 306 assertThat(compilation) 307 .hadErrorContaining(messages.missingFactoryMethod()) 308 .inFile(componentFile); 309 } 310 311 @Test testCreatorWithBindsInstanceNoStaticCreateGenerated()312 public void testCreatorWithBindsInstanceNoStaticCreateGenerated() { 313 JavaFileObject componentFile = 314 javaFileBuilder("test.SimpleComponent") 315 .addLines( 316 "package test;", 317 "", 318 "import dagger.BindsInstance;", 319 "import dagger.Component;", 320 "import javax.inject.Provider;", 321 "", 322 "@Component", 323 "interface SimpleComponent {", 324 " Object object();", 325 "") 326 .addLinesIf( 327 BUILDER, 328 " @Component.Builder", 329 " interface Builder {", 330 " @BindsInstance Builder object(Object object);", 331 " SimpleComponent build();", 332 " }") 333 .addLinesIf( 334 FACTORY, 335 " @Component.Factory", 336 " interface Factory {", 337 " SimpleComponent create(@BindsInstance Object object);", 338 " }") 339 .addLines("}") 340 .build(); 341 342 JavaFileObject generatedComponent = 343 javaFileBuilder("test.DaggerSimpleComponent") 344 .addLines( 345 "package test;", 346 "", 347 "import dagger.internal.Preconditions;", 348 IMPORT_GENERATED_ANNOTATION, 349 "", 350 GENERATED_ANNOTATION, 351 "final class DaggerSimpleComponent implements SimpleComponent {", 352 " private final Object object;", 353 "", 354 " private DaggerSimpleComponent(Object objectParam) {", 355 " this.object = objectParam;", 356 " }", 357 "") 358 .addLinesIf( 359 BUILDER, 360 " public static SimpleComponent.Builder builder() {", 361 " return new Builder();", 362 " }") 363 .addLinesIf( 364 FACTORY, 365 " public static SimpleComponent.Factory factory() {", 366 " return new Factory();", 367 " }") 368 .addLines( 369 "", // 370 " @Override", 371 " public Object object() {", 372 " return object;", 373 " }", 374 "") 375 .addLinesIf( 376 BUILDER, 377 " private static final class Builder implements SimpleComponent.Builder {", 378 " private Object object;", 379 "", 380 " @Override", 381 " public Builder object(Object object) {", 382 " this.object = Preconditions.checkNotNull(object);", 383 " return this;", 384 " }", 385 "", 386 " @Override", 387 " public SimpleComponent build() {", 388 " Preconditions.checkBuilderRequirement(object, Object.class);", 389 " return new DaggerSimpleComponent(object);", 390 " }", 391 " }") 392 .addLinesIf( 393 FACTORY, 394 " private static final class Factory implements SimpleComponent.Factory {", 395 " @Override", 396 " public SimpleComponent create(Object object) {", 397 " Preconditions.checkNotNull(object);", 398 " return new DaggerSimpleComponent(object);", 399 " }", 400 " }") 401 .addLines("}") 402 .build(); 403 404 Compilation compilation = compile(componentFile); 405 assertThat(compilation).succeededWithoutWarnings(); 406 assertThat(compilation) 407 .generatedSourceFile("test.DaggerSimpleComponent") 408 .hasSourceEquivalentTo(generatedComponent); 409 } 410 411 @Test testCreatorWithPrimitiveBindsInstance()412 public void testCreatorWithPrimitiveBindsInstance() { 413 JavaFileObject componentFile = 414 javaFileBuilder("test.SimpleComponent") 415 .addLines( 416 "package test;", 417 "", 418 "import dagger.BindsInstance;", 419 "import dagger.Component;", 420 "import javax.inject.Provider;", 421 "", 422 "@Component", 423 "interface SimpleComponent {", 424 " int anInt();", 425 "") 426 .addLinesIf( 427 BUILDER, 428 " @Component.Builder", 429 " interface Builder {", 430 " @BindsInstance Builder i(int i);", 431 " SimpleComponent build();", 432 " }") 433 .addLinesIf( 434 FACTORY, 435 " @Component.Factory", 436 " interface Factory {", 437 " SimpleComponent create(@BindsInstance int i);", 438 " }") 439 .addLines( 440 "}") 441 .build(); 442 443 JavaFileObject generatedComponent = 444 javaFileBuilder("test.DaggerSimpleComponent") 445 .addLines( 446 "package test;", 447 "", 448 "import dagger.internal.Preconditions;", 449 IMPORT_GENERATED_ANNOTATION, 450 "", 451 GENERATED_ANNOTATION, 452 "final class DaggerSimpleComponent implements SimpleComponent {", 453 " private final Integer i;", 454 "", 455 " private DaggerSimpleComponent(Integer iParam) {", 456 " this.i = iParam;", 457 " }", 458 "", 459 " @Override", 460 " public int anInt() {", 461 " return i;", 462 " }", 463 "") 464 .addLinesIf( 465 BUILDER, 466 " private static final class Builder implements SimpleComponent.Builder {", 467 " private Integer i;", 468 "", 469 " @Override", 470 " public Builder i(int i) {", 471 " this.i = Preconditions.checkNotNull(i);", 472 " return this;", 473 " }", 474 "", 475 " @Override", 476 " public SimpleComponent build() {", 477 " Preconditions.checkBuilderRequirement(i, Integer.class);", 478 " return new DaggerSimpleComponent(i);", 479 " }", 480 " }") 481 .addLinesIf( 482 FACTORY, 483 " private static final class Factory implements SimpleComponent.Factory {", 484 " @Override", 485 " public SimpleComponent create(int i) {", 486 " Preconditions.checkNotNull(i);", 487 " return new DaggerSimpleComponent(i);", 488 " }", 489 " }") 490 .addLines( 491 "}") 492 .build(); 493 494 Compilation compilation = compile(componentFile); 495 assertThat(compilation).succeededWithoutWarnings(); 496 assertThat(compilation) 497 .generatedSourceFile("test.DaggerSimpleComponent") 498 .containsElementsIn(generatedComponent); 499 } 500 501 @Test testPrivateCreatorFails()502 public void testPrivateCreatorFails() { 503 JavaFileObject componentFile = 504 preprocessedJavaFile( 505 "test.SimpleComponent", 506 "package test;", 507 "", 508 "import dagger.Component;", 509 "import javax.inject.Provider;", 510 "", 511 "@Component", 512 "abstract class SimpleComponent {", 513 " @Component.Builder", 514 " private interface Builder {}", 515 "}"); 516 Compilation compilation = compile(componentFile); 517 assertThat(compilation).failed(); 518 assertThat(compilation).hadErrorContaining(messages.isPrivate()).inFile(componentFile); 519 } 520 521 @Test testNonStaticCreatorFails()522 public void testNonStaticCreatorFails() { 523 JavaFileObject componentFile = 524 preprocessedJavaFile( 525 "test.SimpleComponent", 526 "package test;", 527 "", 528 "import dagger.Component;", 529 "import javax.inject.Provider;", 530 "", 531 "@Component", 532 "abstract class SimpleComponent {", 533 " @Component.Builder", 534 " abstract class Builder {}", 535 "}"); 536 Compilation compilation = compile(componentFile); 537 assertThat(compilation).failed(); 538 assertThat(compilation).hadErrorContaining(messages.mustBeStatic()).inFile(componentFile); 539 } 540 541 @Test testNonAbstractCreatorFails()542 public void testNonAbstractCreatorFails() { 543 JavaFileObject componentFile = 544 preprocessedJavaFile( 545 "test.SimpleComponent", 546 "package test;", 547 "", 548 "import dagger.Component;", 549 "import javax.inject.Provider;", 550 "", 551 "@Component", 552 "abstract class SimpleComponent {", 553 " @Component.Builder", 554 " static class Builder {}", 555 "}"); 556 Compilation compilation = compile(componentFile); 557 assertThat(compilation).failed(); 558 assertThat(compilation).hadErrorContaining(messages.mustBeAbstract()).inFile(componentFile); 559 } 560 561 @Test testCreatorOneConstructorWithArgsFails()562 public void testCreatorOneConstructorWithArgsFails() { 563 JavaFileObject componentFile = 564 preprocessedJavaFile( 565 "test.SimpleComponent", 566 "package test;", 567 "", 568 "import dagger.Component;", 569 "import javax.inject.Provider;", 570 "", 571 "@Component", 572 "abstract class SimpleComponent {", 573 " @Component.Builder", 574 " static abstract class Builder {", 575 " Builder(String unused) {}", 576 " }", 577 "}"); 578 Compilation compilation = compile(componentFile); 579 assertThat(compilation).failed(); 580 assertThat(compilation) 581 .hadErrorContaining(messages.invalidConstructor()) 582 .inFile(componentFile); 583 } 584 585 @Test testCreatorMoreThanOneConstructorFails()586 public void testCreatorMoreThanOneConstructorFails() { 587 JavaFileObject componentFile = 588 preprocessedJavaFile( 589 "test.SimpleComponent", 590 "package test;", 591 "", 592 "import dagger.Component;", 593 "import javax.inject.Provider;", 594 "", 595 "@Component", 596 "abstract class SimpleComponent {", 597 " @Component.Builder", 598 " static abstract class Builder {", 599 " Builder() {}", 600 " Builder(String unused) {}", 601 " }", 602 "}"); 603 Compilation compilation = compile(componentFile); 604 assertThat(compilation).failed(); 605 assertThat(compilation) 606 .hadErrorContaining(messages.invalidConstructor()) 607 .inFile(componentFile); 608 } 609 610 @Test testCreatorEnumFails()611 public void testCreatorEnumFails() { 612 JavaFileObject componentFile = 613 preprocessedJavaFile( 614 "test.SimpleComponent", 615 "package test;", 616 "", 617 "import dagger.Component;", 618 "import javax.inject.Provider;", 619 "", 620 "@Component", 621 "abstract class SimpleComponent {", 622 " @Component.Builder", 623 " enum Builder {}", 624 "}"); 625 Compilation compilation = compile(componentFile); 626 assertThat(compilation).failed(); 627 assertThat(compilation) 628 .hadErrorContaining(messages.mustBeClassOrInterface()) 629 .inFile(componentFile); 630 } 631 632 @Test testCreatorFactoryMethodReturnsWrongTypeFails()633 public void testCreatorFactoryMethodReturnsWrongTypeFails() { 634 JavaFileObject componentFile = 635 preprocessedJavaFile( 636 "test.SimpleComponent", 637 "package test;", 638 "", 639 "import dagger.Component;", 640 "import javax.inject.Provider;", 641 "", 642 "@Component", 643 "abstract class SimpleComponent {", 644 " @Component.Builder", 645 " interface Builder {", 646 " String build();", 647 " }", 648 "}"); 649 Compilation compilation = compile(componentFile); 650 assertThat(compilation).failed(); 651 assertThat(compilation) 652 .hadErrorContaining(messages.factoryMethodMustReturnComponentType()) 653 .inFile(componentFile) 654 .onLineContaining(process("String build();")); 655 } 656 657 @Test testCreatorSetterForNonBindsInstancePrimitiveFails()658 public void testCreatorSetterForNonBindsInstancePrimitiveFails() { 659 JavaFileObject component = 660 javaFileBuilder("test.TestComponent") 661 .addLines( 662 "package test;", 663 "", 664 "import dagger.Component;", 665 "", 666 "@Component", 667 "interface TestComponent {", 668 " Object object();", 669 "") 670 .addLinesIf( 671 BUILDER, 672 " @Component.Builder", 673 " interface Builder {", 674 " Builder primitive(long l);", 675 " TestComponent build();", 676 " }") 677 .addLinesIf( 678 FACTORY, 679 " @Component.Factory", 680 " interface Factory {", 681 " TestComponent create(long l);", 682 " }") 683 .addLines( // 684 "}") 685 .build(); 686 Compilation compilation = compile(component); 687 assertThat(compilation).failed(); 688 689 assertThat(compilation) 690 .hadErrorContaining(messages.nonBindsInstanceParametersMayNotBePrimitives()) 691 .inFile(component) 692 .onLineContaining("(long l)"); 693 } 694 695 @Test testInheritedBuilderBuildReturnsWrongTypeFails()696 public void testInheritedBuilderBuildReturnsWrongTypeFails() { 697 JavaFileObject componentFile = 698 preprocessedJavaFile( 699 "test.SimpleComponent", 700 "package test;", 701 "", 702 "import dagger.Component;", 703 "import javax.inject.Provider;", 704 "", 705 "@Component", 706 "abstract class SimpleComponent {", 707 " interface Parent {", 708 " String build();", 709 " }", 710 "", 711 " @Component.Builder", 712 " interface Builder extends Parent {}", 713 "}"); 714 Compilation compilation = compile(componentFile); 715 assertThat(compilation).failed(); 716 assertThat(compilation) 717 .hadErrorContaining( 718 String.format( 719 messages.inheritedFactoryMethodMustReturnComponentType(), process("build"))) 720 .inFile(componentFile) 721 .onLineContaining(process("interface Builder")); 722 } 723 724 @Test testTwoFactoryMethodsFails()725 public void testTwoFactoryMethodsFails() { 726 JavaFileObject componentFile = 727 preprocessedJavaFile( 728 "test.SimpleComponent", 729 "package test;", 730 "", 731 "import dagger.Component;", 732 "import javax.inject.Provider;", 733 "", 734 "@Component", 735 "abstract class SimpleComponent {", 736 " @Component.Builder", 737 " interface Builder {", 738 " SimpleComponent build();", 739 " SimpleComponent newSimpleComponent();", 740 " }", 741 "}"); 742 Compilation compilation = compile(componentFile); 743 assertThat(compilation).failed(); 744 assertThat(compilation) 745 .hadErrorContaining(String.format(messages.twoFactoryMethods(), process("build"))) 746 .inFile(componentFile) 747 .onLineContaining("SimpleComponent newSimpleComponent();"); 748 } 749 750 @Test testInheritedTwoFactoryMethodsFails()751 public void testInheritedTwoFactoryMethodsFails() { 752 JavaFileObject componentFile = 753 preprocessedJavaFile( 754 "test.SimpleComponent", 755 "package test;", 756 "", 757 "import dagger.Component;", 758 "import javax.inject.Provider;", 759 "", 760 "@Component", 761 "abstract class SimpleComponent {", 762 " interface Parent {", 763 " SimpleComponent build();", 764 " SimpleComponent newSimpleComponent();", 765 " }", 766 "", 767 " @Component.Builder", 768 " interface Builder extends Parent {}", 769 "}"); 770 Compilation compilation = compile(componentFile); 771 assertThat(compilation).failed(); 772 assertThat(compilation) 773 .hadErrorContaining( 774 String.format( 775 messages.inheritedTwoFactoryMethods(), process("build()"), "newSimpleComponent()")) 776 .inFile(componentFile) 777 .onLineContaining(process("interface Builder")); 778 } 779 780 @Test testMultipleSettersPerTypeFails()781 public void testMultipleSettersPerTypeFails() { 782 JavaFileObject moduleFile = 783 JavaFileObjects.forSourceLines( 784 "test.TestModule", 785 "package test;", 786 "", 787 "import dagger.Module;", 788 "import dagger.Provides;", 789 "", 790 "@Module", 791 "final class TestModule {", 792 " @Provides String s() { return \"\"; }", 793 "}"); 794 JavaFileObject componentFile = 795 javaFileBuilder("test.SimpleComponent") 796 .addLines( 797 "package test;", 798 "", 799 "import dagger.Component;", 800 "import javax.inject.Provider;", 801 "", 802 "@Component(modules = TestModule.class)", 803 "abstract class SimpleComponent {", 804 " abstract String s();", 805 "") 806 .addLinesIf( 807 BUILDER, 808 " @Component.Builder", 809 " interface Builder {", 810 " SimpleComponent build();", 811 " void set1(TestModule s);", 812 " void set2(TestModule s);", 813 " }") 814 .addLinesIf( 815 FACTORY, 816 " @Component.Factory", 817 " interface Factory {", 818 " SimpleComponent create(TestModule m1, TestModule m2);", 819 " }") 820 .addLines( // 821 "}") 822 .build(); 823 Compilation compilation = compile(moduleFile, componentFile); 824 assertThat(compilation).failed(); 825 String elements = 826 creatorKind.equals(BUILDER) 827 ? "[void test.SimpleComponent.Builder.set1(test.TestModule), " 828 + "void test.SimpleComponent.Builder.set2(test.TestModule)]" 829 : "[test.TestModule m1, test.TestModule m2]"; 830 assertThat(compilation) 831 .hadErrorContaining( 832 String.format( 833 messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) 834 .inFile(componentFile) 835 .onLineContaining(process("interface Builder")); 836 } 837 838 @Test testMultipleSettersPerTypeIncludingResolvedGenericsFails()839 public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() { 840 JavaFileObject moduleFile = 841 JavaFileObjects.forSourceLines( 842 "test.TestModule", 843 "package test;", 844 "", 845 "import dagger.Module;", 846 "import dagger.Provides;", 847 "", 848 "@Module", 849 "final class TestModule {", 850 " @Provides String s() { return \"\"; }", 851 "}"); 852 JavaFileObject componentFile = 853 javaFileBuilder("test.SimpleComponent") 854 .addLines( 855 "package test;", 856 "", 857 "import dagger.Component;", 858 "import javax.inject.Provider;", 859 "", 860 "@Component(modules = TestModule.class)", 861 "abstract class SimpleComponent {", 862 " abstract String s();", 863 "") 864 .addLinesIf( 865 BUILDER, 866 " interface Parent<T> {", 867 " void set1(T t);", 868 " }", 869 "", 870 " @Component.Builder", 871 " interface Builder extends Parent<TestModule> {", 872 " SimpleComponent build();", 873 " void set2(TestModule s);", 874 " }") 875 .addLinesIf( 876 FACTORY, 877 " interface Parent<C, T> {", 878 " C create(TestModule m1, T t);", 879 " }", 880 "", 881 " @Component.Factory", 882 " interface Factory extends Parent<SimpleComponent, TestModule> {}") 883 .addLines( // 884 "}") 885 .build(); 886 Compilation compilation = compile(moduleFile, componentFile); 887 assertThat(compilation).failed(); 888 String elements = 889 creatorKind.equals(BUILDER) 890 ? "[void test.SimpleComponent.Builder.set1(test.TestModule), " 891 + "void test.SimpleComponent.Builder.set2(test.TestModule)]" 892 : "[test.TestModule m1, test.TestModule t]"; 893 assertThat(compilation) 894 .hadErrorContaining( 895 String.format( 896 messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) 897 .inFile(componentFile) 898 .onLineContaining(process("interface Builder")); 899 } 900 901 @Test testExtraSettersFails()902 public void testExtraSettersFails() { 903 JavaFileObject componentFile = 904 javaFileBuilder("test.SimpleComponent") 905 .addLines( 906 "package test;", 907 "", 908 "import dagger.Component;", 909 "import javax.inject.Provider;", 910 "", 911 "@Component(modules = AbstractModule.class)", 912 "abstract class SimpleComponent {") 913 .addLinesIf( 914 BUILDER, 915 " @Component.Builder", 916 " interface Builder {", 917 " SimpleComponent build();", 918 " void abstractModule(AbstractModule abstractModule);", 919 " void other(String s);", 920 " }") 921 .addLinesIf( 922 FACTORY, 923 " @Component.Factory", 924 " interface Factory {", 925 " SimpleComponent create(AbstractModule abstractModule, String s);", 926 " }") 927 .addLines("}") 928 .build(); 929 JavaFileObject abstractModule = 930 JavaFileObjects.forSourceLines( 931 "test.AbstractModule", 932 "package test;", 933 "", 934 "import dagger.Module;", 935 "", 936 "@Module", 937 "abstract class AbstractModule {}"); 938 Compilation compilation = compile(componentFile, abstractModule); 939 assertThat(compilation).failed(); 940 String elements = 941 creatorKind.equals(BUILDER) 942 ? "[void test.SimpleComponent.Builder.abstractModule(test.AbstractModule), " 943 + "void test.SimpleComponent.Builder.other(String)]" 944 : "[test.AbstractModule abstractModule, String s]"; 945 assertThat(compilation) 946 .hadErrorContaining(String.format(messages.extraSetters(), elements)) 947 .inFile(componentFile) 948 .onLineContaining(process("interface Builder")); 949 } 950 951 @Test testMissingSettersFail()952 public void testMissingSettersFail() { 953 JavaFileObject moduleFile = 954 JavaFileObjects.forSourceLines( 955 "test.TestModule", 956 "package test;", 957 "", 958 "import dagger.Module;", 959 "import dagger.Provides;", 960 "", 961 "@Module", 962 "final class TestModule {", 963 " TestModule(String unused) {}", 964 " @Provides String s() { return null; }", 965 "}"); 966 JavaFileObject module2File = 967 JavaFileObjects.forSourceLines( 968 "test.Test2Module", 969 "package test;", 970 "", 971 "import dagger.Module;", 972 "import dagger.Provides;", 973 "", 974 "@Module", 975 "final class Test2Module {", 976 " @Provides Integer i() { return null; }", 977 "}"); 978 JavaFileObject module3File = 979 JavaFileObjects.forSourceLines( 980 "test.Test3Module", 981 "package test;", 982 "", 983 "import dagger.Module;", 984 "import dagger.Provides;", 985 "", 986 "@Module", 987 "final class Test3Module {", 988 " Test3Module(String unused) {}", 989 " @Provides Double d() { return null; }", 990 "}"); 991 JavaFileObject componentFile = 992 preprocessedJavaFile( 993 "test.TestComponent", 994 "package test;", 995 "", 996 "import dagger.Component;", 997 "", 998 "@Component(modules = {TestModule.class, Test2Module.class, Test3Module.class},", 999 " dependencies = OtherComponent.class)", 1000 "interface TestComponent {", 1001 " String string();", 1002 " Integer integer();", 1003 "", 1004 " @Component.Builder", 1005 " interface Builder {", 1006 " TestComponent create();", 1007 " }", 1008 "}"); 1009 JavaFileObject otherComponent = 1010 JavaFileObjects.forSourceLines( 1011 "test.OtherComponent", 1012 "package test;", 1013 "", 1014 "import dagger.Component;", 1015 "", 1016 "@Component", 1017 "interface OtherComponent {}"); 1018 Compilation compilation = 1019 daggerCompiler() 1020 .compile(moduleFile, module2File, module3File, componentFile, otherComponent); 1021 assertThat(compilation).failed(); 1022 assertThat(compilation) 1023 .hadErrorContaining( 1024 // Ignores Test2Module because we can construct it ourselves. 1025 // TODO(sameb): Ignore Test3Module because it's not used within transitive dependencies. 1026 String.format( 1027 messages.missingSetters(), 1028 "[test.TestModule, test.Test3Module, test.OtherComponent]")) 1029 .inFile(componentFile) 1030 .onLineContaining(process("interface Builder")); 1031 } 1032 1033 @Test covariantFactoryMethodReturnType()1034 public void covariantFactoryMethodReturnType() { 1035 JavaFileObject foo = 1036 JavaFileObjects.forSourceLines( 1037 "test.Foo", 1038 "package test;", 1039 "", 1040 "import javax.inject.Inject;", 1041 "", 1042 "class Foo {", 1043 " @Inject Foo() {}", 1044 "}"); 1045 JavaFileObject supertype = 1046 JavaFileObjects.forSourceLines( 1047 "test.Supertype", 1048 "package test;", 1049 "", 1050 "interface Supertype {", 1051 " Foo foo();", 1052 "}"); 1053 1054 JavaFileObject component = 1055 preprocessedJavaFile( 1056 "test.HasSupertype", 1057 "package test;", 1058 "", 1059 "import dagger.Component;", 1060 "", 1061 "@Component", 1062 "interface HasSupertype extends Supertype {", 1063 " @Component.Builder", 1064 " interface Builder {", 1065 " Supertype build();", 1066 " }", 1067 "}"); 1068 1069 Compilation compilation = compile(foo, supertype, component); 1070 assertThat(compilation).succeededWithoutWarnings(); 1071 } 1072 1073 @Test covariantFactoryMethodReturnType_hasNewMethod()1074 public void covariantFactoryMethodReturnType_hasNewMethod() { 1075 JavaFileObject foo = 1076 JavaFileObjects.forSourceLines( 1077 "test.Foo", 1078 "package test;", 1079 "", 1080 "import javax.inject.Inject;", 1081 "", 1082 "class Foo {", 1083 " @Inject Foo() {}", 1084 "}"); 1085 JavaFileObject bar = 1086 JavaFileObjects.forSourceLines( 1087 "test.Bar", 1088 "package test;", 1089 "", 1090 "import javax.inject.Inject;", 1091 "", 1092 "class Bar {", 1093 " @Inject Bar() {}", 1094 "}"); 1095 JavaFileObject supertype = 1096 JavaFileObjects.forSourceLines( 1097 "test.Supertype", 1098 "package test;", 1099 "", 1100 "interface Supertype {", 1101 " Foo foo();", 1102 "}"); 1103 1104 JavaFileObject component = 1105 preprocessedJavaFile( 1106 "test.HasSupertype", 1107 "package test;", 1108 "", 1109 "import dagger.Component;", 1110 "", 1111 "@Component", 1112 "interface HasSupertype extends Supertype {", 1113 " Bar bar();", 1114 "", 1115 " @Component.Builder", 1116 " interface Builder {", 1117 " Supertype build();", 1118 " }", 1119 "}"); 1120 1121 Compilation compilation = compile(foo, bar, supertype, component); 1122 assertThat(compilation).succeeded(); 1123 assertThat(compilation) 1124 .hadWarningContaining( 1125 process( 1126 "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype " 1127 + "declares additional component method(s): bar(). In order to provide " 1128 + "type-safe access to these methods, override build() to return " 1129 + "test.HasSupertype")) 1130 .inFile(component) 1131 .onLine(11); 1132 } 1133 1134 @Test covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited()1135 public void covariantFactoryMethodReturnType_hasNewMethod_factoryMethodInherited() { 1136 JavaFileObject foo = 1137 JavaFileObjects.forSourceLines( 1138 "test.Foo", 1139 "package test;", 1140 "", 1141 "import javax.inject.Inject;", 1142 "", 1143 "class Foo {", 1144 " @Inject Foo() {}", 1145 "}"); 1146 JavaFileObject bar = 1147 JavaFileObjects.forSourceLines( 1148 "test.Bar", 1149 "package test;", 1150 "", 1151 "import javax.inject.Inject;", 1152 "", 1153 "class Bar {", 1154 " @Inject Bar() {}", 1155 "}"); 1156 JavaFileObject supertype = 1157 JavaFileObjects.forSourceLines( 1158 "test.Supertype", 1159 "package test;", 1160 "", 1161 "interface Supertype {", 1162 " Foo foo();", 1163 "}"); 1164 1165 JavaFileObject creatorSupertype = 1166 preprocessedJavaFile( 1167 "test.CreatorSupertype", 1168 "package test;", 1169 "", 1170 "interface CreatorSupertype {", 1171 " Supertype build();", 1172 "}"); 1173 1174 JavaFileObject component = 1175 preprocessedJavaFile( 1176 "test.HasSupertype", 1177 "package test;", 1178 "", 1179 "import dagger.Component;", 1180 "", 1181 "@Component", 1182 "interface HasSupertype extends Supertype {", 1183 " Bar bar();", 1184 "", 1185 " @Component.Builder", 1186 " interface Builder extends CreatorSupertype {}", 1187 "}"); 1188 1189 Compilation compilation = compile(foo, bar, supertype, creatorSupertype, component); 1190 assertThat(compilation).succeeded(); 1191 assertThat(compilation) 1192 .hadWarningContaining( 1193 process( 1194 "test.HasSupertype.Builder.build() returns test.Supertype, but test.HasSupertype " 1195 + "declares additional component method(s): bar(). In order to provide " 1196 + "type-safe access to these methods, override build() to return " 1197 + "test.HasSupertype")); 1198 } 1199 1200 @Test testGenericsOnFactoryMethodFails()1201 public void testGenericsOnFactoryMethodFails() { 1202 JavaFileObject componentFile = 1203 preprocessedJavaFile( 1204 "test.SimpleComponent", 1205 "package test;", 1206 "", 1207 "import dagger.Component;", 1208 "import javax.inject.Provider;", 1209 "", 1210 "@Component", 1211 "abstract class SimpleComponent {", 1212 " @Component.Builder", 1213 " interface Builder {", 1214 " <T> SimpleComponent build();", 1215 " }", 1216 "}"); 1217 Compilation compilation = compile(componentFile); 1218 assertThat(compilation).failed(); 1219 assertThat(compilation) 1220 .hadErrorContaining(messages.methodsMayNotHaveTypeParameters()) 1221 .inFile(componentFile) 1222 .onLineContaining(process("<T> SimpleComponent build();")); 1223 } 1224 1225 @Test testGenericsOnInheritedFactoryMethodFails()1226 public void testGenericsOnInheritedFactoryMethodFails() { 1227 JavaFileObject componentFile = 1228 preprocessedJavaFile( 1229 "test.SimpleComponent", 1230 "package test;", 1231 "", 1232 "import dagger.Component;", 1233 "import javax.inject.Provider;", 1234 "", 1235 "@Component", 1236 "abstract class SimpleComponent {", 1237 " interface Parent {", 1238 " <T> SimpleComponent build();", 1239 " }", 1240 "", 1241 " @Component.Builder", 1242 " interface Builder extends Parent {}", 1243 "}"); 1244 Compilation compilation = compile(componentFile); 1245 assertThat(compilation).failed(); 1246 assertThat(compilation) 1247 .hadErrorContaining( 1248 String.format( 1249 messages.inheritedMethodsMayNotHaveTypeParameters(), process("<T>build()"))) 1250 .inFile(componentFile) 1251 .onLineContaining(process("interface Builder")); 1252 } 1253 } 1254