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