Home | History | Annotate | Download | only in inject
      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 (at) google.com (Jesse Wilson)
     36  */
     37 @SuppressWarnings("UnusedDeclaration")
     38 public class ProvisionExceptionTest extends TestCase {
     39 
     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    */
     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 
     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 
     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    */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    278     A(B b) { }
    279   }
    280   static class B {
    281     @Inject C c;
    282   }
    283   static class C {
    284     @Inject
    285     void setD(RealD d) { }
    286   }
    287   static class E {
    288     @Inject void setObject(Object o) {
    289       throw new UnsupportedOperationException();
    290     }
    291   }
    292 
    293   static class MethodWithBindingAnnotation {
    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")
    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;
    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 {
    321     @Inject RealD() {
    322       throw new UnsupportedOperationException();
    323     }
    324   }
    325 
    326   static class DProvider implements Provider<D> {
    327     public D get() {
    328       throw new UnsupportedOperationException();
    329     }
    330   }
    331 
    332   static class F {
    333     @Inject public F() {
    334       throw new ProvisionException("User Exception", new RuntimeException());
    335     }
    336   }
    337 
    338   static class FProvider implements Provider<F> {
    339     public F get() {
    340       return new F();
    341     }
    342   }
    343 
    344   static class G {
    345     @Inject void injectFirst() {
    346       throw new IllegalArgumentException(new UnsupportedOperationException("Unsupported"));
    347     }
    348     @Inject void injectSecond() {
    349       throw new NullPointerException("can't inject second either");
    350     }
    351   }
    352 }
    353