• 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 com.google.common.base.Throwables;
29 import com.google.inject.spi.Message;
30 import java.io.IOException;
31 import java.lang.annotation.Retention;
32 import java.lang.annotation.Target;
33 import junit.framework.TestCase;
34 
35 /** @author jessewilson@google.com (Jesse Wilson) */
36 @SuppressWarnings("UnusedDeclaration")
37 public class ProvisionExceptionTest extends TestCase {
38 
testExceptionsCollapsed()39   public void testExceptionsCollapsed() {
40     try {
41       Guice.createInjector().getInstance(A.class);
42       fail();
43     } catch (ProvisionException e) {
44       assertTrue(e.getCause() instanceof UnsupportedOperationException);
45       assertContains(
46           e.getMessage(),
47           "Error injecting constructor",
48           "for the 1st parameter of com.google.inject.ProvisionExceptionTest$C.setD",
49           "for field at com.google.inject.ProvisionExceptionTest$B.c",
50           "for the 1st parameter of com.google.inject.ProvisionExceptionTest$A");
51     }
52   }
53 
54   /**
55    * There's a pass-through of user code in the scope. We want exceptions thrown by Guice to be
56    * limited to a single exception, even if it passes through user code.
57    */
testExceptionsCollapsedWithScopes()58   public void testExceptionsCollapsedWithScopes() {
59     try {
60       Guice.createInjector(
61               new AbstractModule() {
62                 @Override
63                 protected void configure() {
64                   bind(B.class).in(Scopes.SINGLETON);
65                 }
66               })
67           .getInstance(A.class);
68       fail();
69     } catch (ProvisionException e) {
70       assertTrue(e.getCause() instanceof UnsupportedOperationException);
71       assertFalse(e.getMessage().contains("custom provider"));
72       assertContains(
73           e.getMessage(),
74           "Error injecting constructor",
75           "for the 1st parameter of com.google.inject.ProvisionExceptionTest$C.setD",
76           "for field at com.google.inject.ProvisionExceptionTest$B.c",
77           "for the 1st parameter of com.google.inject.ProvisionExceptionTest$A");
78     }
79   }
80 
testMethodInjectionExceptions()81   public void testMethodInjectionExceptions() {
82     try {
83       Guice.createInjector().getInstance(E.class);
84       fail();
85     } catch (ProvisionException e) {
86       assertTrue(e.getCause() instanceof UnsupportedOperationException);
87       assertContains(
88           e.getMessage(),
89           "Error injecting method",
90           "at " + E.class.getName() + ".setObject(ProvisionExceptionTest.java:");
91     }
92   }
93 
testBindToProviderInstanceExceptions()94   public void testBindToProviderInstanceExceptions() {
95     try {
96       Guice.createInjector(
97               new AbstractModule() {
98                 @Override
99                 protected void configure() {
100                   bind(D.class).toProvider(new DProvider());
101                 }
102               })
103           .getInstance(D.class);
104       fail();
105     } catch (ProvisionException e) {
106       assertTrue(e.getCause() instanceof UnsupportedOperationException);
107       assertContains(
108           e.getMessage(),
109           "1) Error in custom provider, java.lang.UnsupportedOperationException",
110           "at " + ProvisionExceptionTest.class.getName(),
111           getDeclaringSourcePart(getClass()));
112     }
113   }
114 
115   /**
116    * This test demonstrates that if the user throws a ProvisionException, we wrap it to add context.
117    */
testProvisionExceptionsAreWrappedForBindToType()118   public void testProvisionExceptionsAreWrappedForBindToType() {
119     try {
120       Guice.createInjector().getInstance(F.class);
121       fail();
122     } catch (ProvisionException e) {
123       assertContains(
124           e.getMessage(),
125           "1) User Exception",
126           "at " + F.class.getName() + ".<init>(ProvisionExceptionTest.java:");
127     }
128   }
129 
testProvisionExceptionsAreWrappedForBindToProviderType()130   public void testProvisionExceptionsAreWrappedForBindToProviderType() {
131     try {
132       Guice.createInjector(
133               new AbstractModule() {
134                 @Override
135                 protected void configure() {
136                   bind(F.class).toProvider(FProvider.class);
137                 }
138               })
139           .getInstance(F.class);
140       fail();
141     } catch (ProvisionException e) {
142       assertContains(
143           e.getMessage(),
144           "1) User Exception",
145           "while locating ",
146           FProvider.class.getName(),
147           "while locating ",
148           F.class.getName());
149     }
150   }
151 
testProvisionExceptionsAreWrappedForBindToProviderInstance()152   public void testProvisionExceptionsAreWrappedForBindToProviderInstance() {
153     try {
154       Guice.createInjector(
155               new AbstractModule() {
156                 @Override
157                 protected void configure() {
158                   bind(F.class).toProvider(new FProvider());
159                 }
160               })
161           .getInstance(F.class);
162       fail();
163     } catch (ProvisionException e) {
164       assertContains(
165           e.getMessage(),
166           "1) User Exception",
167           "at " + ProvisionExceptionTest.class.getName(),
168           getDeclaringSourcePart(getClass()));
169     }
170   }
171 
testProvisionExceptionIsSerializable()172   public void testProvisionExceptionIsSerializable() throws IOException {
173     try {
174       Guice.createInjector().getInstance(A.class);
175       fail();
176     } catch (ProvisionException expected) {
177       ProvisionException reserialized = reserialize(expected);
178       assertContains(
179           reserialized.getMessage(),
180           "1) Error injecting constructor, java.lang.UnsupportedOperationException",
181           "at com.google.inject.ProvisionExceptionTest$RealD.<init>()",
182           "at Key[type=com.google.inject.ProvisionExceptionTest$RealD, annotation=[none]]",
183           "@com.google.inject.ProvisionExceptionTest$C.setD()[0]",
184           "at Key[type=com.google.inject.ProvisionExceptionTest$C, annotation=[none]]",
185           "@com.google.inject.ProvisionExceptionTest$B.c",
186           "at Key[type=com.google.inject.ProvisionExceptionTest$B, annotation=[none]]",
187           "@com.google.inject.ProvisionExceptionTest$A.<init>()[0]",
188           "at Key[type=com.google.inject.ProvisionExceptionTest$A, annotation=[none]]");
189     }
190   }
191 
192   // The only way to trigger an exception with _multiple_ user controlled throwables is by
193   // triggering errors during injector creation.
testMultipleCauses()194   public void testMultipleCauses() {
195     try {
196       Guice.createInjector(
197           Stage.PRODUCTION,
198           new AbstractModule() {
199             @Provides
200             @Singleton
201             String injectFirst() {
202               throw new IllegalArgumentException(new UnsupportedOperationException("Unsupported"));
203             }
204 
205             @Provides
206             @Singleton
207             Object injectSecond() {
208               throw new NullPointerException("can't inject second either");
209             }
210           });
211       fail();
212     } catch (CreationException e) {
213       assertContains(
214           e.getMessage(),
215           "1) Error in custom provider, java.lang.IllegalArgumentException",
216           "Caused by: java.lang.IllegalArgumentException: java.lang.UnsupportedOperationException",
217           "Caused by: java.lang.UnsupportedOperationException: Unsupported",
218           "2) Error in custom provider, java.lang.NullPointerException: can't inject second either",
219           "Caused by: java.lang.NullPointerException: can't inject second either",
220           "2 errors");
221     }
222   }
223 
testInjectInnerClass()224   public void testInjectInnerClass() throws Exception {
225     Injector injector = Guice.createInjector();
226     try {
227       injector.getInstance(InnerClass.class);
228       fail();
229     } catch (Exception expected) {
230       assertContains(
231           expected.getMessage(),
232           "Injecting into inner classes is not supported.",
233           "while locating " + InnerClass.class.getName());
234     }
235   }
236 
testInjectLocalClass()237   public void testInjectLocalClass() throws Exception {
238     class LocalClass {}
239 
240     Injector injector = Guice.createInjector();
241     try {
242       injector.getInstance(LocalClass.class);
243       fail();
244     } catch (Exception expected) {
245       assertContains(
246           expected.getMessage(),
247           "Injecting into inner classes is not supported.",
248           "while locating " + LocalClass.class.getName());
249     }
250   }
251 
testBindingAnnotationsOnMethodsAndConstructors()252   public void testBindingAnnotationsOnMethodsAndConstructors() {
253     try {
254       Injector injector = Guice.createInjector();
255       injector.getInstance(MethodWithBindingAnnotation.class);
256       fail();
257     } catch (ConfigurationException expected) {
258       assertContains(
259           expected.getMessage(),
260           MethodWithBindingAnnotation.class.getName() + ".injectMe() is annotated with @",
261           Green.class.getName() + "(), ",
262           "but binding annotations should be applied to its parameters instead.",
263           "while locating " + MethodWithBindingAnnotation.class.getName());
264     }
265 
266     try {
267       Guice.createInjector().getInstance(ConstructorWithBindingAnnotation.class);
268       fail();
269     } catch (ConfigurationException expected) {
270       assertContains(
271           expected.getMessage(),
272           ConstructorWithBindingAnnotation.class.getName() + ".<init>() is annotated with @",
273           Green.class.getName() + "(), ",
274           "but binding annotations should be applied to its parameters instead.",
275           "at " + ConstructorWithBindingAnnotation.class.getName() + ".class",
276           "while locating " + ConstructorWithBindingAnnotation.class.getName());
277     }
278   }
279 
testBindingAnnotationWarningForScala()280   public void testBindingAnnotationWarningForScala() {
281     Injector injector =
282         Guice.createInjector(
283             new AbstractModule() {
284               @Override
285               protected void configure() {
286                 bind(String.class).annotatedWith(Green.class).toInstance("lime!");
287               }
288             });
289     injector.getInstance(LikeScala.class);
290   }
291 
testLinkedBindings()292   public void testLinkedBindings() {
293     Injector injector =
294         Guice.createInjector(
295             new AbstractModule() {
296               @Override
297               protected void configure() {
298                 bind(D.class).to(RealD.class);
299               }
300             });
301 
302     try {
303       injector.getInstance(D.class);
304       fail();
305     } catch (ProvisionException expected) {
306       assertContains(
307           expected.getMessage(),
308           "at " + RealD.class.getName() + ".<init>(ProvisionExceptionTest.java:",
309           "while locating " + RealD.class.getName(),
310           "while locating " + D.class.getName());
311     }
312   }
313 
testProviderKeyBindings()314   public void testProviderKeyBindings() {
315     Injector injector =
316         Guice.createInjector(
317             new AbstractModule() {
318               @Override
319               protected void configure() {
320                 bind(D.class).toProvider(DProvider.class);
321               }
322             });
323 
324     try {
325       injector.getInstance(D.class);
326       fail();
327     } catch (ProvisionException expected) {
328       assertContains(
329           expected.getMessage(),
330           "while locating " + DProvider.class.getName(),
331           "while locating " + D.class.getName());
332     }
333   }
334 
testDuplicateCausesCollapsed()335   public void testDuplicateCausesCollapsed() {
336     final RuntimeException sharedException = new RuntimeException("fail");
337     try {
338       Guice.createInjector(
339           new AbstractModule() {
340             @Override
341             protected void configure() {
342               addError(sharedException);
343               addError(sharedException);
344             }
345           });
346       fail();
347     } catch (CreationException ce) {
348       assertEquals(sharedException, ce.getCause());
349       assertEquals(2, ce.getErrorMessages().size());
350       for (Message message : ce.getErrorMessages()) {
351         assertEquals(sharedException, message.getCause());
352       }
353     }
354   }
355 
testMultipleDuplicates()356   public void testMultipleDuplicates() {
357     final RuntimeException exception1 = new RuntimeException("fail");
358     final RuntimeException exception2 = new RuntimeException("abort");
359     try {
360       Guice.createInjector(
361           new AbstractModule() {
362             @Override
363             protected void configure() {
364               addError(exception1);
365               addError(exception1);
366               addError(exception2);
367               addError(exception2);
368             }
369           });
370       fail();
371     } catch (CreationException ce) {
372       assertNull(ce.getCause());
373       assertEquals(4, ce.getErrorMessages().size());
374 
375       String e1 = Throwables.getStackTraceAsString(exception1);
376       String e2 = Throwables.getStackTraceAsString(exception2);
377       assertContains(
378           ce.getMessage(),
379           "\n1) ",
380           e1,
381           "\n2) ",
382           "(same stack trace as error #1)",
383           "\n3) ",
384           e2,
385           "\n4) ",
386           "(same stack trace as error #3)");
387     }
388   }
389 
390   @SuppressWarnings("ClassCanBeStatic")
391   private class InnerClass {}
392 
393   static class A {
394     @Inject
A(B b)395     A(B b) {}
396   }
397 
398   static class B {
399     @Inject C c;
400   }
401 
402   static class C {
403     @Inject
setD(RealD d)404     void setD(RealD d) {}
405   }
406 
407   static class E {
408     @Inject
setObject(Object o)409     void setObject(Object o) {
410       throw new UnsupportedOperationException();
411     }
412   }
413 
414   static class MethodWithBindingAnnotation {
415     @Inject
416     @Green
injectMe(String greenString)417     void injectMe(String greenString) {}
418   }
419 
420   static class ConstructorWithBindingAnnotation {
421     // Suppress compiler errors by the error-prone checker InjectedConstructorAnnotations,
422     // which catches injected constructors with binding annotations.
423     @SuppressWarnings("InjectedConstructorAnnotations")
424     @Inject
425     @Green
ConstructorWithBindingAnnotation(String greenString)426     ConstructorWithBindingAnnotation(String greenString) {}
427   }
428 
429   /**
430    * In Scala, fields automatically get accessor methods with the same name. So we don't do
431    * misplaced-binding annotation detection if the offending method has a matching field.
432    */
433   static class LikeScala {
434     @Inject @Green String green;
435 
436     @Inject
437     @Green
green()438     String green() {
439       return green;
440     }
441   }
442 
443   @Retention(RUNTIME)
444   @Target({FIELD, PARAMETER, CONSTRUCTOR, METHOD})
445   @BindingAnnotation
446   @interface Green {}
447 
448   interface D {}
449 
450   static class RealD implements D {
451     @Inject
RealD()452     RealD() {
453       throw new UnsupportedOperationException();
454     }
455   }
456 
457   static class DProvider implements Provider<D> {
458     @Override
get()459     public D get() {
460       throw new UnsupportedOperationException();
461     }
462   }
463 
464   static class F {
465     @Inject
F()466     public F() {
467       throw new ProvisionException("User Exception", new RuntimeException());
468     }
469   }
470 
471   static class FProvider implements Provider<F> {
472     @Override
get()473     public F get() {
474       return new F();
475     }
476   }
477 }
478