1 /* 2 * Copyright (C) 2008 Google Inc. 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 com.google.inject.spi; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static com.google.inject.Asserts.getDeclaringSourcePart; 21 import static com.google.inject.Asserts.isIncludeStackTraceComplete; 22 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.Lists; 25 import com.google.inject.AbstractModule; 26 import com.google.inject.Binding; 27 import com.google.inject.Guice; 28 import com.google.inject.Inject; 29 import com.google.inject.Injector; 30 import com.google.inject.Key; 31 import com.google.inject.Module; 32 import com.google.inject.Provider; 33 import com.google.inject.Scope; 34 import com.google.inject.Scopes; 35 import com.google.inject.Singleton; 36 import com.google.inject.Stage; 37 import com.google.inject.name.Names; 38 import java.lang.reflect.Constructor; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.Iterator; 42 import java.util.List; 43 import java.util.concurrent.atomic.AtomicBoolean; 44 import java.util.logging.Logger; 45 import junit.framework.AssertionFailedError; 46 import junit.framework.TestCase; 47 48 /** @author jessewilson@google.com (Jesse Wilson) */ 49 public class SpiBindingsTest extends TestCase { 50 testBindConstant()51 public void testBindConstant() { 52 checkInjector( 53 new AbstractModule() { 54 @Override 55 protected void configure() { 56 bindConstant().annotatedWith(Names.named("one")).to(1); 57 } 58 }, 59 new FailingElementVisitor() { 60 @Override 61 public <T> Void visit(Binding<T> binding) { 62 assertTrue(binding instanceof InstanceBinding); 63 assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey()); 64 return null; 65 } 66 }); 67 } 68 testToInstanceBinding()69 public void testToInstanceBinding() { 70 checkInjector( 71 new AbstractModule() { 72 @Override 73 protected void configure() { 74 bind(String.class).toInstance("A"); 75 } 76 }, 77 new FailingElementVisitor() { 78 @Override 79 public <T> Void visit(Binding<T> binding) { 80 assertTrue(binding instanceof InstanceBinding); 81 checkBindingSource(binding); 82 assertEquals(Key.get(String.class), binding.getKey()); 83 binding.acceptTargetVisitor( 84 new FailingTargetVisitor<T>() { 85 @Override 86 public Void visit(InstanceBinding<? extends T> binding) { 87 assertEquals("A", binding.getInstance()); 88 return null; 89 } 90 }); 91 binding.acceptScopingVisitor( 92 new FailingBindingScopingVisitor() { 93 @Override 94 public Void visitEagerSingleton() { 95 return null; 96 } 97 }); 98 return null; 99 } 100 }); 101 } 102 testToProviderBinding()103 public void testToProviderBinding() { 104 final Provider<String> stringProvider = new StringProvider(); 105 106 checkInjector( 107 new AbstractModule() { 108 @Override 109 protected void configure() { 110 bind(String.class).toProvider(stringProvider); 111 } 112 }, 113 new FailingElementVisitor() { 114 @Override 115 public <T> Void visit(Binding<T> binding) { 116 assertTrue(binding instanceof ProviderInstanceBinding); 117 checkBindingSource(binding); 118 assertEquals(Key.get(String.class), binding.getKey()); 119 binding.acceptTargetVisitor( 120 new FailingTargetVisitor<T>() { 121 @Override 122 public Void visit(ProviderInstanceBinding<? extends T> binding) { 123 assertSame(stringProvider, binding.getUserSuppliedProvider()); 124 return null; 125 } 126 }); 127 return null; 128 } 129 }); 130 } 131 testToProviderKeyBinding()132 public void testToProviderKeyBinding() { 133 checkInjector( 134 new AbstractModule() { 135 @Override 136 protected void configure() { 137 bind(String.class).toProvider(StringProvider.class); 138 } 139 }, 140 new FailingElementVisitor() { 141 @Override 142 public <T> Void visit(Binding<T> binding) { 143 assertTrue(binding instanceof ProviderKeyBinding); 144 checkBindingSource(binding); 145 assertEquals(Key.get(String.class), binding.getKey()); 146 binding.acceptTargetVisitor( 147 new FailingTargetVisitor<T>() { 148 @Override 149 public Void visit(ProviderKeyBinding<? extends T> binding) { 150 assertEquals(Key.get(StringProvider.class), binding.getProviderKey()); 151 return null; 152 } 153 }); 154 return null; 155 } 156 }); 157 } 158 testToKeyBinding()159 public void testToKeyBinding() { 160 final Key<String> aKey = Key.get(String.class, Names.named("a")); 161 final Key<String> bKey = Key.get(String.class, Names.named("b")); 162 163 checkInjector( 164 new AbstractModule() { 165 @Override 166 protected void configure() { 167 bind(aKey).to(bKey); 168 bind(bKey).toInstance("B"); 169 } 170 }, 171 new FailingElementVisitor() { 172 @Override 173 public <T> Void visit(Binding<T> binding) { 174 assertTrue(binding instanceof LinkedKeyBinding); 175 checkBindingSource(binding); 176 assertEquals(aKey, binding.getKey()); 177 binding.acceptTargetVisitor( 178 new FailingTargetVisitor<T>() { 179 @Override 180 public Void visit(LinkedKeyBinding<? extends T> binding) { 181 assertEquals(bKey, binding.getLinkedKey()); 182 return null; 183 } 184 }); 185 return null; 186 } 187 }, 188 new FailingElementVisitor() { 189 @Override 190 public <T> Void visit(Binding<T> binding) { 191 assertEquals(bKey, binding.getKey()); 192 return null; 193 } 194 }); 195 } 196 testToConstructorBinding()197 public void testToConstructorBinding() { 198 checkInjector( 199 new AbstractModule() { 200 @Override 201 protected void configure() { 202 bind(D.class); 203 } 204 }, 205 new FailingElementVisitor() { 206 @Override 207 public <T> Void visit(Binding<T> binding) { 208 assertTrue(binding instanceof ConstructorBinding); 209 checkBindingSource(binding); 210 assertEquals(Key.get(D.class), binding.getKey()); 211 binding.acceptTargetVisitor( 212 new FailingTargetVisitor<T>() { 213 @Override 214 public Void visit(ConstructorBinding<? extends T> binding) { 215 Constructor<?> expected = D.class.getDeclaredConstructors()[0]; 216 assertEquals(expected, binding.getConstructor().getMember()); 217 assertEquals(ImmutableSet.<InjectionPoint>of(), binding.getInjectableMembers()); 218 return null; 219 } 220 }); 221 return null; 222 } 223 }); 224 } 225 testConstantBinding()226 public void testConstantBinding() { 227 checkInjector( 228 new AbstractModule() { 229 @Override 230 protected void configure() { 231 bindConstant().annotatedWith(Names.named("one")).to(1); 232 } 233 }, 234 new FailingElementVisitor() { 235 @Override 236 public <T> Void visit(Binding<T> binding) { 237 assertTrue(binding instanceof InstanceBinding); 238 checkBindingSource(binding); 239 assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey()); 240 binding.acceptTargetVisitor( 241 new FailingTargetVisitor<T>() { 242 @Override 243 public Void visit(InstanceBinding<? extends T> binding) { 244 assertEquals(1, binding.getInstance()); 245 return null; 246 } 247 }); 248 return null; 249 } 250 }); 251 } 252 testConvertedConstantBinding()253 public void testConvertedConstantBinding() { 254 Injector injector = 255 Guice.createInjector( 256 new AbstractModule() { 257 @Override 258 protected void configure() { 259 bindConstant().annotatedWith(Names.named("one")).to("1"); 260 } 261 }); 262 263 Binding<Integer> binding = injector.getBinding(Key.get(Integer.class, Names.named("one"))); 264 assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey()); 265 checkBindingSource(binding); 266 assertTrue(binding instanceof ConvertedConstantBinding); 267 binding.acceptTargetVisitor( 268 new FailingTargetVisitor<Integer>() { 269 @Override 270 public Void visit(ConvertedConstantBinding<? extends Integer> binding) { 271 assertEquals((Integer) 1, binding.getValue()); 272 assertEquals(Key.get(String.class, Names.named("one")), binding.getSourceKey()); 273 return null; 274 } 275 }); 276 } 277 testProviderBinding()278 public void testProviderBinding() { 279 Injector injector = 280 Guice.createInjector( 281 new AbstractModule() { 282 @Override 283 protected void configure() { 284 bind(String.class).toInstance("A"); 285 } 286 }); 287 288 Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {}; 289 Binding<Provider<String>> binding = injector.getBinding(providerOfStringKey); 290 assertEquals(providerOfStringKey, binding.getKey()); 291 checkBindingSource(binding); 292 assertTrue(binding instanceof ProviderBinding); 293 binding.acceptTargetVisitor( 294 new FailingTargetVisitor<Provider<String>>() { 295 @Override 296 public Void visit(ProviderBinding<? extends Provider<String>> binding) { 297 assertEquals(Key.get(String.class), binding.getProvidedKey()); 298 return null; 299 } 300 }); 301 } 302 testScopes()303 public void testScopes() { 304 checkInjector( 305 new AbstractModule() { 306 @Override 307 protected void configure() { 308 bind(String.class) 309 .annotatedWith(Names.named("a")) 310 .toProvider(StringProvider.class) 311 .in(Singleton.class); 312 bind(String.class) 313 .annotatedWith(Names.named("b")) 314 .toProvider(StringProvider.class) 315 .in(Scopes.SINGLETON); 316 bind(String.class) 317 .annotatedWith(Names.named("c")) 318 .toProvider(StringProvider.class) 319 .asEagerSingleton(); 320 bind(String.class).annotatedWith(Names.named("d")).toProvider(StringProvider.class); 321 } 322 }, 323 new FailingElementVisitor() { 324 @Override 325 public <T> Void visit(Binding<T> command) { 326 assertEquals(Key.get(String.class, Names.named("a")), command.getKey()); 327 command.acceptScopingVisitor( 328 new FailingBindingScopingVisitor() { 329 @Override 330 public Void visitScope(Scope scope) { 331 // even though we bound with an annotation, the injector always uses instances 332 assertSame(Scopes.SINGLETON, scope); 333 return null; 334 } 335 }); 336 return null; 337 } 338 }, 339 new FailingElementVisitor() { 340 @Override 341 public <T> Void visit(Binding<T> command) { 342 assertEquals(Key.get(String.class, Names.named("b")), command.getKey()); 343 command.acceptScopingVisitor( 344 new FailingBindingScopingVisitor() { 345 @Override 346 public Void visitScope(Scope scope) { 347 assertSame(Scopes.SINGLETON, scope); 348 return null; 349 } 350 }); 351 return null; 352 } 353 }, 354 new FailingElementVisitor() { 355 @Override 356 public <T> Void visit(Binding<T> command) { 357 assertEquals(Key.get(String.class, Names.named("c")), command.getKey()); 358 command.acceptScopingVisitor( 359 new FailingBindingScopingVisitor() { 360 @Override 361 public Void visitEagerSingleton() { 362 return null; 363 } 364 }); 365 return null; 366 } 367 }, 368 new FailingElementVisitor() { 369 @Override 370 public <T> Void visit(Binding<T> command) { 371 assertEquals(Key.get(String.class, Names.named("d")), command.getKey()); 372 command.acceptScopingVisitor( 373 new FailingBindingScopingVisitor() { 374 @Override 375 public Void visitNoScoping() { 376 return null; 377 } 378 }); 379 return null; 380 } 381 }); 382 } 383 testExtensionSpi()384 public void testExtensionSpi() { 385 final AtomicBoolean visiting = new AtomicBoolean(false); 386 387 final Injector injector = 388 Guice.createInjector( 389 new AbstractModule() { 390 @Override 391 protected void configure() { 392 bind(String.class) 393 .toProvider( 394 new ProviderWithExtensionVisitor<String>() { 395 @Override 396 public <B, V> V acceptExtensionVisitor( 397 BindingTargetVisitor<B, V> visitor, 398 ProviderInstanceBinding<? extends B> binding) { 399 assertSame(this, binding.getUserSuppliedProvider()); 400 // We can't always check for FailingSpiTargetVisitor, 401 // because constructing the injector visits here, and we need 402 // to process the binding as normal 403 if (visiting.get()) { 404 assertTrue( 405 "visitor: " + visitor, 406 visitor instanceof FailingSpiTargetVisitor); 407 return (V) "visited"; 408 } else { 409 return visitor.visit(binding); 410 } 411 } 412 413 @Override 414 public String get() { 415 return "FooBar"; 416 } 417 }); 418 } 419 }); 420 421 visiting.set(true); 422 423 // Check for Provider<String> binding -- that is still a ProviderBinding. 424 Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {}; 425 Binding<Provider<String>> providerBinding = injector.getBinding(providerOfStringKey); 426 assertEquals(providerOfStringKey, providerBinding.getKey()); 427 checkBindingSource(providerBinding); 428 assertTrue("binding: " + providerBinding, providerBinding instanceof ProviderBinding); 429 providerBinding.acceptTargetVisitor( 430 new FailingTargetVisitor<Provider<String>>() { 431 @Override 432 public Void visit(ProviderBinding<? extends Provider<String>> binding) { 433 assertEquals(Key.get(String.class), binding.getProvidedKey()); 434 return null; 435 } 436 }); 437 438 // Check for String binding -- that one is ProviderInstanceBinding, and gets hooked 439 Binding<String> binding = injector.getBinding(String.class); 440 assertEquals(Key.get(String.class), binding.getKey()); 441 checkBindingSource(binding); 442 assertTrue(binding instanceof ProviderInstanceBinding); 443 assertEquals("visited", binding.acceptTargetVisitor(new FailingSpiTargetVisitor<String>())); 444 } 445 446 private static class FailingSpiTargetVisitor<T> extends DefaultBindingTargetVisitor<T, String> { 447 @Override visitOther(Binding<? extends T> binding)448 protected String visitOther(Binding<? extends T> binding) { 449 throw new AssertionFailedError(); 450 } 451 } 452 checkBindingSource(Binding binding)453 public void checkBindingSource(Binding binding) { 454 assertContains(binding.getSource().toString(), getDeclaringSourcePart(getClass())); 455 ElementSource source = (ElementSource) binding.getSource(); 456 assertFalse(source.getModuleClassNames().isEmpty()); 457 if (isIncludeStackTraceComplete()) { 458 assertTrue(source.getStackTrace().length > 0); 459 } else { 460 assertEquals(0, source.getStackTrace().length); 461 } 462 } 463 checkInjector(Module module, ElementVisitor<?>... visitors)464 public void checkInjector(Module module, ElementVisitor<?>... visitors) { 465 Injector injector = Guice.createInjector(module); 466 467 List<Binding<?>> bindings = Lists.newArrayList(injector.getBindings().values()); 468 for (Iterator<Binding<?>> i = bindings.iterator(); i.hasNext(); ) { 469 if (BUILT_IN_BINDINGS.contains(i.next().getKey())) { 470 i.remove(); 471 } 472 } 473 474 Collections.sort(bindings, orderByKey); 475 476 assertEquals(bindings.size(), visitors.length); 477 478 for (int i = 0; i < visitors.length; i++) { 479 ElementVisitor<?> visitor = visitors[i]; 480 Binding<?> binding = bindings.get(i); 481 binding.acceptVisitor(visitor); 482 } 483 } 484 485 private final ImmutableSet<Key<?>> BUILT_IN_BINDINGS = 486 ImmutableSet.of(Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class)); 487 488 private final Comparator<Binding<?>> orderByKey = 489 new Comparator<Binding<?>>() { 490 @Override 491 public int compare(Binding<?> a, Binding<?> b) { 492 return a.getKey().toString().compareTo(b.getKey().toString()); 493 } 494 }; 495 496 private static class StringProvider implements Provider<String> { 497 @Override get()498 public String get() { 499 return "A"; 500 } 501 } 502 503 private static class C {} 504 505 private static class D extends C { 506 @Inject D(Injector unused)507 public D(Injector unused) {} 508 } 509 } 510