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