1 /* 2 * Copyright (C) 2020 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.testing.compile.CompilationSubject.assertThat; 20 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; 21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; 22 import static dagger.internal.codegen.Compilers.compilerWithOptions; 23 24 import com.google.common.collect.ImmutableCollection; 25 import com.google.testing.compile.Compilation; 26 import com.google.testing.compile.JavaFileObjects; 27 import javax.tools.JavaFileObject; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.Parameterized; 31 import org.junit.runners.Parameterized.Parameters; 32 33 @RunWith(Parameterized.class) 34 public class AssistedFactoryTest { 35 @Parameters(name = "{0}") parameters()36 public static ImmutableCollection<Object[]> parameters() { 37 return CompilerMode.TEST_PARAMETERS; 38 } 39 40 private final CompilerMode compilerMode; 41 AssistedFactoryTest(CompilerMode compilerMode)42 public AssistedFactoryTest(CompilerMode compilerMode) { 43 this.compilerMode = compilerMode; 44 } 45 46 @Test testAssistedFactory()47 public void testAssistedFactory() { 48 JavaFileObject foo = 49 JavaFileObjects.forSourceLines( 50 "test.Foo", 51 "package test;", 52 "", 53 "import dagger.assisted.Assisted;", 54 "import dagger.assisted.AssistedInject;", 55 "", 56 "class Foo {", 57 " @AssistedInject", 58 " Foo(@Assisted String str, Bar bar) {}", 59 "}"); 60 JavaFileObject fooFactory = 61 JavaFileObjects.forSourceLines( 62 "test.FooFactory", 63 "package test;", 64 "", 65 "import dagger.assisted.AssistedFactory;", 66 "", 67 "@AssistedFactory", 68 "interface FooFactory {", 69 " Foo create(String factoryStr);", 70 "}"); 71 JavaFileObject bar = 72 JavaFileObjects.forSourceLines( 73 "test.Bar", 74 "package test;", 75 "", 76 "import javax.inject.Inject;", 77 "", 78 "class Bar {", 79 " @Inject Bar() {}", 80 "}"); 81 JavaFileObject component = 82 JavaFileObjects.forSourceLines( 83 "test.TestComponent", 84 "package test;", 85 "", 86 "import dagger.Component;", 87 "", 88 "@Component", 89 "interface TestComponent {", 90 " FooFactory fooFactory();", 91 "}"); 92 Compilation compilation = 93 compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component); 94 assertThat(compilation).succeeded(); 95 JavaFileObject generatedComponent = 96 compilerMode 97 .javaFileBuilder("test.DaggerTestComponent") 98 .addLines("package test;", "", GeneratedLines.generatedAnnotations()) 99 .addLinesIn( 100 FAST_INIT_MODE, 101 "final class DaggerTestComponent implements TestComponent {", 102 " private final DaggerTestComponent testComponent = this;", 103 " private Provider<FooFactory> fooFactoryProvider;", 104 "", 105 " @SuppressWarnings(\"unchecked\")", 106 " private void initialize() {", 107 " this.fooFactoryProvider = SingleCheck.provider(new" 108 + " SwitchingProvider<FooFactory>(testComponent, 0));", 109 " }", 110 "", 111 " @Override", 112 " public FooFactory fooFactory() {", 113 " return fooFactoryProvider.get();", 114 " }", 115 "", 116 " private static final class SwitchingProvider<T> implements Provider<T> {", 117 " private final DaggerTestComponent testComponent;", 118 " private final int id;", 119 "", 120 " @SuppressWarnings(\"unchecked\")", 121 " @Override", 122 " public T get() {", 123 " switch (id) {", 124 " case 0:", 125 " return (T) new FooFactory() {", 126 " @Override", 127 " public Foo create(String str) {", 128 " return new Foo(str, new Bar());", 129 " }", 130 " };", 131 "", 132 " default: throw new AssertionError(id);", 133 " }", 134 " }", 135 " }", 136 "}") 137 .addLinesIn( 138 DEFAULT_MODE, 139 "final class DaggerTestComponent implements TestComponent {", 140 "", 141 " private Foo_Factory fooProvider;", 142 "", 143 " private Provider<FooFactory> fooFactoryProvider;", 144 "", 145 " @SuppressWarnings(\"unchecked\")", 146 " private void initialize() {", 147 " this.fooProvider = Foo_Factory.create(Bar_Factory.create());", 148 " this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);", 149 " }", 150 "", 151 " @Override", 152 " public FooFactory fooFactory() {", 153 " return fooFactoryProvider.get();", 154 " }", 155 "}") 156 .build(); 157 assertThat(compilation) 158 .generatedSourceFile("test.DaggerTestComponent") 159 .containsElementsIn(generatedComponent); 160 } 161 162 @Test testAssistedFactoryCycle()163 public void testAssistedFactoryCycle() { 164 JavaFileObject foo = 165 JavaFileObjects.forSourceLines( 166 "test.Foo", 167 "package test;", 168 "", 169 "import dagger.assisted.Assisted;", 170 "import dagger.assisted.AssistedInject;", 171 "", 172 "class Foo {", 173 " @AssistedInject", 174 " Foo(@Assisted String str, Bar bar) {}", 175 "}"); 176 JavaFileObject fooFactory = 177 JavaFileObjects.forSourceLines( 178 "test.FooFactory", 179 "package test;", 180 "", 181 "import dagger.assisted.AssistedFactory;", 182 "", 183 "@AssistedFactory", 184 "interface FooFactory {", 185 " Foo create(String factoryStr);", 186 "}"); 187 JavaFileObject bar = 188 JavaFileObjects.forSourceLines( 189 "test.Bar", 190 "package test;", 191 "", 192 "import javax.inject.Inject;", 193 "", 194 "class Bar {", 195 " @Inject Bar(FooFactory fooFactory) {}", 196 "}"); 197 JavaFileObject component = 198 JavaFileObjects.forSourceLines( 199 "test.TestComponent", 200 "package test;", 201 "", 202 "import dagger.Component;", 203 "", 204 "@Component", 205 "interface TestComponent {", 206 " FooFactory fooFactory();", 207 "}"); 208 Compilation compilation = 209 compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component); 210 assertThat(compilation).succeeded(); 211 JavaFileObject generatedComponent = 212 compilerMode 213 .javaFileBuilder("test.DaggerTestComponent") 214 .addLines("package test;", "", GeneratedLines.generatedAnnotations()) 215 .addLinesIn( 216 FAST_INIT_MODE, 217 "final class DaggerTestComponent implements TestComponent {", 218 " private final DaggerTestComponent testComponent = this;", 219 " private Provider<FooFactory> fooFactoryProvider;", 220 "", 221 " private Bar bar() {", 222 " return new Bar(fooFactoryProvider.get());", 223 " }", 224 "", 225 " @SuppressWarnings(\"unchecked\")", 226 " private void initialize() {", 227 " this.fooFactoryProvider = SingleCheck.provider(new" 228 + " SwitchingProvider<FooFactory>(testComponent, 0));", 229 " }", 230 "", 231 " @Override", 232 " public FooFactory fooFactory() {", 233 " return fooFactoryProvider.get();", 234 " }", 235 "", 236 " private static final class SwitchingProvider<T> implements Provider<T> {", 237 " private final DaggerTestComponent testComponent;", 238 " private final int id;", 239 "", 240 " @SuppressWarnings(\"unchecked\")", 241 " @Override", 242 " public T get() {", 243 " switch (id) {", 244 " case 0:", 245 " return (T) new FooFactory() {", 246 " @Override", 247 " public Foo create(String str) {", 248 " return new Foo(str, testComponent.bar())", 249 " }", 250 " };", 251 "", 252 " default: throw new AssertionError(id);", 253 " }", 254 " }", 255 " }", 256 "}") 257 .addLinesIn( 258 DEFAULT_MODE, 259 "final class DaggerTestComponent implements TestComponent {", 260 "", 261 " private Provider<FooFactory> fooFactoryProvider;", 262 "", 263 " private Provider<Bar> barProvider;", 264 "", 265 " private Foo_Factory fooProvider;", 266 "", 267 " @SuppressWarnings(\"unchecked\")", 268 " private void initialize() {", 269 " this.fooFactoryProvider = new DelegateFactory<>();", 270 " this.barProvider = Bar_Factory.create(fooFactoryProvider);", 271 " this.fooProvider = Foo_Factory.create(barProvider);", 272 " DelegateFactory.setDelegate(", 273 " fooFactoryProvider, FooFactory_Impl.create(fooProvider));", 274 " }", 275 "", 276 " @Override", 277 " public FooFactory fooFactory() {", 278 " return fooFactoryProvider.get();", 279 " }", 280 "}") 281 .build(); 282 assertThat(compilation) 283 .generatedSourceFile("test.DaggerTestComponent") 284 .containsElementsIn(generatedComponent); 285 } 286 287 @Test assistedParamConflictsWithComponentFieldName_successfulyDeduped()288 public void assistedParamConflictsWithComponentFieldName_successfulyDeduped() { 289 JavaFileObject foo = 290 JavaFileObjects.forSourceLines( 291 "test.Foo", 292 "package test;", 293 "", 294 "import dagger.assisted.Assisted;", 295 "import dagger.assisted.AssistedInject;", 296 "import javax.inject.Provider;", 297 "", 298 "class Foo {", 299 " @AssistedInject", 300 " Foo(@Assisted String testComponent, Provider<Bar> bar) {}", 301 "}"); 302 JavaFileObject fooFactory = 303 JavaFileObjects.forSourceLines( 304 "test.FooFactory", 305 "package test;", 306 "", 307 "import dagger.assisted.AssistedFactory;", 308 "", 309 "@AssistedFactory", 310 "interface FooFactory {", 311 " Foo create(String factoryStr);", 312 "}"); 313 JavaFileObject bar = 314 JavaFileObjects.forSourceLines( 315 "test.Bar", 316 "package test;", 317 "", 318 "import javax.inject.Inject;", 319 "", 320 "class Bar {", 321 " @Inject Bar() {}", 322 "}"); 323 JavaFileObject component = 324 JavaFileObjects.forSourceLines( 325 "test.TestComponent", 326 "package test;", 327 "", 328 "import dagger.Component;", 329 "", 330 "@Component", 331 "interface TestComponent {", 332 " FooFactory fooFactory();", 333 "}"); 334 JavaFileObject generatedComponent = 335 compilerMode 336 .javaFileBuilder("test.DaggerTestComponent") 337 .addLines("package test;", "", GeneratedLines.generatedAnnotations()) 338 .addLinesIn( 339 FAST_INIT_MODE, 340 "final class DaggerTestComponent implements TestComponent {", 341 " private final DaggerTestComponent testComponent = this;", 342 " private Provider<FooFactory> fooFactoryProvider;", 343 "", 344 " @SuppressWarnings(\"unchecked\")", 345 " private void initialize() {", 346 " this.barProvider = new SwitchingProvider<>(testComponent, 1);", 347 " this.fooFactoryProvider = SingleCheck.provider(", 348 " new SwitchingProvider<FooFactory>(testComponent, 0));", 349 " }", 350 "", 351 " @Override", 352 " public FooFactory fooFactory() {", 353 " return fooFactoryProvider.get();", 354 " }", 355 "", 356 " private static final class SwitchingProvider<T> implements Provider<T> {", 357 " private final DaggerTestComponent testComponent;", 358 " private final int id;", 359 "", 360 " @SuppressWarnings(\"unchecked\")", 361 " @Override", 362 " public T get() {", 363 " switch (id) {", 364 " case 0:", 365 " return (T) new FooFactory() {", 366 " @Override", 367 " public Foo create(String testComponent2) {", 368 " return new Foo(testComponent2, testComponent.barProvider);", 369 " }", 370 " };", 371 " case 1: return (T) new Bar();", 372 " default: throw new AssertionError(id);", 373 " }", 374 " }", 375 " }", 376 "}") 377 .addLinesIn( 378 DEFAULT_MODE, 379 "final class DaggerTestComponent implements TestComponent {", 380 "", 381 " private Foo_Factory fooProvider;", 382 "", 383 " private Provider<FooFactory> fooFactoryProvider;", 384 "", 385 " @SuppressWarnings(\"unchecked\")", 386 " private void initialize() {", 387 " this.fooProvider = Foo_Factory.create(Bar_Factory.create());", 388 " this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);", 389 " }", 390 "", 391 " @Override", 392 " public FooFactory fooFactory() {", 393 " return fooFactoryProvider.get();", 394 " }", 395 "}") 396 .build(); 397 398 Compilation compilation = 399 compilerWithOptions(compilerMode.javacopts()).compile(foo, bar, fooFactory, component); 400 401 assertThat(compilation).succeeded(); 402 assertThat(compilation) 403 .generatedSourceFile("test.DaggerTestComponent") 404 .containsElementsIn(generatedComponent); 405 } 406 407 @Test testFactoryGeneratorDuplicatedParamNames()408 public void testFactoryGeneratorDuplicatedParamNames() { 409 JavaFileObject componentSrc = 410 JavaFileObjects.forSourceLines( 411 "test.TestComponent", 412 "package test;", 413 "", 414 "import dagger.BindsInstance;", 415 "import dagger.Component;", 416 "", 417 "@Component", 418 "interface TestComponent {", 419 " @Component.Factory", 420 " interface Factory {", 421 " TestComponent create(@BindsInstance Bar arg);", 422 "}", 423 " FooFactory getFooFactory();", 424 "}"); 425 JavaFileObject factorySrc = 426 JavaFileObjects.forSourceLines( 427 "test.FooFactory", 428 "package test;", 429 "", 430 "import dagger.assisted.AssistedFactory;", 431 "", 432 "@AssistedFactory", 433 "public interface FooFactory {", 434 " Foo create(Integer arg);", 435 "}"); 436 JavaFileObject barSrc = 437 JavaFileObjects.forSourceLines("test.Bar", "package test;", "", "interface Bar {}"); 438 JavaFileObject injectSrc = 439 JavaFileObjects.forSourceLines( 440 "test.Foo", 441 "package test;", 442 "", 443 "import dagger.assisted.Assisted;", 444 "import dagger.assisted.AssistedInject;", 445 "", 446 "class Foo {", 447 " @AssistedInject", 448 " Foo(Bar arg, @Assisted Integer argProvider) {}", 449 "}"); 450 JavaFileObject generatedSrc = 451 compilerMode 452 .javaFileBuilder("test.DaggerTestComponent") 453 .addLines( 454 "package test;", 455 "", 456 "@ScopeMetadata", 457 "@QualifierMetadata", 458 GeneratedLines.generatedAnnotations()) 459 .addLinesIn( 460 FAST_INIT_MODE, 461 "public final class Foo_Factory {", 462 " private final Provider<Bar> argProvider;", 463 "", 464 " public Foo_Factory(Provider<Bar> argProvider) {", 465 " this.argProvider = argProvider;", 466 " }", 467 "", 468 " public Foo get(Integer argProvider2) {", 469 " return newInstance(argProvider.get(), argProvider2);", 470 " }", 471 "", 472 " public static Foo_Factory create(Provider<Bar> argProvider) {", 473 " return new Foo_Factory(argProvider);", 474 " }", 475 "", 476 " public static Foo newInstance(Object arg, Integer argProvider) {", 477 " return new Foo((Bar) arg, argProvider);", 478 " }", 479 "}") 480 .addLinesIn( 481 DEFAULT_MODE, 482 "public final class Foo_Factory {", 483 " private final Provider<Bar> argProvider;", 484 "", 485 " public Foo_Factory(Provider<Bar> argProvider) {", 486 " this.argProvider = argProvider;", 487 " }", 488 "", 489 " public Foo get(Integer argProvider2) {", 490 " return newInstance(argProvider.get(), argProvider2);", 491 " }", 492 "", 493 " public static Foo_Factory create(Provider<Bar> argProvider) {", 494 " return new Foo_Factory(argProvider);", 495 " }", 496 "", 497 " public static Foo newInstance(Object arg, Integer argProvider) {", 498 " return new Foo((Bar) arg, argProvider);", 499 " }", 500 "}") 501 .build(); 502 Compilation compilation = 503 compilerWithOptions(compilerMode.javacopts()) 504 .compile(componentSrc, factorySrc, barSrc, injectSrc); 505 assertThat(compilation).succeeded(); 506 assertThat(compilation) 507 .generatedSourceFile("test.Foo_Factory") 508 .containsElementsIn(generatedSrc); 509 } 510 511 @Test testParameterizedAssistParam()512 public void testParameterizedAssistParam() { 513 JavaFileObject componentSrc = 514 JavaFileObjects.forSourceLines( 515 "test.TestComponent", 516 "package test;", 517 "", 518 "import dagger.Component;", 519 "", 520 "@Component", 521 "interface TestComponent {", 522 " FooFactory<String> getFooFactory();", 523 "}"); 524 JavaFileObject factorySrc = 525 JavaFileObjects.forSourceLines( 526 "test.FooFactory", 527 "package test;", 528 "", 529 "import dagger.assisted.AssistedFactory;", 530 "", 531 "@AssistedFactory", 532 "public interface FooFactory<T> {", 533 " Foo<T> create(T arg);", 534 "}"); 535 JavaFileObject injectSrc = 536 JavaFileObjects.forSourceLines( 537 "test.Foo", 538 "package test;", 539 "", 540 "import dagger.assisted.Assisted;", 541 "import dagger.assisted.AssistedInject;", 542 "", 543 "class Foo<T> {", 544 " @AssistedInject", 545 " Foo(@Assisted T arg) {}", 546 "}"); 547 JavaFileObject generatedSrc = 548 compilerMode 549 .javaFileBuilder("test.DaggerTestComponent") 550 .addLines("package test;", "", GeneratedLines.generatedAnnotations()) 551 .addLinesIn( 552 FAST_INIT_MODE, 553 "final class DaggerTestComponent implements TestComponent {", 554 " private final DaggerTestComponent testComponent = this;", 555 " private Provider<FooFactory<String>> fooFactoryProvider;", 556 "", 557 " @SuppressWarnings(\"unchecked\")", 558 " private void initialize() {", 559 " this.fooFactoryProvider = SingleCheck.provider(new" 560 + " SwitchingProvider<FooFactory<String>>(testComponent, 0));", 561 " }", 562 "", 563 " @Override", 564 " public FooFactory<String> getFooFactory() {", 565 " return fooFactoryProvider.get();", 566 " }", 567 " ", 568 " private static final class SwitchingProvider<T> implements Provider<T> {", 569 " private final DaggerTestComponent testComponent;", 570 " private final int id;", 571 "", 572 " @SuppressWarnings(\"unchecked\")", 573 " @Override", 574 " public T get() {", 575 " switch (id) {", 576 " case 0: return (T) new FooFactory<String>() {", 577 " @Override", 578 " public Foo<String> create(String arg) {", 579 " return new Foo<String>(arg)", 580 " }", 581 " };", 582 "", 583 " default: throw new AssertionError(id);", 584 " }", 585 " }", 586 " }", 587 "}") 588 .addLinesIn( 589 DEFAULT_MODE, 590 "final class DaggerTestComponent implements TestComponent {", 591 " private final DaggerTestComponent testComponent = this;", 592 " private Foo_Factory<String> fooProvider;", 593 " private Provider<FooFactory<String>> fooFactoryProvider;", 594 "", 595 " private DaggerTestComponent() {", 596 " initialize();", 597 " }", 598 "", 599 " public static Builder builder() {", 600 " return new Builder();", 601 " }", 602 "", 603 " public static TestComponent create() {", 604 " return new Builder().build();", 605 " }", 606 "", 607 " @SuppressWarnings(\"unchecked\")", 608 " private void initialize() {", 609 " this.fooProvider = Foo_Factory.create();", 610 " this.fooFactoryProvider = FooFactory_Impl.create(fooProvider);", 611 " }", 612 "", 613 " @Override", 614 " public FooFactory<String> getFooFactory() {", 615 " return fooFactoryProvider.get();", 616 " }", 617 "}") 618 .build(); 619 Compilation compilation = 620 compilerWithOptions(compilerMode.javacopts()).compile(componentSrc, factorySrc, injectSrc); 621 assertThat(compilation).succeeded(); 622 assertThat(compilation) 623 .generatedSourceFile("test.DaggerTestComponent") 624 .containsElementsIn(generatedSrc); 625 } 626 } 627