blob: aa1777df9ab3d0c3723e7a34619403d22efc5616 [file] [log] [blame]
/**
* Copyright (C) 2008 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.spi;
import com.google.inject.AbstractModule;
import static com.google.inject.Asserts.assertContains;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.ConstantBindingBuilder;
import com.google.inject.binder.ScopedBindingBuilder;
import com.google.inject.internal.util.ImmutableMap;
import com.google.inject.internal.util.ImmutableSet;
import static com.google.inject.internal.util.Iterables.getOnlyElement;
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import com.google.inject.util.Providers;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import junit.framework.TestCase;
/**
* @author jessewilson@google.com (Jesse Wilson)
*/
public class ElementsTest extends TestCase {
// Binder fidelity tests
public void testAddMessageErrorCommand() {
checkModule(
new AbstractModule() {
protected void configure() {
addError("Message %s %d %s", "A", 5, "C");
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("Message A 5 C", command.getMessage());
assertNull(command.getCause());
assertContains(command.getSources().toString(),
ElementsTest.class.getName(), ".configure(ElementsTest.java:");
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testAddThrowableErrorCommand() {
checkModule(
new AbstractModule() {
protected void configure() {
addError(new Exception("A"));
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("A", command.getCause().getMessage());
assertEquals(command.getMessage(),
"An exception was caught and reported. Message: A");
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testErrorsAddedWhenExceptionsAreThrown() {
checkModule(
new AbstractModule() {
protected void configure() {
install(new AbstractModule() {
protected void configure() {
throw new RuntimeException("Throwing RuntimeException in AbstractModule.configure().");
}
});
addError("Code after the exception still gets executed");
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("Throwing RuntimeException in AbstractModule.configure().",
command.getCause().getMessage());
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("Code after the exception still gets executed",
command.getMessage());
return null;
}
}
);
}
private <T> T getInstance(Binding<T> binding) {
return binding.acceptTargetVisitor(Elements.<T>getInstanceVisitor());
}
public void testBindConstantAnnotations() {
checkModule(
new AbstractModule() {
protected void configure() {
bindConstant().annotatedWith(SampleAnnotation.class).to("A");
bindConstant().annotatedWith(Names.named("Bee")).to("B");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
assertEquals("A", getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(String.class, Names.named("Bee")), command.getKey());
assertEquals("B", getInstance(command));
return null;
}
}
);
}
public void testBindConstantTypes() {
checkModule(
new AbstractModule() {
protected void configure() {
bindConstant().annotatedWith(Names.named("String")).to("A");
bindConstant().annotatedWith(Names.named("int")).to(2);
bindConstant().annotatedWith(Names.named("long")).to(3L);
bindConstant().annotatedWith(Names.named("boolean")).to(false);
bindConstant().annotatedWith(Names.named("double")).to(5.0d);
bindConstant().annotatedWith(Names.named("float")).to(6.0f);
bindConstant().annotatedWith(Names.named("short")).to((short) 7);
bindConstant().annotatedWith(Names.named("char")).to('h');
bindConstant().annotatedWith(Names.named("Class")).to(Iterator.class);
bindConstant().annotatedWith(Names.named("Enum")).to(CoinSide.TAILS);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(String.class, Names.named("String")), command.getKey());
assertEquals("A", getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Integer.class, Names.named("int")), command.getKey());
assertEquals(2, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Long.class, Names.named("long")), command.getKey());
assertEquals(3L, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Boolean.class, Names.named("boolean")), command.getKey());
assertEquals(false, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Double.class, Names.named("double")), command.getKey());
assertEquals(5.0d, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Float.class, Names.named("float")), command.getKey());
assertEquals(6.0f, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Short.class, Names.named("short")), command.getKey());
assertEquals((short) 7, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Character.class, Names.named("char")), command.getKey());
assertEquals('h', getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(Class.class, Names.named("Class")), command.getKey());
assertEquals(Iterator.class, getInstance(command));
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(CoinSide.class, Names.named("Enum")), command.getKey());
assertEquals(CoinSide.TAILS, getInstance(command));
return null;
}
}
);
}
public void testBindKeysNoAnnotations() {
FailingElementVisitor keyChecker = new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class), command.getKey());
return null;
}
};
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class).toInstance("A");
bind(new TypeLiteral<String>() {}).toInstance("B");
bind(Key.get(String.class)).toInstance("C");
}
},
keyChecker,
keyChecker,
keyChecker
);
}
public void testBindKeysWithAnnotationType() {
FailingElementVisitor annotationChecker = new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
return null;
}
};
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("A");
bind(new TypeLiteral<String>() {}).annotatedWith(SampleAnnotation.class).toInstance("B");
}
},
annotationChecker,
annotationChecker
);
}
public void testBindKeysWithAnnotationInstance() {
FailingElementVisitor annotationChecker = new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class, Names.named("a")), command.getKey());
return null;
}
};
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class).annotatedWith(Names.named("a")).toInstance("B");
bind(new TypeLiteral<String>() {}).annotatedWith(Names.named("a")).toInstance("C");
}
},
annotationChecker,
annotationChecker
);
}
public void testBindToProvider() {
final Provider<String> aProvider = new Provider<String>() {
public String get() {
return "A";
}
};
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class).toProvider(aProvider);
bind(List.class).toProvider(ListProvider.class);
bind(Collection.class).toProvider(Key.get(ListProvider.class));
bind(Iterable.class).toProvider(new TypeLiteral<TProvider<List>>() {});
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof ProviderInstanceBinding);
assertEquals(Key.get(String.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(
ProviderInstanceBinding<? extends T> binding) {
assertSame(aProvider, binding.getProviderInstance());
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof ProviderKeyBinding);
assertEquals(Key.get(List.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ProviderKeyBinding<? extends T> binding) {
assertEquals(Key.get(ListProvider.class), binding.getProviderKey());
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof ProviderKeyBinding);
assertEquals(Key.get(Collection.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ProviderKeyBinding<? extends T> binding) {
assertEquals(Key.get(ListProvider.class), binding.getProviderKey());
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof ProviderKeyBinding);
assertEquals(Key.get(Iterable.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ProviderKeyBinding<? extends T> binding) {
assertEquals(new Key<TProvider<List>>() {}, binding.getProviderKey());
return null;
}
});
return null;
}
}
);
}
public void testBindToLinkedBinding() {
checkModule(
new AbstractModule() {
protected void configure() {
bind(List.class).to(ArrayList.class);
bind(Map.class).to(new TypeLiteral<HashMap<Integer, String>>() {});
bind(Set.class).to(Key.get(TreeSet.class, SampleAnnotation.class));
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof LinkedKeyBinding);
assertEquals(Key.get(List.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(LinkedKeyBinding<? extends T> binding) {
assertEquals(Key.get(ArrayList.class), binding.getLinkedKey());
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof LinkedKeyBinding);
assertEquals(Key.get(Map.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(LinkedKeyBinding<? extends T> binding) {
assertEquals(Key.get(new TypeLiteral<HashMap<Integer, String>>() {}),
binding.getLinkedKey());
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof LinkedKeyBinding);
assertEquals(Key.get(Set.class), command.getKey());
command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(LinkedKeyBinding<? extends T> binding) {
assertEquals(Key.get(TreeSet.class, SampleAnnotation.class),
binding.getLinkedKey());
return null;
}
});
return null;
}
}
);
}
public void testBindToInstance() {
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class).toInstance("A");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertTrue(command instanceof InstanceBinding);
assertEquals(Key.get(String.class), command.getKey());
assertEquals("A", getInstance(command));
return null;
}
}
);
}
public void testBindInScopes() {
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class);
bind(List.class).to(ArrayList.class).in(Scopes.SINGLETON);
bind(Map.class).to(HashMap.class).in(Singleton.class);
bind(Set.class).to(TreeSet.class).asEagerSingleton();
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitNoScoping() {
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(List.class), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitScope(Scope scope) {
assertEquals(Scopes.SINGLETON, scope);
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(Map.class), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
assertEquals(Singleton.class, annotation);
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(Set.class), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
public Void visitEagerSingleton() {
return null;
}
});
return null;
}
}
);
}
public void testBindToInstanceInScope() {
checkModule(
new AbstractModule() {
protected void configure() {
AnnotatedBindingBuilder<String> b = bind(String.class);
b.toInstance("A");
b.in(Singleton.class);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("Setting the scope is not permitted when binding to a single instance.",
command.getMessage());
assertNull(command.getCause());
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testBindToInstanceScope() {
checkModule(
new AbstractModule() {
protected void configure() {
bind(String.class).toInstance("A");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals(Key.get(String.class), binding.getKey());
binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
public Void visitEagerSingleton() {
return null;
}
});
return null;
}
}
);
}
/*if[AOP]*/
public void testBindIntercepor() {
final Matcher<Class> classMatcher = Matchers.subclassesOf(List.class);
final Matcher<Object> methodMatcher = Matchers.any();
final org.aopalliance.intercept.MethodInterceptor methodInterceptor
= new org.aopalliance.intercept.MethodInterceptor() {
public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) {
return null;
}
};
checkModule(
new AbstractModule() {
protected void configure() {
bindInterceptor(classMatcher, methodMatcher, methodInterceptor);
}
},
new FailingElementVisitor() {
@Override public Void visit(InterceptorBinding command) {
assertSame(classMatcher, command.getClassMatcher());
assertSame(methodMatcher, command.getMethodMatcher());
assertEquals(Arrays.asList(methodInterceptor), command.getInterceptors());
return null;
}
}
);
}
/*end[AOP]*/
public void testBindScope() {
checkModule(
new AbstractModule() {
protected void configure() {
bindScope(SampleAnnotation.class, Scopes.NO_SCOPE);
}
},
new FailingElementVisitor() {
@Override public Void visit(ScopeBinding command) {
assertSame(SampleAnnotation.class, command.getAnnotationType());
assertSame(Scopes.NO_SCOPE, command.getScope());
return null;
}
}
);
}
public void testBindListener() {
final Matcher<Object> typeMatcher = Matchers.only(TypeLiteral.get(String.class));
final TypeListener listener = new TypeListener() {
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
throw new UnsupportedOperationException();
}
};
checkModule(
new AbstractModule() {
protected void configure() {
bindListener(typeMatcher, listener);
}
},
new FailingElementVisitor() {
@Override public Void visit(TypeListenerBinding binding) {
assertSame(typeMatcher, binding.getTypeMatcher());
assertSame(listener, binding.getListener());
return null;
}
}
);
}
public void testConvertToTypes() {
final TypeConverter typeConverter = new TypeConverter() {
public Object convert(String value, TypeLiteral<?> toType) {
return value;
}
};
checkModule(
new AbstractModule() {
protected void configure() {
convertToTypes(Matchers.any(), typeConverter);
}
},
new FailingElementVisitor() {
@Override public Void visit(TypeConverterBinding command) {
assertSame(typeConverter, command.getTypeConverter());
assertSame(Matchers.any(), command.getTypeMatcher());
return null;
}
}
);
}
public void testGetProvider() {
checkModule(
new AbstractModule() {
protected void configure() {
Provider<String> keyGetProvider
= getProvider(Key.get(String.class, SampleAnnotation.class));
try {
keyGetProvider.get();
} catch (IllegalStateException e) {
assertEquals("This Provider cannot be used until the Injector has been created.",
e.getMessage());
}
Provider<String> typeGetProvider = getProvider(String.class);
try {
typeGetProvider.get();
} catch (IllegalStateException e) {
assertEquals("This Provider cannot be used until the Injector has been created.",
e.getMessage());
}
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(ProviderLookup<T> command) {
assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
assertNull(command.getDelegate());
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(ProviderLookup<T> command) {
assertEquals(Key.get(String.class), command.getKey());
assertNull(command.getDelegate());
return null;
}
}
);
}
public void testElementInitialization() {
final AtomicReference<Provider<String>> providerFromBinder
= new AtomicReference<Provider<String>>();
final AtomicReference<MembersInjector<String>> membersInjectorFromBinder
= new AtomicReference<MembersInjector<String>>();
final AtomicReference<String> lastInjected = new AtomicReference<String>();
final MembersInjector<String> stringInjector = new MembersInjector<String>() {
public void injectMembers(String instance) {
lastInjected.set(instance);
}
};
checkModule(
new AbstractModule() {
protected void configure() {
providerFromBinder.set(getProvider(String.class));
membersInjectorFromBinder.set(getMembersInjector(String.class));
}
},
new FailingElementVisitor() {
public <T> Void visit(ProviderLookup<T> providerLookup) {
@SuppressWarnings("unchecked") // we know that T is a String here
ProviderLookup<String> stringLookup = (ProviderLookup<String>) providerLookup;
stringLookup.initializeDelegate(Providers.of("out"));
assertEquals("out", providerFromBinder.get().get());
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(MembersInjectorLookup<T> lookup) {
@SuppressWarnings("unchecked") // we know that T is a String here
MembersInjectorLookup<String> stringLookup = (MembersInjectorLookup<String>) lookup;
stringLookup.initializeDelegate(stringInjector);
membersInjectorFromBinder.get().injectMembers("in");
assertEquals("in", lastInjected.get());
return null;
}
});
}
public void testGetMembersInjector() {
checkModule(
new AbstractModule() {
protected void configure() {
MembersInjector<A<String>> typeMembersInjector
= getMembersInjector(new TypeLiteral<A<String>>() {});
try {
typeMembersInjector.injectMembers(new A<String>());
} catch (IllegalStateException e) {
assertEquals(
"This MembersInjector cannot be used until the Injector has been created.",
e.getMessage());
}
MembersInjector<String> classMembersInjector = getMembersInjector(String.class);
try {
classMembersInjector.injectMembers("hello");
} catch (IllegalStateException e) {
assertEquals(
"This MembersInjector cannot be used until the Injector has been created.",
e.getMessage());
}
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(MembersInjectorLookup<T> command) {
assertEquals(new TypeLiteral<A<String>>() {}, command.getType());
assertNull(command.getDelegate());
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(MembersInjectorLookup<T> command) {
assertEquals(TypeLiteral.get(String.class), command.getType());
assertNull(command.getDelegate());
return null;
}
}
);
}
public void testRequestInjection() {
final Object firstObject = new Object();
final Object secondObject = new Object();
checkModule(
new AbstractModule() {
protected void configure() {
requestInjection(firstObject);
requestInjection(secondObject);
}
},
new FailingElementVisitor() {
@Override public Void visit(InjectionRequest<?> command) {
assertEquals(firstObject, command.getInstance());
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(InjectionRequest<?> command) {
assertEquals(secondObject, command.getInstance());
return null;
}
}
);
}
public void testRequestStaticInjection() {
checkModule(
new AbstractModule() {
protected void configure() {
requestStaticInjection(ArrayList.class);
}
},
new FailingElementVisitor() {
@Override public Void visit(StaticInjectionRequest command) {
assertEquals(ArrayList.class, command.getType());
return null;
}
}
);
}
public void testNewPrivateBinder() {
final Key<Collection> collection = Key.get(Collection.class, SampleAnnotation.class);
final Key<ArrayList> arrayList = Key.get(ArrayList.class);
final ImmutableSet<Key<?>> collections = ImmutableSet.<Key<?>>of(arrayList, collection);
final Key<?> a = Key.get(String.class, Names.named("a"));
final Key<?> b = Key.get(String.class, Names.named("b"));
final ImmutableSet<Key<?>> ab = ImmutableSet.of(a, b);
checkModule(
new AbstractModule() {
protected void configure() {
PrivateBinder one = binder().newPrivateBinder();
one.expose(ArrayList.class);
one.expose(Collection.class).annotatedWith(SampleAnnotation.class);
one.bind(List.class).to(ArrayList.class);
PrivateBinder two = binder().withSource("1 ElementsTest.java")
.newPrivateBinder().withSource("2 ElementsTest.java");
two.expose(String.class).annotatedWith(Names.named("a"));
two.expose(b);
two.bind(List.class).to(ArrayList.class);
}
},
new FailingElementVisitor() {
@Override public Void visit(PrivateElements one) {
assertEquals(collections, one.getExposedKeys());
checkElements(one.getElements(),
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals(Key.get(List.class), binding.getKey());
return null;
}
}
);
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(PrivateElements two) {
assertEquals(ab, two.getExposedKeys());
assertEquals("1 ElementsTest.java", two.getSource());
checkElements(two.getElements(),
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals("2 ElementsTest.java", binding.getSource());
assertEquals(Key.get(List.class), binding.getKey());
return null;
}
}
);
return null;
}
}
);
}
public void testBindWithMultipleAnnotationsAddsError() {
checkModule(
new AbstractModule() {
protected void configure() {
AnnotatedBindingBuilder<String> abb = bind(String.class);
abb.annotatedWith(SampleAnnotation.class);
abb.annotatedWith(Names.named("A"));
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("More than one annotation is specified for this binding.",
command.getMessage());
assertNull(command.getCause());
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testBindWithMultipleTargetsAddsError() {
checkModule(
new AbstractModule() {
protected void configure() {
AnnotatedBindingBuilder<String> abb = bind(String.class);
abb.toInstance("A");
abb.toInstance("B");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("Implementation is set more than once.", command.getMessage());
assertNull(command.getCause());
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testBindWithMultipleScopesAddsError() {
checkModule(
new AbstractModule() {
protected void configure() {
ScopedBindingBuilder sbb = bind(List.class).to(ArrayList.class);
sbb.in(Scopes.NO_SCOPE);
sbb.asEagerSingleton();
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("Scope is set more than once.", command.getMessage());
assertNull(command.getCause());
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testBindConstantWithMultipleAnnotationsAddsError() {
checkModule(
new AbstractModule() {
protected void configure() {
AnnotatedConstantBindingBuilder cbb = bindConstant();
cbb.annotatedWith(SampleAnnotation.class).to("A");
cbb.annotatedWith(Names.named("A"));
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message command) {
assertEquals("More than one annotation is specified for this binding.",
command.getMessage());
assertNull(command.getCause());
assertContains(command.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testBindConstantWithMultipleTargetsAddsError() {
checkModule(
new AbstractModule() {
protected void configure() {
ConstantBindingBuilder cbb = bindConstant().annotatedWith(SampleAnnotation.class);
cbb.to("A");
cbb.to("B");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
return null;
}
},
new FailingElementVisitor() {
@Override public Void visit(Message message) {
assertEquals("Constant value is set more than once.", message.getMessage());
assertNull(message.getCause());
assertContains(message.getSource(), "ElementsTest.java");
return null;
}
}
);
}
public void testBindToConstructor() throws NoSuchMethodException, NoSuchFieldException {
final Constructor<A> aConstructor = A.class.getDeclaredConstructor();
final Constructor<B> bConstructor = B.class.getDeclaredConstructor(Object.class);
final Field field = B.class.getDeclaredField("stage");
checkModule(
new AbstractModule() {
protected void configure() {
bind(A.class).toConstructor(aConstructor);
bind(B.class).toConstructor(bConstructor, new TypeLiteral<B<Integer>>() {})
.in(Singleton.class);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals(new Key<A>() {}, binding.getKey());
return binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ConstructorBinding<? extends T> constructorBinding) {
InjectionPoint injectionPoint = constructorBinding.getConstructor();
assertEquals(aConstructor, injectionPoint.getMember());
assertEquals(new TypeLiteral<A>() {}, injectionPoint.getDeclaringType());
return null;
}
});
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals(new Key<B>() {}, binding.getKey());
binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
assertEquals(Singleton.class, annotation);
return null;
}
});
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ConstructorBinding<? extends T> constructorBinding) {
assertEquals(bConstructor, constructorBinding.getConstructor().getMember());
assertEquals(Key.get(Integer.class),
getOnlyElement(constructorBinding.getConstructor().getDependencies()).getKey());
assertEquals(field,
getOnlyElement(constructorBinding.getInjectableMembers()).getMember());
assertEquals(2, constructorBinding.getDependencies().size());
assertEquals(ImmutableMap.of(), constructorBinding.getMethodInterceptors());
return null;
}
});
return null;
}
}
);
}
public void testBindToMalformedConstructor() throws NoSuchMethodException, NoSuchFieldException {
final Constructor<C> constructor = C.class.getDeclaredConstructor(Integer.class);
checkModule(
new AbstractModule() {
protected void configure() {
bind(C.class).toConstructor(constructor);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals(Key.get(C.class), binding.getKey());
assertTrue(binding instanceof UntargettedBinding);
return null;
}
},
new ExternalFailureVisitor() {
@Override public Void visit(Message message) {
assertContains(message.getMessage(),
C.class.getName() + ".a has more than one annotation ",
Named.class.getName(), SampleAnnotation.class.getName());
return null;
}
},
new ExternalFailureVisitor() {
@Override public Void visit(Message message) {
assertContains(message.getMessage(),
C.class.getName() + ".<init>() has more than one annotation ",
Named.class.getName(), SampleAnnotation.class.getName());
return null;
}
}
);
}
// Business logic tests
public void testModulesAreInstalledAtMostOnce() {
final AtomicInteger aConfigureCount = new AtomicInteger(0);
final Module a = new AbstractModule() {
public void configure() {
aConfigureCount.incrementAndGet();
}
};
Elements.getElements(a, a);
assertEquals(1, aConfigureCount.get());
aConfigureCount.set(0);
Module b = new AbstractModule() {
protected void configure() {
install(a);
install(a);
}
};
Elements.getElements(b);
assertEquals(1, aConfigureCount.get());
}
/**
* Ensures the module performs the commands consistent with {@code visitors}.
*/
protected void checkModule(Module module, ElementVisitor<?>... visitors) {
List<Element> elements = Elements.getElements(module);
assertEquals(elements.size(), visitors.length);
checkElements(elements, visitors);
}
protected void checkElements(List<Element> elements, ElementVisitor<?>... visitors) {
for (int i = 0; i < visitors.length; i++) {
ElementVisitor<?> visitor = visitors[i];
Element element = elements.get(i);
if (!(visitor instanceof ExternalFailureVisitor)) {
assertContains(element.getSource().toString(), "ElementsTest.java");
}
element.acceptVisitor(visitor);
}
}
private static class ListProvider implements Provider<List> {
public List get() {
return new ArrayList();
}
}
private static class TProvider<T> implements Provider<T> {
public T get() {
return null;
}
}
/**
* By extending this interface rather than FailingElementVisitor, the source of the error doesn't
* need to contain the string {@code ElementsTest.java}.
*/
abstract class ExternalFailureVisitor extends FailingElementVisitor {}
@Retention(RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@BindingAnnotation
public @interface SampleAnnotation { }
public enum CoinSide { HEADS, TAILS }
static class A<T> {
@Inject Stage stage;
}
static class B<T> {
@Inject Stage stage;
B(T t) {}
}
static class C {
@Inject @Named("foo") @SampleAnnotation String a;
C(@Named("bar") @SampleAnnotation Integer b) {}
}
}