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