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