• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.google.inject.Asserts.reserialize;
22 import static java.lang.annotation.ElementType.CONSTRUCTOR;
23 import static java.lang.annotation.ElementType.FIELD;
24 import static java.lang.annotation.ElementType.METHOD;
25 import static java.lang.annotation.ElementType.PARAMETER;
26 import static java.lang.annotation.RetentionPolicy.RUNTIME;
27 
28 import junit.framework.TestCase;
29 
30 import java.io.IOException;
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.Target;
33 
34 /**
35  * @author jessewilson@google.com (Jesse Wilson)
36  */
37 @SuppressWarnings("UnusedDeclaration")
38 public class ProvisionExceptionTest extends TestCase {
39 
testExceptionsCollapsed()40   public void testExceptionsCollapsed() {
41     try {
42       Guice.createInjector().getInstance(A.class);
43       fail();
44     } catch (ProvisionException e) {
45       assertTrue(e.getCause() instanceof UnsupportedOperationException);
46       assertContains(e.getMessage(), "Error injecting constructor",
47           "for parameter 0 at com.google.inject.ProvisionExceptionTest$C.setD",
48           "for field at com.google.inject.ProvisionExceptionTest$B.c",
49           "for parameter 0 at com.google.inject.ProvisionExceptionTest$A");
50     }
51   }
52 
53   /**
54    * There's a pass-through of user code in the scope. We want exceptions thrown by Guice to be
55    * limited to a single exception, even if it passes through user code.
56    */
testExceptionsCollapsedWithScopes()57   public void testExceptionsCollapsedWithScopes() {
58     try {
59       Guice.createInjector(new AbstractModule() {
60         protected void configure() {
61           bind(B.class).in(Scopes.SINGLETON);
62         }
63       }).getInstance(A.class);
64       fail();
65     } catch (ProvisionException e) {
66       assertTrue(e.getCause() instanceof UnsupportedOperationException);
67       assertFalse(e.getMessage().contains("custom provider"));
68       assertContains(e.getMessage(), "Error injecting constructor",
69           "for parameter 0 at com.google.inject.ProvisionExceptionTest$C.setD",
70           "for field at com.google.inject.ProvisionExceptionTest$B.c",
71           "for parameter 0 at com.google.inject.ProvisionExceptionTest$A");
72     }
73   }
74 
testMethodInjectionExceptions()75   public void testMethodInjectionExceptions() {
76     try {
77       Guice.createInjector().getInstance(E.class);
78       fail();
79     } catch (ProvisionException e) {
80       assertTrue(e.getCause() instanceof UnsupportedOperationException);
81       assertContains(e.getMessage(), "Error injecting method",
82           "at " + E.class.getName() + ".setObject(ProvisionExceptionTest.java:");
83     }
84   }
85 
testBindToProviderInstanceExceptions()86   public void testBindToProviderInstanceExceptions() {
87     try {
88       Guice.createInjector(new AbstractModule() {
89         protected void configure() {
90           bind(D.class).toProvider(new DProvider());
91         }
92       }).getInstance(D.class);
93       fail();
94     } catch (ProvisionException e) {
95       assertTrue(e.getCause() instanceof UnsupportedOperationException);
96       assertContains(e.getMessage(),
97           "1) Error in custom provider, java.lang.UnsupportedOperationException",
98           "at " + ProvisionExceptionTest.class.getName(),
99           getDeclaringSourcePart(getClass()));
100     }
101   }
102 
103   /**
104    * This test demonstrates that if the user throws a ProvisionException, we wrap it to add context.
105    */
testProvisionExceptionsAreWrappedForBindToType()106   public void testProvisionExceptionsAreWrappedForBindToType() {
107     try {
108       Guice.createInjector().getInstance(F.class);
109       fail();
110     } catch (ProvisionException e) {
111       assertContains(e.getMessage(), "1) User Exception",
112           "at " + F.class.getName() + ".<init>(ProvisionExceptionTest.java:");
113     }
114   }
115 
testProvisionExceptionsAreWrappedForBindToProviderType()116   public void testProvisionExceptionsAreWrappedForBindToProviderType() {
117     try {
118       Guice.createInjector(new AbstractModule() {
119         protected void configure() {
120           bind(F.class).toProvider(FProvider.class);
121         }
122       }).getInstance(F.class);
123       fail();
124     } catch (ProvisionException e) {
125       assertContains(e.getMessage(), "1) User Exception",
126           "while locating ", FProvider.class.getName(),
127           "while locating ", F.class.getName());
128     }
129   }
130 
testProvisionExceptionsAreWrappedForBindToProviderInstance()131   public void testProvisionExceptionsAreWrappedForBindToProviderInstance() {
132     try {
133       Guice.createInjector(new AbstractModule() {
134         protected void configure() {
135           bind(F.class).toProvider(new FProvider());
136         }
137       }).getInstance(F.class);
138       fail();
139     } catch (ProvisionException e) {
140       assertContains(e.getMessage(), "1) User Exception",
141           "at " + ProvisionExceptionTest.class.getName(),
142           getDeclaringSourcePart(getClass()));
143     }
144   }
145 
testProvisionExceptionIsSerializable()146   public void testProvisionExceptionIsSerializable() throws IOException {
147     try {
148       Guice.createInjector().getInstance(A.class);
149       fail();
150     } catch (ProvisionException expected) {
151       ProvisionException reserialized = reserialize(expected);
152       assertContains(reserialized.getMessage(),
153           "1) Error injecting constructor, java.lang.UnsupportedOperationException",
154               "at com.google.inject.ProvisionExceptionTest$RealD.<init>()",
155               "at Key[type=com.google.inject.ProvisionExceptionTest$RealD, annotation=[none]]",
156               "@com.google.inject.ProvisionExceptionTest$C.setD()[0]",
157               "at Key[type=com.google.inject.ProvisionExceptionTest$C, annotation=[none]]",
158               "@com.google.inject.ProvisionExceptionTest$B.c",
159               "at Key[type=com.google.inject.ProvisionExceptionTest$B, annotation=[none]]",
160               "@com.google.inject.ProvisionExceptionTest$A.<init>()[0]",
161               "at Key[type=com.google.inject.ProvisionExceptionTest$A, annotation=[none]]");
162     }
163   }
164 
testMultipleCauses()165   public void testMultipleCauses() {
166     try {
167       Guice.createInjector().getInstance(G.class);
168       fail();
169     } catch (ProvisionException e) {
170       assertContains(e.getMessage(),
171           "1) Error injecting method, java.lang.IllegalArgumentException",
172           "Caused by: java.lang.IllegalArgumentException: java.lang.UnsupportedOperationException",
173           "Caused by: java.lang.UnsupportedOperationException: Unsupported",
174           "2) Error injecting method, java.lang.NullPointerException: can't inject second either",
175           "Caused by: java.lang.NullPointerException: can't inject second either",
176           "2 errors");
177     }
178   }
179 
testInjectInnerClass()180   public void testInjectInnerClass() throws Exception {
181     Injector injector = Guice.createInjector();
182     try {
183       injector.getInstance(InnerClass.class);
184       fail();
185     } catch (Exception expected) {
186       assertContains(expected.getMessage(),
187           "Injecting into inner classes is not supported.",
188           "while locating " + InnerClass.class.getName());
189     }
190   }
191 
testInjectLocalClass()192   public void testInjectLocalClass() throws Exception {
193     class LocalClass {}
194 
195     Injector injector = Guice.createInjector();
196     try {
197       injector.getInstance(LocalClass.class);
198       fail();
199     } catch (Exception expected) {
200       assertContains(expected.getMessage(),
201           "Injecting into inner classes is not supported.",
202           "while locating " + LocalClass.class.getName());
203     }
204   }
205 
testBindingAnnotationsOnMethodsAndConstructors()206   public void testBindingAnnotationsOnMethodsAndConstructors() {
207     try {
208       Injector injector = Guice.createInjector();
209       injector.getInstance(MethodWithBindingAnnotation.class);
210       fail();
211     } catch (ConfigurationException expected) {
212       assertContains(expected.getMessage(), MethodWithBindingAnnotation.class.getName()
213           + ".injectMe() is annotated with @", Green.class.getName() + "(), ",
214           "but binding annotations should be applied to its parameters instead.",
215           "while locating " + MethodWithBindingAnnotation.class.getName());
216     }
217 
218     try {
219       Guice.createInjector().getInstance(ConstructorWithBindingAnnotation.class);
220       fail();
221     } catch (ConfigurationException expected) {
222       assertContains(expected.getMessage(), ConstructorWithBindingAnnotation.class.getName()
223           + ".<init>() is annotated with @", Green.class.getName() + "(), ",
224           "but binding annotations should be applied to its parameters instead.",
225           "at " + ConstructorWithBindingAnnotation.class.getName() + ".class",
226           "while locating " + ConstructorWithBindingAnnotation.class.getName());
227     }
228   }
229 
testBindingAnnotationWarningForScala()230   public void testBindingAnnotationWarningForScala() {
231     Injector injector = Guice.createInjector(new AbstractModule() {
232       protected void configure() {
233         bind(String.class).annotatedWith(Green.class).toInstance("lime!");
234       }
235     });
236     injector.getInstance(LikeScala.class);
237   }
238 
testLinkedBindings()239   public void testLinkedBindings() {
240     Injector injector = Guice.createInjector(new AbstractModule() {
241       protected void configure() {
242         bind(D.class).to(RealD.class);
243       }
244     });
245 
246     try {
247       injector.getInstance(D.class);
248       fail();
249     } catch (ProvisionException expected) {
250       assertContains(expected.getMessage(),
251           "at " + RealD.class.getName() + ".<init>(ProvisionExceptionTest.java:",
252           "while locating " + RealD.class.getName(),
253           "while locating " + D.class.getName());
254     }
255   }
256 
testProviderKeyBindings()257   public void testProviderKeyBindings() {
258     Injector injector = Guice.createInjector(new AbstractModule() {
259       protected void configure() {
260         bind(D.class).toProvider(DProvider.class);
261       }
262     });
263 
264     try {
265       injector.getInstance(D.class);
266       fail();
267     } catch (ProvisionException expected) {
268       assertContains(expected.getMessage(),
269           "while locating " + DProvider.class.getName(),
270           "while locating " + D.class.getName());
271     }
272   }
273 
274   private class InnerClass {}
275 
276   static class A {
277     @Inject
A(B b)278     A(B b) { }
279   }
280   static class B {
281     @Inject C c;
282   }
283   static class C {
284     @Inject
setD(RealD d)285     void setD(RealD d) { }
286   }
287   static class E {
setObject(Object o)288     @Inject void setObject(Object o) {
289       throw new UnsupportedOperationException();
290     }
291   }
292 
293   static class MethodWithBindingAnnotation {
injectMe(String greenString)294     @Inject @Green void injectMe(String greenString) {}
295   }
296 
297   static class ConstructorWithBindingAnnotation {
298     // Suppress compiler errors by the error-prone checker InjectedConstructorAnnotations,
299     // which catches injected constructors with binding annotations.
300     @SuppressWarnings("InjectedConstructorAnnotations")
ConstructorWithBindingAnnotation(String greenString)301     @Inject @Green ConstructorWithBindingAnnotation(String greenString) {}
302   }
303 
304   /**
305    * In Scala, fields automatically get accessor methods with the same name. So we don't do
306    * misplaced-binding annotation detection if the offending method has a matching field.
307    */
308   static class LikeScala {
309     @Inject @Green String green;
green()310     @Inject @Green String green() { return green; }
311   }
312 
313   @Retention(RUNTIME)
314   @Target({ FIELD, PARAMETER, CONSTRUCTOR, METHOD })
315   @BindingAnnotation
316   @interface Green {}
317 
318   interface D {}
319 
320   static class RealD implements D {
RealD()321     @Inject RealD() {
322       throw new UnsupportedOperationException();
323     }
324   }
325 
326   static class DProvider implements Provider<D> {
get()327     public D get() {
328       throw new UnsupportedOperationException();
329     }
330   }
331 
332   static class F {
F()333     @Inject public F() {
334       throw new ProvisionException("User Exception", new RuntimeException());
335     }
336   }
337 
338   static class FProvider implements Provider<F> {
get()339     public F get() {
340       return new F();
341     }
342   }
343 
344   static class G {
injectFirst()345     @Inject void injectFirst() {
346       throw new IllegalArgumentException(new UnsupportedOperationException("Unsupported"));
347     }
injectSecond()348     @Inject void injectSecond() {
349       throw new NullPointerException("can't inject second either");
350     }
351   }
352 }
353