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