| /** |
| * Copyright (C) 2007 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject; |
| |
| import static com.google.inject.Asserts.assertContains; |
| import static com.google.inject.Asserts.reserialize; |
| import java.io.IOException; |
| import static java.lang.annotation.ElementType.CONSTRUCTOR; |
| import static java.lang.annotation.ElementType.FIELD; |
| import static java.lang.annotation.ElementType.METHOD; |
| import static java.lang.annotation.ElementType.PARAMETER; |
| import java.lang.annotation.Retention; |
| import static java.lang.annotation.RetentionPolicy.RUNTIME; |
| import java.lang.annotation.Target; |
| import junit.framework.TestCase; |
| |
| /** |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| @SuppressWarnings("UnusedDeclaration") |
| public class ProvisionExceptionTest extends TestCase { |
| |
| public void testExceptionsCollapsed() { |
| try { |
| Guice.createInjector().getInstance(A.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertTrue(e.getCause() instanceof UnsupportedOperationException); |
| assertContains(e.getMessage(), "Error injecting constructor", |
| "for parameter 0 at com.google.inject.ProvisionExceptionTest$C.setD", |
| "for field at com.google.inject.ProvisionExceptionTest$B.c", |
| "for parameter 0 at com.google.inject.ProvisionExceptionTest$A"); |
| } |
| } |
| |
| /** |
| * There's a pass-through of user code in the scope. We want exceptions thrown by Guice to be |
| * limited to a single exception, even if it passes through user code. |
| */ |
| public void testExceptionsCollapsedWithScopes() { |
| try { |
| Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(B.class).in(Scopes.SINGLETON); |
| } |
| }).getInstance(A.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertTrue(e.getCause() instanceof UnsupportedOperationException); |
| assertFalse(e.getMessage().contains("custom provider")); |
| assertContains(e.getMessage(), "Error injecting constructor", |
| "for parameter 0 at com.google.inject.ProvisionExceptionTest$C.setD", |
| "for field at com.google.inject.ProvisionExceptionTest$B.c", |
| "for parameter 0 at com.google.inject.ProvisionExceptionTest$A"); |
| } |
| } |
| |
| public void testMethodInjectionExceptions() { |
| try { |
| Guice.createInjector().getInstance(E.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertTrue(e.getCause() instanceof UnsupportedOperationException); |
| assertContains(e.getMessage(), "Error injecting method", |
| "at " + E.class.getName() + ".setObject(ProvisionExceptionTest.java:"); |
| } |
| } |
| |
| public void testBindToProviderInstanceExceptions() { |
| try { |
| Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(D.class).toProvider(new DProvider()); |
| } |
| }).getInstance(D.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertTrue(e.getCause() instanceof UnsupportedOperationException); |
| assertContains(e.getMessage(), |
| "1) Error in custom provider, java.lang.UnsupportedOperationException", |
| "at " + ProvisionExceptionTest.class.getName(), ".configure(ProvisionExceptionTest.java"); |
| } |
| } |
| |
| /** |
| * This test demonstrates that if the user throws a ProvisionException, we wrap it to add context. |
| */ |
| public void testProvisionExceptionsAreWrappedForBindToType() { |
| try { |
| Guice.createInjector().getInstance(F.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertContains(e.getMessage(), "1) User Exception", |
| "at " + F.class.getName() + ".<init>(ProvisionExceptionTest.java:"); |
| } |
| } |
| |
| public void testProvisionExceptionsAreWrappedForBindToProviderType() { |
| try { |
| Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(F.class).toProvider(FProvider.class); |
| } |
| }).getInstance(F.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertContains(e.getMessage(), "1) User Exception", |
| "while locating ", FProvider.class.getName(), |
| "while locating ", F.class.getName()); |
| } |
| } |
| |
| public void testProvisionExceptionsAreWrappedForBindToProviderInstance() { |
| try { |
| Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(F.class).toProvider(new FProvider()); |
| } |
| }).getInstance(F.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertContains(e.getMessage(), "1) User Exception", |
| "at " + ProvisionExceptionTest.class.getName(), ".configure(ProvisionExceptionTest.java"); |
| } |
| } |
| |
| public void testProvisionExceptionIsSerializable() throws IOException { |
| try { |
| Guice.createInjector().getInstance(A.class); |
| fail(); |
| } catch (ProvisionException expected) { |
| ProvisionException reserialized = reserialize(expected); |
| assertContains(reserialized.getMessage(), |
| "1) Error injecting constructor, java.lang.UnsupportedOperationException", |
| "at com.google.inject.ProvisionExceptionTest$RealD.<init>()", |
| "at Key[type=com.google.inject.ProvisionExceptionTest$RealD, annotation=[none]]", |
| "@com.google.inject.ProvisionExceptionTest$C.setD()[0]", |
| "at Key[type=com.google.inject.ProvisionExceptionTest$C, annotation=[none]]", |
| "@com.google.inject.ProvisionExceptionTest$B.c", |
| "at Key[type=com.google.inject.ProvisionExceptionTest$B, annotation=[none]]", |
| "@com.google.inject.ProvisionExceptionTest$A.<init>()[0]", |
| "at Key[type=com.google.inject.ProvisionExceptionTest$A, annotation=[none]]"); |
| } |
| } |
| |
| public void testMultipleCauses() { |
| try { |
| Guice.createInjector().getInstance(G.class); |
| fail(); |
| } catch (ProvisionException e) { |
| assertContains(e.getMessage(), |
| "1) Error injecting method, java.lang.IllegalArgumentException", |
| "Caused by: java.lang.IllegalArgumentException: java.lang.UnsupportedOperationException", |
| "Caused by: java.lang.UnsupportedOperationException: Unsupported", |
| "2) Error injecting method, java.lang.NullPointerException: can't inject second either", |
| "Caused by: java.lang.NullPointerException: can't inject second either", |
| "2 errors"); |
| } |
| } |
| |
| public void testInjectInnerClass() throws Exception { |
| Injector injector = Guice.createInjector(); |
| try { |
| injector.getInstance(InnerClass.class); |
| fail(); |
| } catch (Exception expected) { |
| assertContains(expected.getMessage(), |
| "Injecting into inner classes is not supported.", |
| "while locating " + InnerClass.class.getName()); |
| } |
| } |
| |
| public void testInjectLocalClass() throws Exception { |
| class LocalClass {} |
| |
| Injector injector = Guice.createInjector(); |
| try { |
| injector.getInstance(LocalClass.class); |
| fail(); |
| } catch (Exception expected) { |
| assertContains(expected.getMessage(), |
| "Injecting into inner classes is not supported.", |
| "while locating " + LocalClass.class.getName()); |
| } |
| } |
| |
| public void testBindingAnnotationsOnMethodsAndConstructors() { |
| try { |
| Injector injector = Guice.createInjector(); |
| injector.getInstance(MethodWithBindingAnnotation.class); |
| fail(); |
| } catch (ConfigurationException expected) { |
| assertContains(expected.getMessage(), MethodWithBindingAnnotation.class.getName() |
| + ".injectMe() is annotated with @", Green.class.getName() + "(), ", |
| "but binding annotations should be applied to its parameters instead.", |
| "while locating " + MethodWithBindingAnnotation.class.getName()); |
| } |
| |
| try { |
| Guice.createInjector().getInstance(ConstructorWithBindingAnnotation.class); |
| fail(); |
| } catch (ConfigurationException expected) { |
| assertContains(expected.getMessage(), ConstructorWithBindingAnnotation.class.getName() |
| + ".<init>() is annotated with @", Green.class.getName() + "(), ", |
| "but binding annotations should be applied to its parameters instead.", |
| "at " + ConstructorWithBindingAnnotation.class.getName() + ".class", |
| "while locating " + ConstructorWithBindingAnnotation.class.getName()); |
| } |
| } |
| |
| public void testBindingAnnotationWarningForScala() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(String.class).annotatedWith(Green.class).toInstance("lime!"); |
| } |
| }); |
| injector.getInstance(LikeScala.class); |
| } |
| |
| public void testLinkedBindings() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(D.class).to(RealD.class); |
| } |
| }); |
| |
| try { |
| injector.getInstance(D.class); |
| fail(); |
| } catch (ProvisionException expected) { |
| assertContains(expected.getMessage(), |
| "at " + RealD.class.getName() + ".<init>(ProvisionExceptionTest.java:", |
| "while locating " + RealD.class.getName(), |
| "while locating " + D.class.getName()); |
| } |
| } |
| |
| public void testProviderKeyBindings() { |
| Injector injector = Guice.createInjector(new AbstractModule() { |
| protected void configure() { |
| bind(D.class).toProvider(DProvider.class); |
| } |
| }); |
| |
| try { |
| injector.getInstance(D.class); |
| fail(); |
| } catch (ProvisionException expected) { |
| assertContains(expected.getMessage(), |
| "while locating " + DProvider.class.getName(), |
| "while locating " + D.class.getName()); |
| } |
| } |
| |
| private class InnerClass {} |
| |
| static class A { |
| @Inject |
| A(B b) { } |
| } |
| static class B { |
| @Inject C c; |
| } |
| static class C { |
| @Inject |
| void setD(RealD d) { } |
| } |
| static class E { |
| @Inject void setObject(Object o) { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| static class MethodWithBindingAnnotation { |
| @Inject @Green void injectMe(String greenString) {} |
| } |
| |
| static class ConstructorWithBindingAnnotation { |
| @Inject @Green ConstructorWithBindingAnnotation(String greenString) {} |
| } |
| |
| /** |
| * In Scala, fields automatically get accessor methods with the same name. So we don't do |
| * misplaced-binding annotation detection if the offending method has a matching field. |
| */ |
| static class LikeScala { |
| @Inject @Green String green; |
| @Inject @Green String green() { return green; } |
| } |
| |
| @Retention(RUNTIME) |
| @Target({ FIELD, PARAMETER, CONSTRUCTOR, METHOD }) |
| @BindingAnnotation |
| @interface Green {} |
| |
| interface D {} |
| |
| static class RealD implements D { |
| @Inject RealD() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| static class DProvider implements Provider<D> { |
| public D get() { |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| static class F { |
| @Inject public F() { |
| throw new ProvisionException("User Exception", new RuntimeException()); |
| } |
| } |
| |
| static class FProvider implements Provider<F> { |
| public F get() { |
| return new F(); |
| } |
| } |
| |
| static class G { |
| @Inject void injectFirst() { |
| throw new IllegalArgumentException(new UnsupportedOperationException("Unsupported")); |
| } |
| @Inject void injectSecond() { |
| throw new NullPointerException("can't inject second either"); |
| } |
| } |
| } |