blob: a5fb45a0a2f28f7c48685c3dffa17160742e1a25 [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 static com.google.inject.Asserts.assertContains;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
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.name.Names;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
/**
* @author jessewilson@google.com (Jesse Wilson)
*/
public class SpiBindingsTest extends TestCase {
public void testBindConstant() {
checkInjector(
new AbstractModule() {
protected void configure() {
bindConstant().annotatedWith(Names.named("one")).to(1);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof InstanceBinding);
assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
return null;
}
}
);
}
public void testToInstanceBinding() {
checkInjector(
new AbstractModule() {
protected void configure() {
bind(String.class).toInstance("A");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof InstanceBinding);
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(String.class), binding.getKey());
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(InstanceBinding<? extends T> binding) {
assertEquals("A", binding.getInstance());
return null;
}
});
binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
public Void visitEagerSingleton() {
return null;
}
});
return null;
}
}
);
}
public void testToProviderBinding() {
final Provider<String> stringProvider = new StringProvider();
checkInjector(
new AbstractModule() {
protected void configure() {
bind(String.class).toProvider(stringProvider);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof ProviderInstanceBinding);
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(String.class), binding.getKey());
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(
ProviderInstanceBinding<? extends T> binding) {
assertSame(stringProvider, binding.getProviderInstance());
return null;
}
});
return null;
}
}
);
}
public void testToProviderKeyBinding() {
checkInjector(
new AbstractModule() {
protected void configure() {
bind(String.class).toProvider(StringProvider.class);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof ProviderKeyBinding);
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(String.class), binding.getKey());
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ProviderKeyBinding<? extends T> binding) {
assertEquals(Key.get(StringProvider.class), binding.getProviderKey());
return null;
}
});
return null;
}
}
);
}
public void testToKeyBinding() {
final Key<String> aKey = Key.get(String.class, Names.named("a"));
final Key<String> bKey = Key.get(String.class, Names.named("b"));
checkInjector(
new AbstractModule() {
protected void configure() {
bind(aKey).to(bKey);
bind(bKey).toInstance("B");
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof LinkedKeyBinding);
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertEquals(aKey, binding.getKey());
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(LinkedKeyBinding<? extends T> binding) {
assertEquals(bKey, binding.getLinkedKey());
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertEquals(bKey, binding.getKey());
return null;
}
}
);
}
public void testToConstructorBinding() {
checkInjector(
new AbstractModule() {
protected void configure() {
bind(D.class);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof ConstructorBinding);
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(D.class), binding.getKey());
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(ConstructorBinding<? extends T> binding) {
Constructor<?> expected = D.class.getDeclaredConstructors()[0];
assertEquals(expected, binding.getConstructor().getMember());
assertEquals(ImmutableSet.<InjectionPoint>of(), binding.getInjectableMembers());
return null;
}
});
return null;
}
}
);
}
public void testConstantBinding() {
checkInjector(
new AbstractModule() {
protected void configure() {
bindConstant().annotatedWith(Names.named("one")).to(1);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> binding) {
assertTrue(binding instanceof InstanceBinding);
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
@Override public Void visit(InstanceBinding<? extends T> binding) {
assertEquals(1, binding.getInstance());
return null;
}
});
return null;
}
}
);
}
public void testConvertedConstantBinding() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bindConstant().annotatedWith(Names.named("one")).to("1");
}
});
Binding<Integer> binding = injector.getBinding(Key.get(Integer.class, Names.named("one")));
assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertTrue(binding instanceof ConvertedConstantBinding);
binding.acceptTargetVisitor(new FailingTargetVisitor<Integer>() {
@Override public Void visit(
ConvertedConstantBinding<? extends Integer> binding) {
assertEquals((Integer) 1, binding.getValue());
assertEquals(Key.get(String.class, Names.named("one")), binding.getSourceKey());
return null;
}
});
}
public void testProviderBinding() {
Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(String.class).toInstance("A");
}
});
Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
Binding<Provider<String>> binding = injector.getBinding(providerOfStringKey);
assertEquals(providerOfStringKey, binding.getKey());
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertTrue(binding instanceof ProviderBinding);
binding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
@Override public Void visit(
ProviderBinding<? extends Provider<String>> binding) {
assertEquals(Key.get(String.class), binding.getProvidedKey());
return null;
}
});
}
public void testScopes() {
checkInjector(
new AbstractModule() {
protected void configure() {
bind(String.class).annotatedWith(Names.named("a"))
.toProvider(StringProvider.class).in(Singleton.class);
bind(String.class).annotatedWith(Names.named("b"))
.toProvider(StringProvider.class).in(Scopes.SINGLETON);
bind(String.class).annotatedWith(Names.named("c"))
.toProvider(StringProvider.class).asEagerSingleton();
bind(String.class).annotatedWith(Names.named("d"))
.toProvider(StringProvider.class);
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class, Names.named("a")), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitScope(Scope scope) {
// even though we bound with an annotation, the injector always uses instances
assertSame(Scopes.SINGLETON, scope);
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class, Names.named("b")), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitScope(Scope scope) {
assertSame(Scopes.SINGLETON, scope);
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class, Names.named("c")), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitEagerSingleton() {
return null;
}
});
return null;
}
},
new FailingElementVisitor() {
@Override public <T> Void visit(Binding<T> command) {
assertEquals(Key.get(String.class, Names.named("d")), command.getKey());
command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
@Override public Void visitNoScoping() {
return null;
}
});
return null;
}
}
);
}
public void testExtensionSpi() {
final AtomicBoolean visiting = new AtomicBoolean(false);
final Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(String.class).toProvider(new ProviderWithExtensionVisitor<String>() {
public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
ProviderInstanceBinding<? extends B> binding) {
assertSame(this, binding.getProviderInstance());
// We can't always check for FailingSpiTargetVisitor,
// because constructing the injector visits here, and we need
// to process the binding as normal
if(visiting.get()) {
assertTrue("visitor: " + visitor, visitor instanceof FailingSpiTargetVisitor);
return (V)"visited";
} else {
return visitor.visit(binding);
}
}
public String get() {
return "FooBar";
}
});
}
});
visiting.set(true);
// Check for Provider<String> binding -- that is still a ProviderBinding.
Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
Binding<Provider<String>> providerBinding = injector.getBinding(providerOfStringKey);
assertEquals(providerOfStringKey, providerBinding.getKey());
assertContains(providerBinding.getSource().toString(), "SpiBindingsTest.java");
assertTrue("binding: " + providerBinding, providerBinding instanceof ProviderBinding);
providerBinding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
@Override public Void visit(ProviderBinding<? extends Provider<String>> binding) {
assertEquals(Key.get(String.class), binding.getProvidedKey());
return null;
}
});
// Check for String binding -- that one is ProviderInstanceBinding, and gets hooked
Binding<String> binding = injector.getBinding(String.class);
assertEquals(Key.get(String.class), binding.getKey());
assertContains(binding.getSource().toString(), "SpiBindingsTest.java");
assertTrue(binding instanceof ProviderInstanceBinding);
assertEquals("visited", binding.acceptTargetVisitor(new FailingSpiTargetVisitor<String>()));
}
private static class FailingSpiTargetVisitor<T> extends DefaultBindingTargetVisitor<T, String> {
@Override
protected String visitOther(Binding<? extends T> binding) {
throw new AssertionFailedError();
}
}
public void checkInjector(Module module, ElementVisitor<?>... visitors) {
Injector injector = Guice.createInjector(module);
List<Binding<?>> bindings = Lists.newArrayList(injector.getBindings().values());
for (Iterator<Binding<?>> i = bindings.iterator(); i.hasNext(); ) {
if (BUILT_IN_BINDINGS.contains(i.next().getKey())) {
i.remove();
}
}
Collections.sort(bindings, orderByKey);
assertEquals(bindings.size(), visitors.length);
for (int i = 0; i < visitors.length; i++) {
ElementVisitor<?> visitor = visitors[i];
Binding<?> binding = bindings.get(i);
binding.acceptVisitor(visitor);
}
}
private final ImmutableSet<Key<?>> BUILT_IN_BINDINGS = ImmutableSet.of(
Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class));
private final Comparator<Binding<?>> orderByKey = new Comparator<Binding<?>>() {
public int compare(Binding<?> a, Binding<?> b) {
return a.getKey().toString().compareTo(b.getKey().toString());
}
};
private static class StringProvider implements Provider<String> {
public String get() {
return "A";
}
}
private static class C { }
private static class D extends C {
@Inject public D(Injector unused) { }
}
}