1 /* 2 * Copyright (C) 2014 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass; 20 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod; 21 22 import androidx.room.compiler.processing.util.Source; 23 import com.google.common.collect.ImmutableList; 24 import com.google.common.collect.ImmutableMap; 25 import dagger.testing.compile.CompilerTests; 26 import dagger.testing.golden.GoldenFileRule; 27 import org.junit.Rule; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.JUnit4; 31 32 @RunWith(JUnit4.class) 33 public class ModuleFactoryGeneratorTest { 34 35 private static final Source NULLABLE = 36 CompilerTests.javaSource( 37 "test.Nullable", "package test;", "public @interface Nullable {}"); 38 39 @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule(); 40 41 // TODO(gak): add tests for invalid combinations of scope and qualifier annotations like we have 42 // for @Inject 43 providesMethodNotInModule()44 @Test public void providesMethodNotInModule() { 45 assertThatMethodInUnannotatedClass("@Provides String provideString() { return null; }") 46 .hasError("@Provides methods can only be present within a @Module or @ProducerModule"); 47 } 48 providesMethodAbstract()49 @Test public void providesMethodAbstract() { 50 assertThatModuleMethod("@Provides abstract String abstractMethod();") 51 .hasError("@Provides methods cannot be abstract"); 52 } 53 providesMethodPrivate()54 @Test public void providesMethodPrivate() { 55 assertThatModuleMethod("@Provides private String privateMethod() { return null; }") 56 .hasError("@Provides methods cannot be private"); 57 } 58 providesMethodReturnVoid()59 @Test public void providesMethodReturnVoid() { 60 assertThatModuleMethod("@Provides void voidMethod() {}") 61 .hasError("@Provides methods must return a value (not void)"); 62 } 63 64 @Test providesMethodReturnsProvider()65 public void providesMethodReturnsProvider() { 66 assertThatModuleMethod("@Provides Provider<String> provideProvider() {}") 67 .hasError("@Provides methods must not return framework types"); 68 } 69 70 @Test providesMethodReturnsLazy()71 public void providesMethodReturnsLazy() { 72 assertThatModuleMethod("@Provides Lazy<String> provideLazy() {}") 73 .hasError("@Provides methods must not return framework types"); 74 } 75 76 @Test providesMethodReturnsMembersInjector()77 public void providesMethodReturnsMembersInjector() { 78 assertThatModuleMethod("@Provides MembersInjector<String> provideMembersInjector() {}") 79 .hasError("@Provides methods must not return framework types"); 80 } 81 82 @Test providesMethodReturnsProducer()83 public void providesMethodReturnsProducer() { 84 assertThatModuleMethod("@Provides Producer<String> provideProducer() {}") 85 .hasError("@Provides methods must not return framework types"); 86 } 87 88 @Test providesMethodReturnsProduced()89 public void providesMethodReturnsProduced() { 90 assertThatModuleMethod("@Provides Produced<String> provideProduced() {}") 91 .hasError("@Provides methods must not return framework types"); 92 } 93 providesMethodWithTypeParameter()94 @Test public void providesMethodWithTypeParameter() { 95 assertThatModuleMethod("@Provides <T> String typeParameter() { return null; }") 96 .hasError("@Provides methods may not have type parameters"); 97 } 98 providesMethodSetValuesWildcard()99 @Test public void providesMethodSetValuesWildcard() { 100 assertThatModuleMethod("@Provides @ElementsIntoSet Set<?> provideWildcard() { return null; }") 101 .hasError( 102 "@Provides methods must return a primitive, an array, a type variable, " 103 + "or a declared type"); 104 } 105 providesMethodSetValuesRawSet()106 @Test public void providesMethodSetValuesRawSet() { 107 assertThatModuleMethod("@Provides @ElementsIntoSet Set provideSomething() { return null; }") 108 .hasError("@Provides methods annotated with @ElementsIntoSet cannot return a raw Set"); 109 } 110 providesMethodSetValuesNotASet()111 @Test public void providesMethodSetValuesNotASet() { 112 assertThatModuleMethod( 113 "@Provides @ElementsIntoSet List<String> provideStrings() { return null; }") 114 .hasError("@Provides methods annotated with @ElementsIntoSet must return a Set"); 115 } 116 modulesWithTypeParamsMustBeAbstract()117 @Test public void modulesWithTypeParamsMustBeAbstract() { 118 Source moduleFile = 119 CompilerTests.javaSource( 120 "test.TestModule", 121 "package test;", 122 "", 123 "import dagger.Module;", 124 "", 125 "@Module", 126 "final class TestModule<A> {}"); 127 CompilerTests.daggerCompiler(moduleFile) 128 .compile( 129 subject -> { 130 subject.hasErrorCount(1); 131 subject.hasErrorContaining("Modules with type parameters must be abstract") 132 .onSource(moduleFile) 133 .onLine(6); 134 }); 135 } 136 provideOverriddenByNoProvide()137 @Test public void provideOverriddenByNoProvide() { 138 Source parent = 139 CompilerTests.javaSource( 140 "test.Parent", 141 "package test;", 142 "", 143 "import dagger.Module;", 144 "import dagger.Provides;", 145 "", 146 "@Module", 147 "class Parent {", 148 " @Provides String foo() { return null; }", 149 "}"); 150 assertThatModuleMethod("String foo() { return null; }") 151 .withDeclaration("@Module class %s extends Parent { %s }") 152 .withAdditionalSources(parent) 153 .hasError( 154 "Binding methods may not be overridden in modules. Overrides: " 155 + "@Provides String test.Parent.foo()"); 156 } 157 provideOverriddenByProvide()158 @Test public void provideOverriddenByProvide() { 159 Source parent = 160 CompilerTests.javaSource( 161 "test.Parent", 162 "package test;", 163 "", 164 "import dagger.Module;", 165 "import dagger.Provides;", 166 "", 167 "@Module", 168 "class Parent {", 169 " @Provides String foo() { return null; }", 170 "}"); 171 assertThatModuleMethod("@Provides String foo() { return null; }") 172 .withDeclaration("@Module class %s extends Parent { %s }") 173 .withAdditionalSources(parent) 174 .hasError( 175 "Binding methods may not override another method. Overrides: " 176 + "@Provides String test.Parent.foo()"); 177 } 178 providesOverridesNonProvides()179 @Test public void providesOverridesNonProvides() { 180 Source parent = 181 CompilerTests.javaSource( 182 "test.Parent", 183 "package test;", 184 "", 185 "import dagger.Module;", 186 "", 187 "@Module", 188 "class Parent {", 189 " String foo() { return null; }", 190 "}"); 191 assertThatModuleMethod("@Provides String foo() { return null; }") 192 .withDeclaration("@Module class %s extends Parent { %s }") 193 .withAdditionalSources(parent) 194 .hasError( 195 "Binding methods may not override another method. Overrides: " 196 + "String test.Parent.foo()"); 197 } 198 validatesIncludedModules()199 @Test public void validatesIncludedModules() { 200 Source module = 201 CompilerTests.javaSource( 202 "test.Parent", 203 "package test;", 204 "", 205 "import dagger.Module;", 206 "", 207 "@Module(", 208 " includes = {", 209 " Void.class,", 210 " String.class,", 211 " }", 212 ")", 213 "class TestModule {}"); 214 215 CompilerTests.daggerCompiler(module) 216 .compile( 217 subject -> { 218 subject.hasErrorCount(2); 219 // We avoid asserting on the line number because ksp and javac report different lines. 220 // The main issue here is that ksp doesn't allow reporting errors on individual 221 // annotation values, it only allows reporting errors on annotations themselves. 222 subject.hasErrorContaining( 223 "java.lang.Void is listed as a module, but is not annotated with @Module") 224 .onSource(module); 225 subject.hasErrorContaining( 226 "java.lang.String is listed as a module, but is not annotated with @Module") 227 .onSource(module); 228 }); 229 } 230 singleProvidesMethodNoArgs()231 @Test public void singleProvidesMethodNoArgs() { 232 Source moduleFile = 233 CompilerTests.javaSource( 234 "test.TestModule", 235 "package test;", 236 "", 237 "import dagger.Module;", 238 "import dagger.Provides;", 239 "", 240 "@Module", 241 "final class TestModule {", 242 " @Provides String provideString() {", 243 " return \"\";", 244 " }", 245 "}"); 246 CompilerTests.daggerCompiler(moduleFile) 247 .compile( 248 subject -> { 249 subject.hasErrorCount(0); 250 subject.generatedSource( 251 goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory")); 252 }); 253 } 254 singleProvidesMethodNoArgs_disableNullable()255 @Test public void singleProvidesMethodNoArgs_disableNullable() { 256 Source moduleFile = 257 CompilerTests.javaSource( 258 "test.TestModule", 259 "package test;", 260 "", 261 "import dagger.Module;", 262 "import dagger.Provides;", 263 "", 264 "@Module", 265 "final class TestModule {", 266 " @Provides String provideString() {", 267 " return \"\";", 268 " }", 269 "}"); 270 CompilerTests.daggerCompiler(moduleFile) 271 .withProcessingOptions(ImmutableMap.of("dagger.nullableValidation", "WARNING")) 272 .compile( 273 subject -> { 274 subject.hasErrorCount(0); 275 subject.generatedSource( 276 goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory")); 277 }); 278 } 279 nullableProvides()280 @Test public void nullableProvides() { 281 Source moduleFile = 282 CompilerTests.javaSource( 283 "test.TestModule", 284 "package test;", 285 "", 286 "import dagger.Module;", 287 "import dagger.Provides;", 288 "", 289 "@Module", 290 "final class TestModule {", 291 " @Provides @Nullable String provideString() { return null; }", 292 "}"); 293 CompilerTests.daggerCompiler(moduleFile, NULLABLE) 294 .compile( 295 subject -> { 296 subject.hasErrorCount(0); 297 subject.generatedSource( 298 goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory")); 299 }); 300 } 301 multipleProvidesMethods()302 @Test public void multipleProvidesMethods() { 303 Source classXFile = 304 CompilerTests.javaSource("test.X", 305 "package test;", 306 "", 307 "import javax.inject.Inject;", 308 "", 309 "class X {", 310 " @Inject public String s;", 311 "}"); 312 Source moduleFile = 313 CompilerTests.javaSource( 314 "test.TestModule", 315 "package test;", 316 "", 317 "import dagger.MembersInjector;", 318 "import dagger.Module;", 319 "import dagger.Provides;", 320 "import java.util.Arrays;", 321 "import java.util.List;", 322 "", 323 "@Module", 324 "final class TestModule {", 325 " @Provides List<Object> provideObjects(", 326 " @QualifierA Object a, @QualifierB Object b, MembersInjector<X> xInjector) {", 327 " return Arrays.asList(a, b);", 328 " }", 329 "", 330 " @Provides @QualifierA Object provideAObject() {", 331 " return new Object();", 332 " }", 333 "", 334 " @Provides @QualifierB Object provideBObject() {", 335 " return new Object();", 336 " }", 337 "}"); 338 CompilerTests.daggerCompiler(classXFile, moduleFile, QUALIFIER_A, QUALIFIER_B) 339 .compile( 340 subject -> { 341 subject.hasErrorCount(0); 342 subject.generatedSource( 343 goldenFileRule.goldenSource("test/TestModule_ProvideObjectsFactory")); 344 }); 345 } 346 providesSetElement()347 @Test public void providesSetElement() { 348 Source moduleFile = 349 CompilerTests.javaSource( 350 "test.TestModule", 351 "package test;", 352 "", 353 "import java.util.logging.Logger;", 354 "import dagger.Module;", 355 "import dagger.Provides;", 356 "import dagger.multibindings.IntoSet;", 357 "", 358 "@Module", 359 "final class TestModule {", 360 " @Provides @IntoSet String provideString() {", 361 " return \"\";", 362 " }", 363 "}"); 364 CompilerTests.daggerCompiler(moduleFile) 365 .compile( 366 subject -> { 367 subject.hasErrorCount(0); 368 subject.generatedSource( 369 goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory")); 370 }); 371 } 372 providesSetElementWildcard()373 @Test public void providesSetElementWildcard() { 374 Source moduleFile = 375 CompilerTests.javaSource( 376 "test.TestModule", 377 "package test;", 378 "", 379 "import java.util.logging.Logger;", 380 "import dagger.Module;", 381 "import dagger.Provides;", 382 "import dagger.multibindings.IntoSet;", 383 "import java.util.ArrayList;", 384 "import java.util.List;", 385 "", 386 "@Module", 387 "final class TestModule {", 388 " @Provides @IntoSet List<List<?>> provideWildcardList() {", 389 " return new ArrayList<>();", 390 " }", 391 "}"); 392 CompilerTests.daggerCompiler(moduleFile) 393 .compile( 394 subject -> { 395 subject.hasErrorCount(0); 396 subject.generatedSource( 397 goldenFileRule.goldenSource("test/TestModule_ProvideWildcardListFactory")); 398 }); 399 } 400 providesSetValues()401 @Test public void providesSetValues() { 402 Source moduleFile = 403 CompilerTests.javaSource( 404 "test.TestModule", 405 "package test;", 406 "", 407 "import dagger.Module;", 408 "import dagger.Provides;", 409 "import dagger.multibindings.ElementsIntoSet;", 410 "import java.util.Set;", 411 "", 412 "@Module", 413 "final class TestModule {", 414 " @Provides @ElementsIntoSet Set<String> provideStrings() {", 415 " return null;", 416 " }", 417 "}"); 418 CompilerTests.daggerCompiler(moduleFile) 419 .compile( 420 subject -> { 421 subject.hasErrorCount(0); 422 subject.generatedSource( 423 goldenFileRule.goldenSource("test/TestModule_ProvideStringsFactory")); 424 }); 425 } 426 multipleProvidesMethodsWithSameName()427 @Test public void multipleProvidesMethodsWithSameName() { 428 Source moduleFile = 429 CompilerTests.javaSource("test.TestModule", 430 "package test;", 431 "", 432 "import dagger.Module;", 433 "import dagger.Provides;", 434 "", 435 "@Module", 436 "final class TestModule {", 437 " @Provides Object provide(int i) {", 438 " return i;", 439 " }", 440 "", 441 " @Provides String provide() {", 442 " return \"\";", 443 " }", 444 "}"); 445 CompilerTests.daggerCompiler(moduleFile) 446 .compile( 447 subject -> { 448 subject.hasErrorCount(2); 449 subject.hasErrorContaining( 450 "Cannot have more than one binding method with the same name in a single " 451 + "module") 452 .onSource(moduleFile) 453 .onLine(8); 454 subject.hasErrorContaining( 455 "Cannot have more than one binding method with the same name in a single " 456 + "module") 457 .onSource(moduleFile) 458 .onLine(12); 459 }); 460 } 461 462 @Test providesMethodThrowsChecked()463 public void providesMethodThrowsChecked() { 464 Source moduleFile = 465 CompilerTests.javaSource( 466 "test.TestModule", 467 "package test;", 468 "", 469 "import dagger.Module;", 470 "import dagger.Provides;", 471 "", 472 "@Module", 473 "final class TestModule {", 474 " @Provides int i() throws Exception {", 475 " return 0;", 476 " }", 477 "", 478 " @Provides String s() throws Throwable {", 479 " return \"\";", 480 " }", 481 "}"); 482 CompilerTests.daggerCompiler(moduleFile) 483 .compile( 484 subject -> { 485 subject.hasErrorCount(2); 486 subject.hasErrorContaining("@Provides methods may only throw unchecked exceptions") 487 .onSource(moduleFile) 488 .onLine(8); 489 subject.hasErrorContaining("@Provides methods may only throw unchecked exceptions") 490 .onSource(moduleFile) 491 .onLine(12); 492 }); 493 } 494 495 @Test providedTypes()496 public void providedTypes() { 497 Source moduleFile = 498 CompilerTests.javaSource( 499 "test.TestModule", 500 "package test;", 501 "", 502 "import dagger.Module;", 503 "import dagger.Provides;", 504 "import java.io.Closeable;", 505 "import java.util.Set;", 506 "", 507 "@Module", 508 "final class TestModule {", 509 " @Provides String string() {", 510 " return null;", 511 " }", 512 "", 513 " @Provides Set<String> strings() {", 514 " return null;", 515 " }", 516 "", 517 " @Provides Set<? extends Closeable> closeables() {", 518 " return null;", 519 " }", 520 "", 521 " @Provides String[] stringArray() {", 522 " return null;", 523 " }", 524 "", 525 " @Provides int integer() {", 526 " return 0;", 527 " }", 528 "", 529 " @Provides int[] integers() {", 530 " return null;", 531 " }", 532 "}"); 533 CompilerTests.daggerCompiler(moduleFile).compile(subject -> subject.hasErrorCount(0)); 534 } 535 536 @Test privateModule()537 public void privateModule() { 538 Source moduleFile = 539 CompilerTests.javaSource( 540 "test.Enclosing", 541 "package test;", 542 "", 543 "import dagger.Module;", 544 "", 545 "final class Enclosing {", 546 " @Module private static final class PrivateModule {", 547 " }", 548 "}"); 549 CompilerTests.daggerCompiler(moduleFile) 550 .compile( 551 subject -> { 552 subject.hasErrorCount(1); 553 subject.hasErrorContaining("Modules cannot be private") 554 .onSource(moduleFile) 555 .onLine(6); 556 }); 557 } 558 559 @Test privateModule_kotlin()560 public void privateModule_kotlin() { 561 Source moduleFile = 562 CompilerTests.kotlinSource( 563 "test.TestModule.kt", 564 "package test", 565 "", 566 "import dagger.Component", 567 "import dagger.Module", 568 "import dagger.Provides", 569 "", 570 "@Module", 571 "private class TestModule {", 572 " @Provides fun provideInt(): Int = 1", 573 "}"); 574 575 CompilerTests.daggerCompiler(moduleFile) 576 .compile( 577 subject -> { 578 subject.hasErrorCount(1); 579 subject 580 .hasErrorContaining("Modules cannot be private") 581 .onSource(moduleFile); 582 }); 583 } 584 585 @Test enclosedInPrivateModule()586 public void enclosedInPrivateModule() { 587 Source moduleFile = 588 CompilerTests.javaSource("test.Enclosing", 589 "package test;", 590 "", 591 "import dagger.Module;", 592 "", 593 "final class Enclosing {", 594 " private static final class PrivateEnclosing {", 595 " @Module static final class TestModule {", 596 " }", 597 " }", 598 "}"); 599 CompilerTests.daggerCompiler(moduleFile) 600 .compile( 601 subject -> { 602 subject.hasErrorCount(1); 603 subject.hasErrorContaining("Modules cannot be enclosed in private types") 604 .onSource(moduleFile) 605 .onLine(7); 606 }); 607 } 608 609 @Test publicModuleNonPublicIncludes()610 public void publicModuleNonPublicIncludes() { 611 Source publicModuleFile = 612 CompilerTests.javaSource("test.PublicModule", 613 "package test;", 614 "", 615 "import dagger.Module;", 616 "", 617 "@Module(includes = {", 618 " BadNonPublicModule.class, OtherPublicModule.class, OkNonPublicModule.class", 619 "})", 620 "public final class PublicModule {", 621 "}"); 622 Source badNonPublicModuleFile = 623 CompilerTests.javaSource( 624 "test.BadNonPublicModule", 625 "package test;", 626 "", 627 "import dagger.Module;", 628 "import dagger.Provides;", 629 "", 630 "@Module", 631 "final class BadNonPublicModule {", 632 " @Provides", 633 " int provideInt() {", 634 " return 42;", 635 " }", 636 "}"); 637 Source okNonPublicModuleFile = 638 CompilerTests.javaSource("test.OkNonPublicModule", 639 "package test;", 640 "", 641 "import dagger.Module;", 642 "import dagger.Provides;", 643 "", 644 "@Module", 645 "final class OkNonPublicModule {", 646 " @Provides", 647 " static String provideString() {", 648 " return \"foo\";", 649 " }", 650 "}"); 651 Source otherPublicModuleFile = 652 CompilerTests.javaSource("test.OtherPublicModule", 653 "package test;", 654 "", 655 "import dagger.Module;", 656 "", 657 "@Module", 658 "public final class OtherPublicModule {", 659 "}"); 660 CompilerTests.daggerCompiler( 661 publicModuleFile, 662 badNonPublicModuleFile, 663 okNonPublicModuleFile, 664 otherPublicModuleFile) 665 .compile( 666 subject -> { 667 subject.hasErrorCount(1); 668 subject.hasErrorContaining( 669 "This module is public, but it includes non-public (or effectively non-" 670 + "public) modules (test.BadNonPublicModule) that have non-static, non-" 671 + "abstract binding methods. Either reduce the visibility of this module" 672 + ", make the included modules public, or make all of the binding " 673 + "methods on the included modules abstract or static.") 674 .onSource(publicModuleFile) 675 .onLine(8); 676 }); 677 } 678 679 @Test genericSubclassedModule()680 public void genericSubclassedModule() { 681 Source parent = 682 CompilerTests.javaSource( 683 "test.ParentModule", 684 "package test;", 685 "", 686 "import dagger.Module;", 687 "import dagger.Provides;", 688 "import dagger.multibindings.IntoSet;", 689 "import dagger.multibindings.IntoMap;", 690 "import dagger.multibindings.StringKey;", 691 "import java.util.List;", 692 "import java.util.ArrayList;", 693 "", 694 "@Module", 695 "abstract class ParentModule<A extends CharSequence,", 696 " B,", 697 " C extends Number & Comparable<C>> {", 698 " @Provides List<B> provideListB(B b) {", 699 " List<B> list = new ArrayList<B>();", 700 " list.add(b);", 701 " return list;", 702 " }", 703 "", 704 " @Provides @IntoSet B provideBElement(B b) {", 705 " return b;", 706 " }", 707 "", 708 " @Provides @IntoMap @StringKey(\"b\") B provideBEntry(B b) {", 709 " return b;", 710 " }", 711 "}"); 712 Source numberChild = 713 CompilerTests.javaSource("test.ChildNumberModule", 714 "package test;", 715 "", 716 "import dagger.Module;", 717 "import dagger.Provides;", 718 "", 719 "@Module", 720 "class ChildNumberModule extends ParentModule<String, Number, Double> {", 721 " @Provides Number provideNumber() { return 1; }", 722 "}"); 723 Source integerChild = 724 CompilerTests.javaSource("test.ChildIntegerModule", 725 "package test;", 726 "", 727 "import dagger.Module;", 728 "import dagger.Provides;", 729 "", 730 "@Module", 731 "class ChildIntegerModule extends ParentModule<StringBuilder, Integer, Float> {", 732 " @Provides Integer provideInteger() { return 2; }", 733 "}"); 734 Source component = 735 CompilerTests.javaSource("test.C", 736 "package test;", 737 "", 738 "import dagger.Component;", 739 "import java.util.List;", 740 "", 741 "@Component(modules={ChildNumberModule.class, ChildIntegerModule.class})", 742 "interface C {", 743 " List<Number> numberList();", 744 " List<Integer> integerList();", 745 "}"); 746 CompilerTests.daggerCompiler(parent, numberChild, integerChild, component) 747 .compile( 748 subject -> { 749 subject.hasErrorCount(0); 750 subject.generatedSource( 751 goldenFileRule.goldenSource("test/ParentModule_ProvideListBFactory")); 752 subject.generatedSource( 753 goldenFileRule.goldenSource("test/ParentModule_ProvideBElementFactory")); 754 subject.generatedSource( 755 goldenFileRule.goldenSource("test/ParentModule_ProvideBEntryFactory")); 756 subject.generatedSource( 757 goldenFileRule.goldenSource("test/ChildNumberModule_ProvideNumberFactory")); 758 subject.generatedSource( 759 goldenFileRule.goldenSource("test/ChildIntegerModule_ProvideIntegerFactory")); 760 }); 761 } 762 parameterizedModuleWithStaticProvidesMethodOfGenericType()763 @Test public void parameterizedModuleWithStaticProvidesMethodOfGenericType() { 764 Source moduleFile = 765 CompilerTests.javaSource( 766 "test.ParameterizedModule", 767 "package test;", 768 "", 769 "import dagger.Module;", 770 "import dagger.Provides;", 771 "import java.util.List;", 772 "import java.util.ArrayList;", 773 "import java.util.Map;", 774 "import java.util.HashMap;", 775 "", 776 "@Module abstract class ParameterizedModule<T> {", 777 " @Provides List<T> provideListT() {", 778 " return new ArrayList<>();", 779 " }", 780 "", 781 " @Provides static Map<String, Number> provideMapStringNumber() {", 782 " return new HashMap<>();", 783 " }", 784 "", 785 " @Provides static Object provideNonGenericType() {", 786 " return new Object();", 787 " }", 788 "", 789 " @Provides static String provideNonGenericTypeWithDeps(Object o) {", 790 " return o.toString();", 791 " }", 792 "}"); 793 CompilerTests.daggerCompiler(moduleFile) 794 .compile( 795 subject -> { 796 subject.hasErrorCount(0); 797 subject.generatedSource( 798 goldenFileRule.goldenSource( 799 "test/ParameterizedModule_ProvideMapStringNumberFactory")); 800 subject.generatedSource( 801 goldenFileRule.goldenSource( 802 "test/ParameterizedModule_ProvideNonGenericTypeFactory")); 803 subject.generatedSource( 804 goldenFileRule.goldenSource( 805 "test/ParameterizedModule_ProvideNonGenericTypeWithDepsFactory")); 806 }); 807 } 808 809 private static final Source QUALIFIER_A = 810 CompilerTests.javaSource( 811 "test.QualifierA", 812 "package test;", 813 "", 814 "import javax.inject.Qualifier;", 815 "", 816 "@Qualifier @interface QualifierA {}"); 817 818 private static final Source QUALIFIER_B = 819 CompilerTests.javaSource( 820 "test.QualifierB", 821 "package test;", 822 "", 823 "import javax.inject.Qualifier;", 824 "", 825 "@Qualifier @interface QualifierB {}"); 826 827 @Test providesMethodMultipleQualifiersOnMethod()828 public void providesMethodMultipleQualifiersOnMethod() { 829 Source moduleFile = 830 CompilerTests.javaSource("test.TestModule", 831 "package test;", 832 "", 833 "import dagger.Module;", 834 "import dagger.Provides;", 835 "", 836 "@Module", 837 "final class TestModule {", 838 " @Provides", 839 " @QualifierA", 840 " @QualifierB", 841 " String provideString() {", 842 " return \"foo\";", 843 " }", 844 "}"); 845 CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B) 846 .compile( 847 subject -> { 848 // There are 2 errors -- 1 per qualifier. 849 subject.hasErrorCount(2); 850 subject.hasErrorContaining("may not use more than one @Qualifier") 851 .onSource(moduleFile) 852 .onLine(9); 853 subject.hasErrorContaining("may not use more than one @Qualifier") 854 .onSource(moduleFile) 855 .onLine(10); 856 }); 857 } 858 859 @Test providesMethodMultipleQualifiersOnParameter()860 public void providesMethodMultipleQualifiersOnParameter() { 861 Source moduleFile = 862 CompilerTests.javaSource( 863 "test.TestModule", 864 "package test;", 865 "", 866 "import dagger.Module;", 867 "import dagger.Provides;", 868 "", 869 "@Module", 870 "final class TestModule {", 871 " @Provides", 872 " static String provideString(", 873 " @QualifierA", 874 " @QualifierB", 875 " Object object) {", 876 " return \"foo\";", 877 " }", 878 "}"); 879 CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B) 880 .compile( 881 subject -> { 882 // There are two errors -- 1 per qualifier. 883 subject.hasErrorCount(2); 884 subject.hasErrorContaining("may not use more than one @Qualifier") 885 .onSource(moduleFile) 886 .onLine(10); 887 subject.hasErrorContaining("may not use more than one @Qualifier") 888 .onSource(moduleFile) 889 .onLine(11); 890 }); 891 } 892 893 @Test providesMethodWildcardDependency()894 public void providesMethodWildcardDependency() { 895 Source moduleFile = 896 CompilerTests.javaSource( 897 "test.TestModule", 898 "package test;", 899 "", 900 "import dagger.Module;", 901 "import dagger.Provides;", 902 "import javax.inject.Provider;", 903 "", 904 "@Module", 905 "final class TestModule {", 906 " @Provides static String provideString(Provider<? extends Number> numberProvider) {", 907 " return \"foo\";", 908 " }", 909 "}"); 910 CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B) 911 .compile( 912 subject -> { 913 subject.hasErrorCount(1); 914 subject.hasErrorContaining( 915 "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or " 916 + "Produced<T> when T is a wildcard type such as ? extends java.lang.Number"); 917 }); 918 } 919 920 private static final Source SCOPE_A = 921 CompilerTests.javaSource( 922 "test.ScopeA", 923 "package test;", 924 "", 925 "import javax.inject.Scope;", 926 "", 927 "@Scope @interface ScopeA {}"); 928 929 private static final Source SCOPE_B = 930 CompilerTests.javaSource( 931 "test.ScopeB", 932 "package test;", 933 "", 934 "import javax.inject.Scope;", 935 "", 936 "@Scope @interface ScopeB {}"); 937 938 @Test providesMethodMultipleScopes()939 public void providesMethodMultipleScopes() { 940 Source moduleFile = 941 CompilerTests.javaSource( 942 "test.TestModule", 943 "package test;", 944 "", 945 "import dagger.Module;", 946 "import dagger.Provides;", 947 "", 948 "@Module", 949 "final class TestModule {", 950 " @Provides", 951 " @ScopeA", 952 " @ScopeB", 953 " String provideString() {", 954 " return \"foo\";", 955 " }", 956 "}"); 957 CompilerTests.daggerCompiler(moduleFile, SCOPE_A, SCOPE_B) 958 .compile( 959 subject -> { 960 subject.hasErrorCount(2); 961 subject.hasErrorContaining("cannot use more than one @Scope") 962 .onSource(moduleFile) 963 .onLineContaining("@ScopeA"); 964 subject.hasErrorContaining("cannot use more than one @Scope") 965 .onSource(moduleFile) 966 .onLineContaining("@ScopeB"); 967 }); 968 } 969 providerDependsOnProduced()970 @Test public void providerDependsOnProduced() { 971 Source moduleFile = 972 CompilerTests.javaSource( 973 "test.TestModule", 974 "package test;", 975 "", 976 "import dagger.Module;", 977 "import dagger.Provides;", 978 "import dagger.producers.Producer;", 979 "", 980 "@Module", 981 "final class TestModule {", 982 " @Provides String provideString(Producer<Integer> producer) {", 983 " return \"foo\";", 984 " }", 985 "}"); 986 CompilerTests.daggerCompiler(moduleFile) 987 .compile( 988 subject -> { 989 subject.hasErrorCount(1); 990 subject.hasErrorContaining("Producer may only be injected in @Produces methods"); 991 }); 992 } 993 providerDependsOnProducer()994 @Test public void providerDependsOnProducer() { 995 Source moduleFile = 996 CompilerTests.javaSource( 997 "test.TestModule", 998 "package test;", 999 "", 1000 "import dagger.Module;", 1001 "import dagger.Provides;", 1002 "import dagger.producers.Produced;", 1003 "", 1004 "@Module", 1005 "final class TestModule {", 1006 " @Provides String provideString(Produced<Integer> produced) {", 1007 " return \"foo\";", 1008 " }", 1009 "}"); 1010 CompilerTests.daggerCompiler(moduleFile) 1011 .compile( 1012 subject -> { 1013 subject.hasErrorCount(1); 1014 subject.hasErrorContaining("Produced may only be injected in @Produces methods"); 1015 }); 1016 } 1017 1018 @Test proxyMethodsConflictWithOtherFactoryMethods()1019 public void proxyMethodsConflictWithOtherFactoryMethods() throws Exception { 1020 Source module = 1021 CompilerTests.javaSource( 1022 "test.TestModule", 1023 "package test;", 1024 "", 1025 "import dagger.Module;", 1026 "import dagger.Provides;", 1027 "", 1028 "@Module", 1029 "interface TestModule {", 1030 " @Provides", 1031 " static int get() { return 1; }", 1032 "", 1033 " @Provides", 1034 " static boolean create() { return true; }", 1035 "}"); 1036 1037 CompilerTests.daggerCompiler(module) 1038 .compile( 1039 subject -> { 1040 subject.hasErrorCount(0); 1041 subject.generatedSource(goldenFileRule.goldenSource("test/TestModule_GetFactory")); 1042 subject.generatedSource(goldenFileRule.goldenSource("test/TestModule_CreateFactory")); 1043 }); 1044 } 1045 1046 @Test testScopedMetadataOnStaticProvides()1047 public void testScopedMetadataOnStaticProvides() throws Exception { 1048 Source module = 1049 CompilerTests.javaSource( 1050 "test.ScopedBinding", 1051 "package test;", 1052 "", 1053 "import dagger.Module;", 1054 "import dagger.Provides;", 1055 "import javax.inject.Singleton;", 1056 "", 1057 "@Module", 1058 "interface MyModule {", 1059 " @NonScope", 1060 " @Singleton", 1061 " @Provides", 1062 " static String provideString() {", 1063 " return \"\";", 1064 " }", 1065 "}"); 1066 Source nonScope = 1067 CompilerTests.javaSource( 1068 "test.NonScope", 1069 "package test;", 1070 "", 1071 "@interface NonScope {}"); 1072 1073 CompilerTests.daggerCompiler(module, nonScope) 1074 .compile( 1075 subject -> { 1076 subject.hasErrorCount(0); 1077 subject.generatedSource( 1078 goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory")); 1079 }); 1080 } 1081 1082 @Test testScopedMetadataOnNonStaticProvides()1083 public void testScopedMetadataOnNonStaticProvides() throws Exception { 1084 Source module = 1085 CompilerTests.javaSource( 1086 "test.ScopedBinding", 1087 "package test;", 1088 "", 1089 "import dagger.Module;", 1090 "import dagger.Provides;", 1091 "import javax.inject.Singleton;", 1092 "", 1093 "@Module", 1094 "class MyModule {", 1095 " @NonScope", 1096 " @Singleton", 1097 " @Provides", 1098 " String provideString() {", 1099 " return \"\";", 1100 " }", 1101 "}"); 1102 Source nonScope = 1103 CompilerTests.javaSource( 1104 "test.NonScope", 1105 "package test;", 1106 "", 1107 "@interface NonScope {}"); 1108 1109 CompilerTests.daggerCompiler(module, nonScope) 1110 .compile( 1111 subject -> { 1112 subject.hasErrorCount(0); 1113 subject.generatedSource( 1114 goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory")); 1115 }); 1116 } 1117 1118 @Test testScopeMetadataWithCustomScope()1119 public void testScopeMetadataWithCustomScope() throws Exception { 1120 Source module = 1121 CompilerTests.javaSource( 1122 "test.ScopedBinding", 1123 "package test;", 1124 "", 1125 "import dagger.Module;", 1126 "import dagger.Provides;", 1127 "import javax.inject.Singleton;", 1128 "", 1129 "@Module", 1130 "interface MyModule {", 1131 " @NonScope(\"someValue\")", 1132 " @CustomScope(\"someOtherValue\")", 1133 " @Provides", 1134 " static String provideString() {", 1135 " return \"\";", 1136 " }", 1137 "}"); 1138 Source customScope = 1139 CompilerTests.javaSource( 1140 "test.CustomScope", 1141 "package test;", 1142 "", 1143 "import javax.inject.Scope;", 1144 "", 1145 "@Scope", 1146 "@interface CustomScope {", 1147 " String value();", 1148 "}"); 1149 Source nonScope = 1150 CompilerTests.javaSource( 1151 "test.NonScope", 1152 "package test;", 1153 "", 1154 "@interface NonScope {", 1155 " String value();", 1156 "}"); 1157 1158 CompilerTests.daggerCompiler(module, customScope, nonScope) 1159 .compile( 1160 subject -> { 1161 subject.hasErrorCount(0); 1162 subject.generatedSource( 1163 goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory")); 1164 }); 1165 } 1166 1167 @Test testQualifierMetadataOnProvides()1168 public void testQualifierMetadataOnProvides() throws Exception { 1169 Source module = 1170 CompilerTests.javaSource( 1171 "test.ScopedBinding", 1172 "package test;", 1173 "", 1174 "import dagger.Module;", 1175 "import dagger.Provides;", 1176 "import javax.inject.Singleton;", 1177 "", 1178 "@Module", 1179 "interface MyModule {", 1180 " @Provides", 1181 " @NonQualifier", 1182 " @MethodQualifier", 1183 " static String provideString(@NonQualifier @ParamQualifier int i) {", 1184 " return \"\";", 1185 " }", 1186 "}"); 1187 Source methodQualifier = 1188 CompilerTests.javaSource( 1189 "test.MethodQualifier", 1190 "package test;", 1191 "", 1192 "import javax.inject.Qualifier;", 1193 "", 1194 "@Qualifier", 1195 "@interface MethodQualifier {}"); 1196 Source paramQualifier = 1197 CompilerTests.javaSource( 1198 "test.ParamQualifier", 1199 "package test;", 1200 "", 1201 "import javax.inject.Qualifier;", 1202 "", 1203 "@Qualifier", 1204 "@interface ParamQualifier {}"); 1205 Source nonQualifier = 1206 CompilerTests.javaSource( 1207 "test.NonQualifier", 1208 "package test;", 1209 "", 1210 "@interface NonQualifier {}"); 1211 1212 CompilerTests.daggerCompiler(module, methodQualifier, paramQualifier, nonQualifier) 1213 .compile( 1214 subject -> { 1215 subject.hasErrorCount(0); 1216 subject.generatedSource( 1217 goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory")); 1218 }); 1219 } 1220 1221 private static final String BINDS_METHOD = "@Binds abstract Foo bindFoo(FooImpl impl);"; 1222 private static final String MULTIBINDS_METHOD = "@Multibinds abstract Set<Foo> foos();"; 1223 private static final String STATIC_PROVIDES_METHOD = 1224 "@Provides static Bar provideBar() { return new Bar(); }"; 1225 private static final String INSTANCE_PROVIDES_METHOD = 1226 "@Provides Baz provideBaz() { return new Baz(); }"; 1227 private static final String SOME_ABSTRACT_METHOD = "abstract void blah();"; 1228 1229 @Test bindsWithInstanceProvides()1230 public void bindsWithInstanceProvides() { 1231 compileMethodCombination(BINDS_METHOD, INSTANCE_PROVIDES_METHOD) 1232 .compile( 1233 subject -> { 1234 subject.hasErrorCount(1); 1235 subject.hasErrorContaining( 1236 "A @Module may not contain both non-static and abstract binding methods"); 1237 }); 1238 } 1239 1240 @Test multibindsWithInstanceProvides()1241 public void multibindsWithInstanceProvides() { 1242 compileMethodCombination(MULTIBINDS_METHOD, INSTANCE_PROVIDES_METHOD) 1243 .compile( 1244 subject -> { 1245 subject.hasErrorCount(1); 1246 subject.hasErrorContaining( 1247 "A @Module may not contain both non-static and abstract binding methods"); 1248 }); 1249 } 1250 1251 @Test bindsWithStaticProvides()1252 public void bindsWithStaticProvides() { 1253 compileMethodCombination(BINDS_METHOD, STATIC_PROVIDES_METHOD) 1254 .compile(subject -> subject.hasErrorCount(0)); 1255 } 1256 1257 @Test bindsWithMultibinds()1258 public void bindsWithMultibinds() { 1259 compileMethodCombination(BINDS_METHOD, MULTIBINDS_METHOD) 1260 .compile(subject -> subject.hasErrorCount(0)); 1261 } 1262 1263 @Test multibindsWithStaticProvides()1264 public void multibindsWithStaticProvides() { 1265 compileMethodCombination(MULTIBINDS_METHOD, STATIC_PROVIDES_METHOD) 1266 .compile(subject -> subject.hasErrorCount(0)); 1267 } 1268 1269 @Test instanceProvidesWithAbstractMethod()1270 public void instanceProvidesWithAbstractMethod() { 1271 compileMethodCombination(INSTANCE_PROVIDES_METHOD, SOME_ABSTRACT_METHOD) 1272 .compile(subject -> subject.hasErrorCount(0)); 1273 } 1274 compileMethodCombination(String... methodLines)1275 private CompilerTests.DaggerCompiler compileMethodCombination(String... methodLines) { 1276 Source fooFile = 1277 CompilerTests.javaSource( 1278 "test.Foo", 1279 "package test;", 1280 "", 1281 "interface Foo {}"); 1282 Source fooImplFile = 1283 CompilerTests.javaSource( 1284 "test.FooImpl", 1285 "package test;", 1286 "", 1287 "import javax.inject.Inject;", 1288 "", 1289 "final class FooImpl implements Foo {", 1290 " @Inject FooImpl() {}", 1291 "}"); 1292 Source barFile = 1293 CompilerTests.javaSource( 1294 "test.Bar", 1295 "package test;", 1296 "", 1297 "final class Bar {}"); 1298 Source bazFile = 1299 CompilerTests.javaSource( 1300 "test.Baz", 1301 "package test;", 1302 "", 1303 "final class Baz {}"); 1304 1305 ImmutableList<String> moduleLines = 1306 new ImmutableList.Builder<String>() 1307 .add( 1308 "package test;", 1309 "", 1310 "import dagger.Binds;", 1311 "import dagger.Module;", 1312 "import dagger.Provides;", 1313 "import dagger.multibindings.Multibinds;", 1314 "import java.util.Set;", 1315 "", 1316 "@Module abstract class TestModule {") 1317 .add(methodLines) 1318 .add("}") 1319 .build(); 1320 1321 Source bindsMethodAndInstanceProvidesMethodModuleFile = 1322 CompilerTests.javaSource("test.TestModule", moduleLines); 1323 return CompilerTests.daggerCompiler( 1324 fooFile, fooImplFile, barFile, bazFile, bindsMethodAndInstanceProvidesMethodModuleFile); 1325 } 1326 } 1327