1 /**
2  * Copyright (C) 2006 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.RetentionPolicy.RUNTIME;
22 
23 import com.google.inject.matcher.Matchers;
24 import com.google.inject.spi.TypeEncounter;
25 import com.google.inject.spi.TypeListener;
26 
27 import junit.framework.TestCase;
28 
29 import java.lang.annotation.Retention;
30 
31 /**
32  * @author crazybob@google.com (Bob Lee)
33  */
34 public class RequestInjectionTest extends TestCase {
35 
36   @Retention(RUNTIME)
37   @BindingAnnotation @interface ForField {}
38 
39   @Retention(RUNTIME)
40   @BindingAnnotation @interface ForMethod {}
41 
42   @Override
setUp()43   protected void setUp() throws Exception {
44     super.setUp();
45     HasInjections.staticField = 0;
46     HasInjections.staticMethod = null;
47   }
48 
testInjectMembers()49   public void testInjectMembers() {
50     final HasInjections hi = new HasInjections();
51 
52     Guice.createInjector(new AbstractModule() {
53       @Override
54       protected void configure() {
55         bindConstant().annotatedWith(ForMethod.class).to("test");
56         bindConstant().annotatedWith(ForField.class).to(5);
57         requestInjection(hi);
58       }
59     });
60 
61     assertEquals("test", hi.instanceMethod);
62     assertEquals(5, hi.instanceField);
63     assertNull(HasInjections.staticMethod);
64     assertEquals(0, HasInjections.staticField);
65   }
66 
testInjectStatics()67   public void testInjectStatics() throws CreationException {
68     Guice.createInjector(new AbstractModule() {
69       @Override
70       protected void configure() {
71         bindConstant().annotatedWith(ForMethod.class).to("test");
72         bindConstant().annotatedWith(ForField.class).to(5);
73         requestStaticInjection(HasInjections.class);
74       }
75     });
76 
77     assertEquals("test", HasInjections.staticMethod);
78     assertEquals(5, HasInjections.staticField);
79   }
80 
testInjectMembersAndStatics()81   public void testInjectMembersAndStatics() {
82     final HasInjections hi = new HasInjections();
83 
84     Guice.createInjector(new AbstractModule() {
85       @Override
86       protected void configure() {
87         bindConstant().annotatedWith(ForMethod.class).to("test");
88         bindConstant().annotatedWith(ForField.class).to(5);
89         requestStaticInjection(HasInjections.class);
90         requestInjection(hi);
91       }
92     });
93 
94     assertEquals("test", hi.instanceMethod);
95     assertEquals(5, hi.instanceField);
96     assertEquals("test", HasInjections.staticMethod);
97     assertEquals(5, HasInjections.staticField);
98   }
99 
testValidationErrorOnInjectedMembers()100   public void testValidationErrorOnInjectedMembers() {
101     try {
102       Guice.createInjector(new AbstractModule() {
103         @Override
104         protected void configure() {
105           requestInjection(new NeedsRunnable());
106         }
107       });
108     } catch (CreationException expected) {
109       assertContains(expected.getMessage(),
110           "1) No implementation for java.lang.Runnable was bound",
111           "at " + NeedsRunnable.class.getName(), ".runnable(RequestInjectionTest.java:");
112     }
113   }
114 
testInjectionErrorOnInjectedMembers()115   public void testInjectionErrorOnInjectedMembers() {
116     try {
117       Guice.createInjector(new AbstractModule() {
118         @Override
119         protected void configure() {
120           bind(Runnable.class).toProvider(new Provider<Runnable>() {
121             public Runnable get() {
122               throw new UnsupportedOperationException();
123             }
124           });
125           requestInjection(new NeedsRunnable());
126         }
127       });
128     } catch (CreationException expected) {
129       assertContains(expected.getMessage(),
130           "1) Error in custom provider, java.lang.UnsupportedOperationException",
131           "for field at " + NeedsRunnable.class.getName() + ".runnable(RequestInjectionTest.java:",
132           "at " + getClass().getName(), getDeclaringSourcePart(getClass()));
133     }
134   }
135 
testUserExceptionWhileInjectingInstance()136   public void testUserExceptionWhileInjectingInstance() {
137     try {
138       Guice.createInjector(new AbstractModule() {
139         @Override
140         protected void configure() {
141           requestInjection(new BlowsUpOnInject());
142         }
143       });
144       fail();
145     } catch (CreationException expected) {
146       assertContains(expected.getMessage(),
147           "1) Error injecting method, java.lang.UnsupportedOperationException: Pop",
148           "at " + BlowsUpOnInject.class.getName() + ".injectInstance(RequestInjectionTest.java:");
149     }
150   }
151 
testUserExceptionWhileInjectingStatically()152   public void testUserExceptionWhileInjectingStatically() {
153     try {
154       Guice.createInjector(new AbstractModule() {
155         @Override
156         protected void configure() {
157           requestStaticInjection(BlowsUpOnInject.class);
158         }
159       });
160       fail();
161     } catch (CreationException expected) {
162       assertContains(expected.getMessage(),
163           "1) Error injecting method, java.lang.UnsupportedOperationException: Snap",
164           "at " + BlowsUpOnInject.class.getName() + ".injectStatically(RequestInjectionTest.java:");
165     }
166   }
167 
168   static class NeedsRunnable {
169     @Inject Runnable runnable;
170   }
171 
172   static class HasInjections {
173 
174     @Inject @ForField static int staticField;
175     @Inject @ForField int instanceField;
176 
177     static String staticMethod;
178     String instanceMethod;
179 
setStaticMethod(@orMethod String staticMethod)180     @Inject static void setStaticMethod(@ForMethod String staticMethod) {
181       HasInjections.staticMethod = staticMethod;
182     }
183 
setInstanceS(@orMethod String instanceS)184     @Inject void setInstanceS(@ForMethod String instanceS) {
185       this.instanceMethod = instanceS;
186     }
187   }
188 
189   static class BlowsUpOnInject {
injectInstance()190     @Inject void injectInstance() {
191       throw new UnsupportedOperationException("Pop");
192     }
193 
injectStatically()194     @Inject static void injectStatically() {
195       throw new UnsupportedOperationException("Snap");
196     }
197   }
198 
199   /*
200    * Tests that initializables of the same instance don't clobber
201    * membersInjectors in InitializableReference, so that they ultimately
202    * can be requested in any order.
203    */
testEarlyInjectableReferencesWithSameIdentity()204   public void testEarlyInjectableReferencesWithSameIdentity() {
205     Injector injector = Guice.createInjector(new AbstractModule() {
206       @Override
207       protected void configure() {
208         // Add a listener to trigger all toInstance bindings to get an Initializable.
209         bindListener(Matchers.any(), new TypeListener() {
210           @Override
211           public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
212           }
213         });
214 
215         // Bind two different Keys to the IDENTITICAL object
216         // ORDER MATTERS! We want the String binding to push out the Object one
217         String fail = new String("better not fail!");
218         bind(Object.class).toInstance(fail);
219         bind(String.class).toInstance(fail);
220 
221         // Then try to inject those objects in a requestInjection,
222         // letting us get into InjectableReference.get before it has
223         // finished running through all its injections.
224         // Each of these technically has its own InjectableReference internally.
225         // ORDER MATTERS!.. because Object is injected first, that InjectableReference
226         // attempts to process its members injector, but it wasn't initialized,
227         // because String pushed it out of the queue!
228         requestInjection(new Object() {
229           @SuppressWarnings("unused") @Inject Object obj;
230           @SuppressWarnings("unused") @Inject String str;
231         });
232       }
233     });
234   }
235 }
236