mirror of
https://github.com/google/guice.git
synced 2024-04-21 12:32:36 +00:00
In 41f307e21b this AbstractModule.configure() became non-abstract in the base class, so it is now allowed for subclasses to not override the method at all when they don’t need to access the binding dsl.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148043119
577 lines
17 KiB
Java
577 lines
17 KiB
Java
/*
|
|
* 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.getDeclaringSourcePart;
|
|
import static com.google.inject.Asserts.reserialize;
|
|
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 static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
|
|
import com.google.common.base.Throwables;
|
|
import com.google.inject.spi.Message;
|
|
import java.io.IOException;
|
|
import java.lang.annotation.Retention;
|
|
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 the 1st parameter of com.google.inject.ProvisionExceptionTest$C.setD",
|
|
"for field at com.google.inject.ProvisionExceptionTest$B.c",
|
|
"for the 1st parameter of 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() {
|
|
@Override
|
|
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 the 1st parameter of com.google.inject.ProvisionExceptionTest$C.setD",
|
|
"for field at com.google.inject.ProvisionExceptionTest$B.c",
|
|
"for the 1st parameter of 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() {
|
|
@Override
|
|
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(),
|
|
getDeclaringSourcePart(getClass()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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() {
|
|
@Override
|
|
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() {
|
|
@Override
|
|
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(),
|
|
getDeclaringSourcePart(getClass()));
|
|
}
|
|
}
|
|
|
|
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() {
|
|
@Override
|
|
protected void configure() {
|
|
bind(String.class).annotatedWith(Green.class).toInstance("lime!");
|
|
}
|
|
});
|
|
injector.getInstance(LikeScala.class);
|
|
}
|
|
|
|
public void testLinkedBindings() {
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
@Override
|
|
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() {
|
|
@Override
|
|
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());
|
|
}
|
|
}
|
|
|
|
public void testDuplicateCausesCollapsed() {
|
|
final RuntimeException sharedException = new RuntimeException("fail");
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
|
|
@Provides
|
|
Integer i() {
|
|
throw sharedException;
|
|
}
|
|
|
|
@Provides
|
|
Double d() {
|
|
throw sharedException;
|
|
}
|
|
});
|
|
|
|
try {
|
|
injector.getInstance(DoubleFailure.class);
|
|
fail();
|
|
} catch (ProvisionException pe) {
|
|
assertEquals(sharedException, pe.getCause());
|
|
assertEquals(2, pe.getErrorMessages().size());
|
|
for (Message message : pe.getErrorMessages()) {
|
|
assertEquals(sharedException, message.getCause());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void testMultipleDuplicates() {
|
|
final RuntimeException exception1 = new RuntimeException("fail");
|
|
final RuntimeException exception2 = new RuntimeException("abort");
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
|
|
@Provides
|
|
Integer i() {
|
|
throw exception1;
|
|
}
|
|
|
|
@Provides
|
|
Double d() {
|
|
throw exception1;
|
|
}
|
|
|
|
@Provides
|
|
String s() {
|
|
throw exception2;
|
|
}
|
|
|
|
@Provides
|
|
Number n() {
|
|
throw exception2;
|
|
}
|
|
});
|
|
|
|
try {
|
|
injector.getInstance(QuadrupleFailure.class);
|
|
fail();
|
|
} catch (ProvisionException pe) {
|
|
assertNull(pe.getCause());
|
|
assertEquals(4, pe.getErrorMessages().size());
|
|
|
|
String e1 = Throwables.getStackTraceAsString(exception1);
|
|
String e2 = Throwables.getStackTraceAsString(exception2);
|
|
assertContains(
|
|
pe.getMessage(),
|
|
"\n1) ",
|
|
e1,
|
|
"\n2) ",
|
|
"(same stack trace as error #1)",
|
|
"\n3) ",
|
|
e2,
|
|
"\n4) ",
|
|
"(same stack trace as error #3)");
|
|
}
|
|
}
|
|
|
|
static class DoubleFailure {
|
|
@Inject
|
|
DoubleFailure(Integer i, Double d) {}
|
|
}
|
|
|
|
static class QuadrupleFailure {
|
|
@Inject
|
|
QuadrupleFailure(Integer i, Double d, String s, Number n) {}
|
|
}
|
|
|
|
public void testDuplicatesDifferentInstances() {
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
|
|
@Provides
|
|
Integer i() {
|
|
throw new RuntimeException();
|
|
}
|
|
});
|
|
|
|
try {
|
|
injector.getInstance(DoubleSameFailure.class);
|
|
fail();
|
|
} catch (ProvisionException pe) {
|
|
assertNotNull(pe.toString(), pe.getCause());
|
|
assertEquals(2, pe.getErrorMessages().size());
|
|
for (Message message : pe.getErrorMessages()) {
|
|
assertNotNull(message.toString(), message.getCause());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void testMultipleDuplicatesDifferentInstaces() {
|
|
Injector injector =
|
|
Guice.createInjector(
|
|
new AbstractModule() {
|
|
|
|
@Provides
|
|
Integer i() {
|
|
throw new RuntimeException("fail");
|
|
}
|
|
|
|
@Provides
|
|
String s() {
|
|
throw new RuntimeException("abort");
|
|
}
|
|
});
|
|
|
|
try {
|
|
injector.getInstance(QuadrupleSameFailure.class);
|
|
fail();
|
|
} catch (ProvisionException pe) {
|
|
assertNull(pe.getCause());
|
|
assertEquals(4, pe.getErrorMessages().size());
|
|
|
|
assertContains(
|
|
pe.getMessage(),
|
|
"\n1) ",
|
|
"Caused by: java.lang.RuntimeException: fail",
|
|
"\n2) ",
|
|
"Caused by: java.lang.RuntimeException (same stack trace as error #1)",
|
|
"\n3) ",
|
|
"Caused by: java.lang.RuntimeException: abort",
|
|
"\n4) ",
|
|
"Caused by: java.lang.RuntimeException (same stack trace as error #3)");
|
|
}
|
|
}
|
|
|
|
static class DoubleSameFailure {
|
|
@Inject
|
|
DoubleSameFailure(Integer i1, Integer i2) {}
|
|
}
|
|
|
|
static class QuadrupleSameFailure {
|
|
@Inject
|
|
QuadrupleSameFailure(Integer i1, Integer i2, String s1, String s2) {}
|
|
}
|
|
|
|
@SuppressWarnings("ClassCanBeStatic")
|
|
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 {
|
|
// Suppress compiler errors by the error-prone checker InjectedConstructorAnnotations,
|
|
// which catches injected constructors with binding annotations.
|
|
@SuppressWarnings("InjectedConstructorAnnotations")
|
|
@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> {
|
|
@Override
|
|
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> {
|
|
@Override
|
|
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");
|
|
}
|
|
}
|
|
}
|