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