blob: a882ab005d4808553ab123f95a61902dedb3706b [file] [log] [blame]
/**
* 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.throwingproviders;
import com.google.inject.AbstractModule;
import static com.google.inject.Asserts.assertContains;
import com.google.inject.CreationException;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Function;
import com.google.inject.internal.util.ImmutableSet;
import com.google.inject.internal.util.Iterables;
import com.google.inject.name.Names;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import junit.framework.TestCase;
/**
* @author jmourits@google.com (Jerome Mourits)
* @author jessewilson@google.com (Jesse Wilson)
*/
public class ThrowingProviderBinderTest extends TestCase {
private final TypeLiteral<RemoteProvider<String>> remoteProviderOfString
= new TypeLiteral<RemoteProvider<String>>() { };
private final MockRemoteProvider<String> mockRemoteProvider = new MockRemoteProvider<String>();
private final TestScope testScope = new TestScope();
private Injector injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(RemoteProvider.class, String.class)
.to(mockRemoteProvider)
.in(testScope);
}
});
public void testExceptionsThrown() {
RemoteProvider<String> remoteProvider =
injector.getInstance(Key.get(remoteProviderOfString));
mockRemoteProvider.throwOnNextGet("kaboom!");
try {
remoteProvider.get();
fail();
} catch (RemoteException expected) {
assertEquals("kaboom!", expected.getMessage());
}
}
public void testValuesScoped() throws RemoteException {
RemoteProvider<String> remoteProvider =
injector.getInstance(Key.get(remoteProviderOfString));
mockRemoteProvider.setNextToReturn("A");
assertEquals("A", remoteProvider.get());
mockRemoteProvider.setNextToReturn("B");
assertEquals("A", remoteProvider.get());
testScope.beginNewScope();
assertEquals("B", remoteProvider.get());
}
public void testExceptionsScoped() {
RemoteProvider<String> remoteProvider =
injector.getInstance(Key.get(remoteProviderOfString));
mockRemoteProvider.throwOnNextGet("A");
try {
remoteProvider.get();
fail();
} catch (RemoteException expected) {
assertEquals("A", expected.getMessage());
}
mockRemoteProvider.throwOnNextGet("B");
try {
remoteProvider.get();
fail();
} catch (RemoteException expected) {
assertEquals("A", expected.getMessage());
}
}
public void testAnnotations() throws RemoteException {
final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>();
mockRemoteProviderA.setNextToReturn("A");
final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>();
mockRemoteProviderB.setNextToReturn("B");
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(RemoteProvider.class, String.class)
.annotatedWith(Names.named("a"))
.to(mockRemoteProviderA);
ThrowingProviderBinder.create(binder())
.bind(RemoteProvider.class, String.class)
.to(mockRemoteProviderB);
}
});
assertEquals("A",
injector.getInstance(Key.get(remoteProviderOfString, Names.named("a"))).get());
assertEquals("B",
injector.getInstance(Key.get(remoteProviderOfString)).get());
}
public void testUndeclaredExceptions() throws RemoteException {
RemoteProvider<String> remoteProvider =
injector.getInstance(Key.get(remoteProviderOfString));
mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A"));
try {
remoteProvider.get();
fail();
} catch (RuntimeException e) {
assertEquals("A", e.getCause().getMessage());
}
// undeclared exceptions shouldn't be scoped
mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B"));
try {
remoteProvider.get();
fail();
} catch (RuntimeException e) {
assertEquals("B", e.getCause().getMessage());
}
}
public void testThrowingProviderSubclassing() throws RemoteException {
final SubMockRemoteProvider aProvider = new SubMockRemoteProvider();
aProvider.setNextToReturn("A");
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(RemoteProvider.class, String.class)
.to(aProvider);
}
});
assertEquals("A",
injector.getInstance(Key.get(remoteProviderOfString)).get());
}
static class SubMockRemoteProvider extends MockRemoteProvider<String> { }
public void testBindingToNonInterfaceType() throws RemoteException {
try {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(MockRemoteProvider.class, String.class)
.to(mockRemoteProvider);
}
});
fail();
} catch (CreationException expected) {
assertContains(expected.getMessage(), "is not a compliant interface");
}
}
public void testBindingToSubSubInterface() throws RemoteException {
try {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(SubRemoteProvider.class, String.class);
}
});
fail();
} catch (CreationException expected) {
assertContains(expected.getMessage(), "is not a compliant interface");
}
}
interface SubRemoteProvider extends RemoteProvider<String> { }
public void testBindingToInterfaceWithExtraMethod() throws RemoteException {
try {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(RemoteProviderWithExtraMethod.class, String.class);
}
});
fail();
} catch (CreationException expected) {
assertContains(expected.getMessage(), "is not a compliant interface");
}
}
public void testDependencies() {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
bind(String.class).toInstance("Foo");
bind(Integer.class).toInstance(5);
bind(Double.class).toInstance(5d);
bind(Long.class).toInstance(5L);
ThrowingProviderBinder.create(binder())
.bind(RemoteProvider.class, String.class)
.to(DependentRemoteProvider.class);
}
});
HasDependencies hasDependencies =
(HasDependencies)injector.getBinding(Key.get(remoteProviderOfString));
hasDependencies =
(HasDependencies)injector.getBinding(
Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
// Make sure that that is dependent on DependentRemoteProvider.
assertEquals(Dependency.get(Key.get(DependentRemoteProvider.class)),
Iterables.getOnlyElement(hasDependencies.getDependencies()));
// And make sure DependentRemoteProvider has the proper dependencies.
hasDependencies = (HasDependencies)injector.getBinding(DependentRemoteProvider.class);
Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
Iterables.transform(hasDependencies.getDependencies(),
new Function<Dependency<?>, Key<?>>() {
public Key<?> apply(Dependency<?> from) {
return from.getKey();
}
}));
assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
}
interface RemoteProviderWithExtraMethod<T> extends ThrowingProvider<T, RemoteException> {
T get(T defaultValue) throws RemoteException;
}
interface RemoteProvider<T> extends ThrowingProvider<T, RemoteException> { }
static class DependentRemoteProvider<T> implements RemoteProvider<T> {
@Inject double foo;
@Inject public DependentRemoteProvider(String foo, int bar) {
}
@Inject void initialize(long foo) {}
public T get() throws RemoteException {
return null;
}
}
static class MockRemoteProvider<T> implements RemoteProvider<T> {
Exception nextToThrow;
T nextToReturn;
public void throwOnNextGet(String message) {
throwOnNextGet(new RemoteException(message));
}
public void throwOnNextGet(Exception nextToThrow) {
this.nextToThrow = nextToThrow;
}
public void setNextToReturn(T nextToReturn) {
this.nextToReturn = nextToReturn;
}
public T get() throws RemoteException {
if (nextToThrow instanceof RemoteException) {
throw (RemoteException) nextToThrow;
} else if (nextToThrow instanceof RuntimeException) {
throw (RuntimeException) nextToThrow;
} else if (nextToThrow == null) {
return nextToReturn;
} else {
throw new AssertionError("nextToThrow must be a runtime or remote exception");
}
}
}
public void testBindingToInterfaceWithBoundValueType() throws RemoteException {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(StringRemoteProvider.class, String.class)
.to(new StringRemoteProvider() {
public String get() throws RemoteException {
return "A";
}
});
}
});
assertEquals("A", injector.getInstance(StringRemoteProvider.class).get());
}
interface StringRemoteProvider extends ThrowingProvider<String, RemoteException> { }
public void testBindingToInterfaceWithGeneric() throws RemoteException {
injector = Guice.createInjector(new AbstractModule() {
protected void configure() {
ThrowingProviderBinder.create(binder())
.bind(RemoteProvider.class, new TypeLiteral<List<String>>() { }.getType())
.to(new RemoteProvider<List<String>>() {
public List<String> get() throws RemoteException {
return Arrays.asList("A", "B");
}
});
}
});
Key<RemoteProvider<List<String>>> key
= Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
assertEquals(Arrays.asList("A", "B"), injector.getInstance(key).get());
}
}