1 /* 2 * Copyright (C) 2017 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 import static dagger.internal.codegen.Compilers.daggerCompiler; 24 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION; 25 26 import com.google.testing.compile.Compilation; 27 import com.google.testing.compile.JavaFileObjects; 28 import java.util.Collection; 29 import javax.tools.JavaFileObject; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.Parameterized; 33 import org.junit.runners.Parameterized.Parameters; 34 35 @RunWith(Parameterized.class) 36 public class MapBindingExpressionWithGuavaTest { 37 @Parameters(name = "{0}") parameters()38 public static Collection<Object[]> parameters() { 39 return CompilerMode.TEST_PARAMETERS; 40 } 41 42 private final CompilerMode compilerMode; 43 MapBindingExpressionWithGuavaTest(CompilerMode compilerMode)44 public MapBindingExpressionWithGuavaTest(CompilerMode compilerMode) { 45 this.compilerMode = compilerMode; 46 } 47 48 @Test mapBindings()49 public void mapBindings() { 50 JavaFileObject mapModuleFile = 51 JavaFileObjects.forSourceLines( 52 "test.MapModule", 53 "package test;", 54 "", 55 "import dagger.Module;", 56 "import dagger.Provides;", 57 "import dagger.multibindings.IntKey;", 58 "import dagger.multibindings.IntoMap;", 59 "import dagger.multibindings.LongKey;", 60 "import dagger.multibindings.Multibinds;", 61 "import java.util.Map;", 62 "", 63 "@Module", 64 "interface MapModule {", 65 " @Multibinds Map<String, String> stringMap();", 66 " @Provides @IntoMap @IntKey(0) static int provideInt() { return 0; }", 67 " @Provides @IntoMap @LongKey(0) static long provideLong0() { return 0; }", 68 " @Provides @IntoMap @LongKey(1) static long provideLong1() { return 1; }", 69 " @Provides @IntoMap @LongKey(2) static long provideLong2() { return 2; }", 70 "}"); 71 JavaFileObject subcomponentModuleFile = 72 JavaFileObjects.forSourceLines( 73 "test.SubcomponentMapModule", 74 "package test;", 75 "", 76 "import dagger.Module;", 77 "import dagger.Provides;", 78 "import dagger.multibindings.IntKey;", 79 "import dagger.multibindings.IntoMap;", 80 "import dagger.multibindings.LongKey;", 81 "import dagger.multibindings.Multibinds;", 82 "import java.util.Map;", 83 "", 84 "@Module", 85 "interface SubcomponentMapModule {", 86 " @Provides @IntoMap @LongKey(3) static long provideLong3() { return 3; }", 87 " @Provides @IntoMap @LongKey(4) static long provideLong4() { return 4; }", 88 " @Provides @IntoMap @LongKey(5) static long provideLong5() { return 5; }", 89 "}"); 90 JavaFileObject componentFile = 91 JavaFileObjects.forSourceLines( 92 "test.TestComponent", 93 "package test;", 94 "", 95 "import dagger.Component;", 96 "import java.util.Map;", 97 "import javax.inject.Provider;", 98 "", 99 "@Component(modules = MapModule.class)", 100 "interface TestComponent {", 101 " Map<String, String> strings();", 102 " Map<String, Provider<String>> providerStrings();", 103 "", 104 " Map<Integer, Integer> ints();", 105 " Map<Integer, Provider<Integer>> providerInts();", 106 " Map<Long, Long> longs();", 107 " Map<Long, Provider<Long>> providerLongs();", 108 "", 109 " Sub sub();", 110 "}"); 111 JavaFileObject subcomponent = 112 JavaFileObjects.forSourceLines( 113 "test.Sub", 114 "package test;", 115 "", 116 "import dagger.Subcomponent;", 117 "import java.util.Map;", 118 "import javax.inject.Provider;", 119 "", 120 "@Subcomponent(modules = SubcomponentMapModule.class)", 121 "interface Sub {", 122 " Map<Long, Long> longs();", 123 " Map<Long, Provider<Long>> providerLongs();", 124 "}"); 125 JavaFileObject generatedComponent = 126 compilerMode 127 .javaFileBuilder("test.DaggerTestComponent") 128 .addLines( 129 "package test;", 130 "", 131 GENERATED_ANNOTATION, 132 "final class DaggerTestComponent implements TestComponent {") 133 .addLinesIn( 134 FAST_INIT_MODE, 135 " private volatile Provider<Integer> provideIntProvider;", 136 " private volatile Provider<Long> provideLong0Provider;", 137 " private volatile Provider<Long> provideLong1Provider;", 138 " private volatile Provider<Long> provideLong2Provider;", 139 "", 140 " private Provider<Integer> getProvideIntProvider() {", 141 " Object local = provideIntProvider;", 142 " if (local == null) {", 143 " local = new SwitchingProvider<>(0);", 144 " provideIntProvider = (Provider<Integer>) local;", 145 " }", 146 " return (Provider<Integer>) local;", 147 " }", 148 "", 149 " private Provider<Long> getProvideLong0Provider() {", 150 " Object local = provideLong0Provider;", 151 " if (local == null) {", 152 " local = new SwitchingProvider<>(1);", 153 " provideLong0Provider = (Provider<Long>) local;", 154 " }", 155 " return (Provider<Long>) local;", 156 " }", 157 "", 158 " private Provider<Long> getProvideLong1Provider() {", 159 " Object local = provideLong1Provider;", 160 " if (local == null) {", 161 " local = new SwitchingProvider<>(2);", 162 " provideLong1Provider = (Provider<Long>) local;", 163 " }", 164 " return (Provider<Long>) local;", 165 " }", 166 "", 167 " private Provider<Long> getProvideLong2Provider() {", 168 " Object local = provideLong2Provider;", 169 " if (local == null) {", 170 " local = new SwitchingProvider<>(3);", 171 " provideLong2Provider = (Provider<Long>) local;", 172 " }", 173 " return (Provider<Long>) local;", 174 " }") 175 .addLines( 176 " @Override", 177 " public Map<String, String> strings() {", 178 " return ImmutableMap.<String, String>of();", 179 " }", 180 "", 181 " @Override", 182 " public Map<String, Provider<String>> providerStrings() {", 183 " return ImmutableMap.<String, Provider<String>>of();", 184 " }", 185 "", 186 " @Override", 187 " public Map<Integer, Integer> ints() {", 188 " return ImmutableMap.<Integer, Integer>of(0, MapModule.provideInt());", 189 " }", 190 "", 191 " @Override", 192 " public Map<Integer, Provider<Integer>> providerInts() {", 193 " return ImmutableMap.<Integer, Provider<Integer>>of(") 194 .addLinesIn( 195 DEFAULT_MODE, // 196 " 0, MapModule_ProvideIntFactory.create());") 197 .addLinesIn( 198 FAST_INIT_MODE, // 199 " 0, getProvideIntProvider());") 200 .addLines( 201 " }", 202 "", 203 " @Override", 204 " public Map<Long, Long> longs() {", 205 " return ImmutableMap.<Long, Long>of(", 206 " 0L, MapModule.provideLong0(),", 207 " 1L, MapModule.provideLong1(),", 208 " 2L, MapModule.provideLong2());", 209 " }", 210 "", 211 " @Override", 212 " public Map<Long, Provider<Long>> providerLongs() {", 213 " return ImmutableMap.<Long, Provider<Long>>of(") 214 .addLinesIn( 215 DEFAULT_MODE, 216 " 0L, MapModule_ProvideLong0Factory.create(),", 217 " 1L, MapModule_ProvideLong1Factory.create(),", 218 " 2L, MapModule_ProvideLong2Factory.create());") 219 .addLinesIn( 220 FAST_INIT_MODE, 221 " 0L, getProvideLong0Provider(),", 222 " 1L, getProvideLong1Provider(),", 223 " 2L, getProvideLong2Provider());") 224 .addLines( 225 " }", 226 "", 227 " @Override", 228 " public Sub sub() {", 229 " return new SubImpl();", 230 " }", 231 "", 232 " private final class SubImpl implements Sub {") 233 .addLinesIn( 234 FAST_INIT_MODE, 235 " private volatile Provider<Long> provideLong3Provider;", 236 " private volatile Provider<Long> provideLong4Provider;", 237 " private volatile Provider<Long> provideLong5Provider;", 238 " private SubImpl() {}", 239 "", 240 " private Provider<Long> getProvideLong3Provider() {", 241 " Object local = provideLong3Provider;", 242 " if (local == null) {", 243 " local = new SwitchingProvider<>(0);", 244 " provideLong3Provider = (Provider<Long>) local;", 245 " }", 246 " return (Provider<Long>) local;", 247 " }", 248 "", 249 " private Provider<Long> getProvideLong4Provider() {", 250 " Object local = provideLong4Provider;", 251 " if (local == null) {", 252 " local = new SwitchingProvider<>(1);", 253 " provideLong4Provider = (Provider<Long>) local;", 254 " }", 255 " return (Provider<Long>) local;", 256 " }", 257 "", 258 " private Provider<Long> getProvideLong5Provider() {", 259 " Object local = provideLong5Provider;", 260 " if (local == null) {", 261 " local = new SwitchingProvider<>(2);", 262 " provideLong5Provider = (Provider<Long>) local;", 263 " }", 264 " return (Provider<Long>) local;", 265 " }") 266 .addLines( 267 " @Override", 268 " public Map<Long, Long> longs() {", 269 " return ImmutableMap.<Long, Long>builderWithExpectedSize(6)", 270 " .put(0L, MapModule.provideLong0())", 271 " .put(1L, MapModule.provideLong1())", 272 " .put(2L, MapModule.provideLong2())", 273 " .put(3L, SubcomponentMapModule.provideLong3())", 274 " .put(4L, SubcomponentMapModule.provideLong4())", 275 " .put(5L, SubcomponentMapModule.provideLong5())", 276 " .build();", 277 " }", 278 "", 279 " @Override", 280 " public Map<Long, Provider<Long>> providerLongs() {", 281 " return ImmutableMap.<Long, Provider<Long>>builderWithExpectedSize(6)") 282 .addLinesIn( 283 DEFAULT_MODE, 284 " .put(0L, MapModule_ProvideLong0Factory.create())", 285 " .put(1L, MapModule_ProvideLong1Factory.create())", 286 " .put(2L, MapModule_ProvideLong2Factory.create())", 287 " .put(3L, SubcomponentMapModule_ProvideLong3Factory.create())", 288 " .put(4L, SubcomponentMapModule_ProvideLong4Factory.create())", 289 " .put(5L, SubcomponentMapModule_ProvideLong5Factory.create())") 290 .addLinesIn( 291 FAST_INIT_MODE, 292 " .put(0L, DaggerTestComponent.this.getProvideLong0Provider())", 293 " .put(1L, DaggerTestComponent.this.getProvideLong1Provider())", 294 " .put(2L, DaggerTestComponent.this.getProvideLong2Provider())", 295 " .put(3L, getProvideLong3Provider())", 296 " .put(4L, getProvideLong4Provider())", 297 " .put(5L, getProvideLong5Provider())") 298 .addLines( // 299 " .build();", " }") 300 .addLinesIn( 301 FAST_INIT_MODE, 302 " private final class SwitchingProvider<T> implements Provider<T> {", 303 " private final int id;", 304 "", 305 " SwitchingProvider(int id) {", 306 " this.id = id;", 307 " }", 308 "", 309 " @SuppressWarnings(\"unchecked\")", 310 " @Override", 311 " public T get() {", 312 " switch (id) {", 313 " case 0: return (T) (Long) SubcomponentMapModule.provideLong3();", 314 " case 1: return (T) (Long) SubcomponentMapModule.provideLong4();", 315 " case 2: return (T) (Long) SubcomponentMapModule.provideLong5();", 316 " default: throw new AssertionError(id);", 317 " }", 318 " }", 319 " }", 320 " }", 321 "", 322 " private final class SwitchingProvider<T> implements Provider<T> {", 323 " private final int id;", 324 "", 325 " SwitchingProvider(int id) {", 326 " this.id = id;", 327 " }", 328 "", 329 " @SuppressWarnings(\"unchecked\")", 330 " @Override", 331 " public T get() {", 332 " switch (id) {", 333 " case 0: return (T) (Integer) MapModule.provideInt();", 334 " case 1: return (T) (Long) MapModule.provideLong0();", 335 " case 2: return (T) (Long) MapModule.provideLong1();", 336 " case 3: return (T) (Long) MapModule.provideLong2();", 337 " default: throw new AssertionError(id);", 338 " }", 339 " }", 340 " }", 341 "}") 342 .build(); 343 Compilation compilation = 344 daggerCompiler() 345 .withOptions(compilerMode.javacopts()) 346 .compile(mapModuleFile, componentFile, subcomponentModuleFile, subcomponent); 347 assertThat(compilation).succeeded(); 348 assertThat(compilation) 349 .generatedSourceFile("test.DaggerTestComponent") 350 .containsElementsIn(generatedComponent); 351 } 352 353 @Test inaccessible()354 public void inaccessible() { 355 JavaFileObject inaccessible = 356 JavaFileObjects.forSourceLines( 357 "other.Inaccessible", "package other;", "", "class Inaccessible {}"); 358 JavaFileObject usesInaccessible = 359 JavaFileObjects.forSourceLines( 360 "other.UsesInaccessible", 361 "package other;", 362 "", 363 "import java.util.Map;", 364 "import javax.inject.Inject;", 365 "", 366 "public class UsesInaccessible {", 367 " @Inject UsesInaccessible(Map<Integer, Inaccessible> map) {}", 368 "}"); 369 370 JavaFileObject module = 371 JavaFileObjects.forSourceLines( 372 "other.TestModule", 373 "package other;", 374 "", 375 "import dagger.Module;", 376 "import dagger.multibindings.Multibinds;", 377 "import java.util.Map;", 378 "", 379 "@Module", 380 "public abstract class TestModule {", 381 " @Multibinds abstract Map<Integer, Inaccessible> ints();", 382 "}"); 383 JavaFileObject componentFile = 384 JavaFileObjects.forSourceLines( 385 "test.TestComponent", 386 "package test;", 387 "", 388 "import dagger.Component;", 389 "import java.util.Map;", 390 "import javax.inject.Provider;", 391 "import other.TestModule;", 392 "import other.UsesInaccessible;", 393 "", 394 "@Component(modules = TestModule.class)", 395 "interface TestComponent {", 396 " UsesInaccessible usesInaccessible();", 397 "}"); 398 JavaFileObject generatedComponent = 399 JavaFileObjects.forSourceLines( 400 "test.DaggerTestComponent", 401 "package test;", 402 "", 403 "import other.UsesInaccessible;", 404 "import other.UsesInaccessible_Factory;", 405 "", 406 GENERATED_ANNOTATION, 407 "final class DaggerTestComponent implements TestComponent {", 408 " @Override", 409 " public UsesInaccessible usesInaccessible() {", 410 " return UsesInaccessible_Factory.newInstance((Map) ImmutableMap.of());", 411 " }", 412 "}"); 413 Compilation compilation = 414 daggerCompiler() 415 .withOptions(compilerMode.javacopts()) 416 .compile(module, inaccessible, usesInaccessible, componentFile); 417 assertThat(compilation).succeeded(); 418 assertThat(compilation) 419 .generatedSourceFile("test.DaggerTestComponent") 420 .containsElementsIn(generatedComponent); 421 } 422 423 @Test subcomponentOmitsInheritedBindings()424 public void subcomponentOmitsInheritedBindings() { 425 JavaFileObject parent = 426 JavaFileObjects.forSourceLines( 427 "test.Parent", 428 "package test;", 429 "", 430 "import dagger.Component;", 431 "", 432 "@Component(modules = ParentModule.class)", 433 "interface Parent {", 434 " Child child();", 435 "}"); 436 JavaFileObject parentModule = 437 JavaFileObjects.forSourceLines( 438 "test.ParentModule", 439 "package test;", 440 "", 441 "import dagger.Module;", 442 "import dagger.Provides;", 443 "import dagger.multibindings.IntoMap;", 444 "import dagger.multibindings.StringKey;", 445 "", 446 "@Module", 447 "class ParentModule {", 448 " @Provides @IntoMap @StringKey(\"parent key\") Object parentKeyObject() {", 449 " return \"parent value\";", 450 " }", 451 "}"); 452 JavaFileObject child = 453 JavaFileObjects.forSourceLines( 454 "test.Child", 455 "package test;", 456 "", 457 "import dagger.Subcomponent;", 458 "import java.util.Map;", 459 "", 460 "@Subcomponent", 461 "interface Child {", 462 " Map<String, Object> objectMap();", 463 "}"); 464 JavaFileObject generatedComponent = 465 JavaFileObjects.forSourceLines( 466 "test.DaggerParent", 467 "package test;", 468 "", 469 GENERATED_ANNOTATION, 470 "final class DaggerParent implements Parent {", 471 " private final ParentModule parentModule;", 472 "", 473 " private final class ChildImpl implements Child {", 474 " @Override", 475 " public Map<String, Object> objectMap() {", 476 " return ImmutableMap.<String, Object>of(", 477 " \"parent key\",", 478 " ParentModule_ParentKeyObjectFactory.parentKeyObject(", 479 " DaggerParent.this.parentModule));", 480 " }", 481 " }", 482 "}"); 483 484 Compilation compilation = 485 daggerCompiler().withOptions(compilerMode.javacopts()).compile(parent, parentModule, child); 486 assertThat(compilation).succeeded(); 487 assertThat(compilation) 488 .generatedSourceFile("test.DaggerParent") 489 .containsElementsIn(generatedComponent); 490 } 491 492 @Test productionComponents()493 public void productionComponents() { 494 JavaFileObject mapModuleFile = 495 JavaFileObjects.forSourceLines( 496 "test.MapModule", 497 "package test;", 498 "", 499 "import dagger.Module;", 500 "import dagger.multibindings.Multibinds;", 501 "import java.util.Map;", 502 "", 503 "@Module", 504 "interface MapModule {", 505 " @Multibinds Map<String, String> stringMap();", 506 "}"); 507 JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent", 508 "package test;", 509 "", 510 "import com.google.common.util.concurrent.ListenableFuture;", 511 "import dagger.producers.ProductionComponent;", 512 "import java.util.Map;", 513 "", 514 "@ProductionComponent(modules = MapModule.class)", 515 "interface TestComponent {", 516 " ListenableFuture<Map<String, String>> stringMap();", 517 "}"); 518 JavaFileObject generatedComponent = 519 JavaFileObjects.forSourceLines( 520 "test.DaggerTestComponent", 521 "package test;", 522 "", 523 "import dagger.producers.internal.CancellationListener;", 524 "", 525 GENERATED_ANNOTATION, 526 "final class DaggerTestComponent implements TestComponent, " 527 + "CancellationListener {", 528 " @Override", 529 " public ListenableFuture<Map<String, String>> stringMap() {", 530 " return Futures.immediateFuture(", 531 " (Map<String, String>) ImmutableMap.<String, String>of());", 532 " }", 533 "", 534 " @Override", 535 " public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}", 536 "}"); 537 Compilation compilation = 538 compilerWithOptions( 539 compilerMode 540 , CompilerMode.JAVA7 541 ) 542 .compile(mapModuleFile, componentFile); 543 assertThat(compilation).succeeded(); 544 assertThat(compilation) 545 .generatedSourceFile("test.DaggerTestComponent") 546 .containsElementsIn(generatedComponent); 547 } 548 } 549