1 /* 2 Copyright (C) 2007 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; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static com.google.inject.Asserts.getDeclaringSourcePart; 21 import static java.lang.annotation.ElementType.TYPE; 22 import static java.lang.annotation.RetentionPolicy.RUNTIME; 23 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.Iterables; 26 import com.google.inject.matcher.Matchers; 27 import com.google.inject.name.Names; 28 import com.google.inject.spi.TypeConverter; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.Target; 31 import java.util.List; 32 import junit.framework.TestCase; 33 34 /** @author [email protected] (Jesse Wilson) */ 35 public class ParentInjectorTest extends TestCase { 36 testParentAndChildCannotShareExplicitBindings()37 public void testParentAndChildCannotShareExplicitBindings() { 38 Injector parent = Guice.createInjector(bindsA); 39 try { 40 parent.createChildInjector(bindsA); 41 fail("Created the same explicit binding on both parent and child"); 42 } catch (CreationException e) { 43 assertContains( 44 e.getMessage(), 45 "A binding to ", 46 A.class.getName(), 47 " was already configured", 48 " at ", 49 getClass().getName(), 50 getDeclaringSourcePart(getClass()), 51 " at ", 52 getClass().getName(), 53 getDeclaringSourcePart(getClass())); 54 } 55 } 56 testParentJitBindingWontClobberChildBinding()57 public void testParentJitBindingWontClobberChildBinding() { 58 Injector parent = Guice.createInjector(); 59 parent.createChildInjector(bindsA); 60 try { 61 parent.getInstance(A.class); 62 fail("Created a just-in-time binding on the parent that's the same as a child's binding"); 63 } catch (ConfigurationException e) { 64 assertContains( 65 e.getMessage(), 66 "Unable to create binding for " + A.class.getName(), 67 "It was already configured on one or more child injectors or private modules", 68 "bound at " + bindsA.getClass().getName() + ".configure(", 69 "If it was in a PrivateModule, did you forget to expose the binding?", 70 "while locating " + A.class.getName()); 71 } 72 } 73 testChildCannotBindToAParentJitBinding()74 public void testChildCannotBindToAParentJitBinding() { 75 Injector parent = Guice.createInjector(); 76 parent.getInstance(A.class); 77 try { 78 parent.createChildInjector(bindsA); 79 fail(); 80 } catch (CreationException ce) { 81 assertContains( 82 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage(), 83 "A just-in-time binding to " 84 + A.class.getName() 85 + " was already configured on a parent injector."); 86 } 87 } 88 testJustInTimeBindingsAreSharedWithParentIfPossible()89 public void testJustInTimeBindingsAreSharedWithParentIfPossible() { 90 Injector parent = Guice.createInjector(); 91 Injector child = parent.createChildInjector(); 92 assertSame(child.getInstance(A.class), parent.getInstance(A.class)); 93 94 Injector anotherChild = parent.createChildInjector(); 95 assertSame(anotherChild.getInstance(A.class), parent.getInstance(A.class)); 96 97 Injector grandchild = child.createChildInjector(); 98 assertSame(grandchild.getInstance(A.class), parent.getInstance(A.class)); 99 } 100 testBindingsInherited()101 public void testBindingsInherited() { 102 Injector parent = Guice.createInjector(bindsB); 103 Injector child = parent.createChildInjector(); 104 assertSame(RealB.class, child.getInstance(B.class).getClass()); 105 } 106 testGetParent()107 public void testGetParent() { 108 Injector top = Guice.createInjector(bindsA); 109 Injector middle = top.createChildInjector(bindsB); 110 Injector bottom = middle.createChildInjector(); 111 assertSame(middle, bottom.getParent()); 112 assertSame(top, middle.getParent()); 113 assertNull(top.getParent()); 114 } 115 testChildBindingsNotVisibleToParent()116 public void testChildBindingsNotVisibleToParent() { 117 Injector parent = Guice.createInjector(); 118 parent.createChildInjector(bindsB); 119 try { 120 parent.getBinding(B.class); 121 fail(); 122 } catch (ConfigurationException expected) { 123 } 124 } 125 testScopesInherited()126 public void testScopesInherited() { 127 Injector parent = 128 Guice.createInjector( 129 new AbstractModule() { 130 @Override 131 protected void configure() { 132 bindScope(MyScope.class, Scopes.SINGLETON); 133 } 134 }); 135 Injector child = 136 parent.createChildInjector( 137 new AbstractModule() { 138 @Override 139 protected void configure() { 140 bind(A.class).in(MyScope.class); 141 } 142 }); 143 assertSame(child.getInstance(A.class), child.getInstance(A.class)); 144 } 145 146 /*if[AOP]*/ 147 private final org.aopalliance.intercept.MethodInterceptor returnNullInterceptor = 148 new org.aopalliance.intercept.MethodInterceptor() { 149 @Override 150 public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) { 151 return null; 152 } 153 }; 154 testInterceptorsInherited()155 public void testInterceptorsInherited() { 156 Injector parent = 157 Guice.createInjector( 158 new AbstractModule() { 159 @Override 160 protected void configure() { 161 super.bindInterceptor( 162 Matchers.any(), 163 Matchers.returns(Matchers.identicalTo(A.class)), 164 returnNullInterceptor); 165 } 166 }); 167 168 Injector child = 169 parent.createChildInjector( 170 new AbstractModule() { 171 @Override 172 protected void configure() { 173 bind(C.class); 174 } 175 }); 176 177 assertNull(child.getInstance(C.class).interceptedMethod()); 178 } 179 /*end[AOP]*/ 180 testTypeConvertersInherited()181 public void testTypeConvertersInherited() { 182 Injector parent = Guice.createInjector(bindListConverterModule); 183 Injector child = parent.createChildInjector(bindStringNamedB); 184 185 assertEquals(ImmutableList.of(), child.getInstance(Key.get(List.class, Names.named("B")))); 186 } 187 testTypeConvertersConflicting()188 public void testTypeConvertersConflicting() { 189 Injector parent = Guice.createInjector(bindListConverterModule); 190 Injector child = parent.createChildInjector(bindListConverterModule, bindStringNamedB); 191 192 try { 193 child.getInstance(Key.get(List.class, Names.named("B"))); 194 fail(); 195 } catch (ConfigurationException expected) { 196 Asserts.assertContains(expected.getMessage(), "Multiple converters can convert"); 197 } 198 } 199 testInjectorInjectionSpanningInjectors()200 public void testInjectorInjectionSpanningInjectors() { 201 Injector parent = Guice.createInjector(); 202 Injector child = 203 parent.createChildInjector( 204 new AbstractModule() { 205 @Override 206 protected void configure() { 207 bind(D.class); 208 } 209 }); 210 211 D d = child.getInstance(D.class); 212 assertSame(d.injector, child); 213 214 E e = child.getInstance(E.class); 215 assertSame(e.injector, parent); 216 } 217 testSeveralLayersOfHierarchy()218 public void testSeveralLayersOfHierarchy() { 219 Injector top = Guice.createInjector(bindsA); 220 Injector left = top.createChildInjector(); 221 Injector leftLeft = left.createChildInjector(bindsD); 222 Injector right = top.createChildInjector(bindsD); 223 224 assertSame(leftLeft, leftLeft.getInstance(D.class).injector); 225 assertSame(right, right.getInstance(D.class).injector); 226 assertSame(top, leftLeft.getInstance(E.class).injector); 227 assertSame(top.getInstance(A.class), leftLeft.getInstance(A.class)); 228 229 Injector leftRight = left.createChildInjector(bindsD); 230 assertSame(leftRight, leftRight.getInstance(D.class).injector); 231 232 try { 233 top.getInstance(D.class); 234 fail(); 235 } catch (ConfigurationException expected) { 236 } 237 238 try { 239 left.getInstance(D.class); 240 fail(); 241 } catch (ConfigurationException expected) { 242 } 243 } 244 testScopeBoundInChildInjectorOnly()245 public void testScopeBoundInChildInjectorOnly() { 246 Injector parent = Guice.createInjector(); 247 Injector child = 248 parent.createChildInjector( 249 new AbstractModule() { 250 @Override 251 protected void configure() { 252 bindScope(MyScope.class, Scopes.SINGLETON); 253 } 254 }); 255 256 try { 257 parent.getProvider(F.class); 258 fail(); 259 } catch (ConfigurationException expected) { 260 assertContains( 261 expected.getMessage(), 262 "No scope is bound to com.google.inject.ParentInjectorTest$MyScope.", 263 "at " + F.class.getName() + ".class(ParentInjectorTest.java", 264 " while locating " + F.class.getName()); 265 } 266 267 assertNotNull(child.getProvider(F.class).get()); 268 } 269 testErrorInParentButOkayInChild()270 public void testErrorInParentButOkayInChild() { 271 Injector parent = Guice.createInjector(); 272 Injector childInjector = 273 parent.createChildInjector( 274 new AbstractModule() { 275 @Override 276 protected void configure() { 277 bindScope(MyScope.class, Scopes.SINGLETON); 278 bind(Object.class).to(F.class); 279 } 280 }); 281 Object one = childInjector.getInstance(Object.class); 282 Object two = childInjector.getInstance(Object.class); 283 assertSame(one, two); 284 } 285 testErrorInParentAndChild()286 public void testErrorInParentAndChild() { 287 Injector parent = Guice.createInjector(); 288 Injector childInjector = parent.createChildInjector(); 289 290 try { 291 childInjector.getInstance(G.class); 292 fail(); 293 } catch (ConfigurationException expected) { 294 assertContains( 295 expected.getMessage(), 296 "No scope is bound to " + MyScope.class.getName(), 297 "at " + F.class.getName() + ".class(ParentInjectorTest.java:", 298 " while locating " + G.class.getName()); 299 } 300 } 301 302 @Singleton 303 static class A {} 304 305 private final Module bindsA = 306 new AbstractModule() { 307 @Override 308 protected void configure() { 309 bind(A.class).toInstance(new A()); 310 } 311 }; 312 313 interface B {} 314 315 static class RealB implements B {} 316 317 private final Module bindsB = 318 new AbstractModule() { 319 @Override 320 protected void configure() { 321 bind(B.class).to(RealB.class); 322 } 323 }; 324 325 @Target(TYPE) 326 @Retention(RUNTIME) 327 @ScopeAnnotation 328 public @interface MyScope {} 329 330 private final TypeConverter listConverter = 331 new TypeConverter() { 332 @Override 333 public Object convert(String value, TypeLiteral<?> toType) { 334 return ImmutableList.of(); 335 } 336 }; 337 338 private final Module bindListConverterModule = 339 new AbstractModule() { 340 @Override 341 protected void configure() { 342 convertToTypes(Matchers.any(), listConverter); 343 } 344 }; 345 346 private final Module bindStringNamedB = 347 new AbstractModule() { 348 @Override 349 protected void configure() { 350 bind(String.class).annotatedWith(Names.named("B")).toInstance("buzz"); 351 } 352 }; 353 354 public static class C { interceptedMethod()355 public A interceptedMethod() { 356 return new A(); 357 } 358 } 359 360 static class D { 361 @Inject Injector injector; 362 } 363 364 static class E { 365 @Inject Injector injector; 366 } 367 368 private final Module bindsD = 369 new AbstractModule() { 370 @Override 371 protected void configure() { 372 bind(D.class); 373 } 374 }; 375 376 @MyScope 377 static class F implements G {} 378 379 @ImplementedBy(F.class) 380 interface G {} 381 } 382