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