blob: 86a76f420fe38445a54088d0f99a9c3e41a43115 [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.assistedinject;
import com.google.inject.*;
import static com.google.inject.Asserts.assertContains;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import junit.framework.TestCase;
import java.awt.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
/**
* @author jmourits@google.com (Jerome Mourits)
* @author jessewilson@google.com (Jesse Wilson)
*/
public class FactoryProviderTest extends TestCase {
public void testAssistedFactory() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(Double.class).toInstance(5.0d);
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
}
});
ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
Mustang blueMustang = (Mustang) carFactory.create(Color.BLUE);
assertEquals(Color.BLUE, blueMustang.color);
assertEquals(5.0d, blueMustang.engineSize);
Mustang redMustang = (Mustang) carFactory.create(Color.RED);
assertEquals(Color.RED, redMustang.color);
assertEquals(5.0d, redMustang.engineSize);
}
public void testAssistedFactoryWithAnnotations() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(int.class).annotatedWith(Names.named("horsePower")).toInstance(250);
bind(int.class).annotatedWith(Names.named("modelYear")).toInstance(1984);
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Camaro.class));
}
});
ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
Camaro blueCamaro = (Camaro) carFactory.create(Color.BLUE);
assertEquals(Color.BLUE, blueCamaro.color);
assertEquals(1984, blueCamaro.modelYear);
assertEquals(250, blueCamaro.horsePower);
Camaro redCamaro = (Camaro) carFactory.create(Color.RED);
assertEquals(Color.RED, redCamaro.color);
assertEquals(1984, redCamaro.modelYear);
assertEquals(250, redCamaro.horsePower);
}
interface Car {
}
interface ColoredCarFactory {
Car create(Color color);
}
public static class Mustang implements Car {
private final double engineSize;
private final Color color;
@AssistedInject
public Mustang(double engineSize, @Assisted Color color) {
this.engineSize = engineSize;
this.color = color;
}
}
public static class Camaro implements Car {
private final int horsePower;
private final int modelYear;
private final Color color;
@AssistedInject
public Camaro(
@Named("horsePower")int horsePower,
@Named("modelYear")int modelYear,
@Assisted Color color) {
this.horsePower = horsePower;
this.modelYear = modelYear;
this.color = color;
}
}
interface SummerCarFactory {
Car create(Color color, boolean convertable);
Car createConvertible(Color color);
}
public void testFactoryWithMultipleMethods() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(float.class).toInstance(140f);
bind(SummerCarFactory.class).toProvider(
FactoryProvider.newFactory(SummerCarFactory.class, Corvette.class));
}
});
SummerCarFactory carFactory = injector.getInstance(SummerCarFactory.class);
Corvette blueCorvette = (Corvette) carFactory.createConvertible(Color.BLUE);
assertEquals(Color.BLUE, blueCorvette.color);
assertEquals(100f, blueCorvette.maxMph);
assertTrue(blueCorvette.isConvertable);
Corvette redCorvette = (Corvette) carFactory.create(Color.RED, false);
assertEquals(Color.RED, redCorvette.color);
assertEquals(140f, redCorvette.maxMph);
assertFalse(redCorvette.isConvertable);
}
public static class Corvette implements Car {
private boolean isConvertable;
private Color color;
private float maxMph;
@AssistedInject
public Corvette(@Assisted Color color) {
this(color, 100f, true);
}
public Corvette(@Assisted Color color, @Assisted boolean isConvertable) {
throw new IllegalStateException("Not an @AssistedInject constructor");
}
@AssistedInject
public Corvette(@Assisted Color color, Float maxMph, @Assisted boolean isConvertable) {
this.isConvertable = isConvertable;
this.color = color;
this.maxMph = maxMph;
}
}
public void testFactoryMethodsMismatch() {
try {
FactoryProvider.newFactory(SummerCarFactory.class, Beetle.class);
fail();
} catch(IllegalArgumentException e) {
assertTrue(e.getMessage().startsWith("Constructor mismatch"));
}
}
public static class Beetle implements Car {
@AssistedInject
public Beetle(@Assisted Color color) {
throw new IllegalStateException("Conflicting constructors");
}
@AssistedInject
public Beetle(@Assisted Color color, @Assisted boolean isConvertable) {
throw new IllegalStateException("Conflicting constructors");
}
@AssistedInject
public Beetle(@Assisted Color color, @Assisted boolean isConvertable, float maxMph) {
throw new IllegalStateException("Conflicting constructors");
}
}
public void testMethodsAndFieldsGetInjected() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(String.class).toInstance("turbo");
bind(int.class).toInstance(911);
bind(double.class).toInstance(50000d);
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Porshe.class));
}
});
ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
Porshe grayPorshe = (Porshe) carFactory.create(Color.GRAY);
assertEquals(Color.GRAY, grayPorshe.color);
assertEquals(50000d, grayPorshe.price);
assertEquals(911, grayPorshe.model);
assertEquals("turbo", grayPorshe.name);
}
public static class Porshe implements Car {
private final Color color;
private final double price;
private @Inject String name;
private int model;
@AssistedInject
public Porshe(@Assisted Color color, double price) {
this.color = color;
this.price = price;
}
@Inject void setModel(int model) {
this.model = model;
}
}
public void testProviderInjection() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(String.class).toInstance("trans am");
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Firebird.class));
}
});
ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
Firebird blackFirebird = (Firebird) carFactory.create(Color.BLACK);
assertEquals(Color.BLACK, blackFirebird.color);
assertEquals("trans am", blackFirebird.modifiersProvider.get());
}
public static class Firebird implements Car {
private final Provider<String> modifiersProvider;
private final Color color;
@AssistedInject
public Firebird(Provider<String> modifiersProvider, @Assisted Color color) {
this.modifiersProvider = modifiersProvider;
this.color = color;
}
}
public void testTypeTokenInjection() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(new TypeLiteral<Set<String>>() {}).toInstance(Collections.singleton("Flux Capacitor"));
bind(new TypeLiteral<Set<Integer>>() {}).toInstance(Collections.singleton(88));
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, DeLorean.class));
}
});
ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
DeLorean deLorean = (DeLorean) carFactory.create(Color.GRAY);
assertEquals(Color.GRAY, deLorean.color);
assertEquals("Flux Capacitor", deLorean.features.iterator().next());
assertEquals(new Integer(88), deLorean.featureActivationSpeeds.iterator().next());
}
public static class DeLorean implements Car {
private final Set<String> features;
private final Set<Integer> featureActivationSpeeds;
private final Color color;
@AssistedInject
public DeLorean(
Set<String> extraFeatures, Set<Integer> featureActivationSpeeds, @Assisted Color color) {
this.features = extraFeatures;
this.featureActivationSpeeds = featureActivationSpeeds;
this.color = color;
}
}
public void testTypeTokenProviderInjection() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(new TypeLiteral<Set<String>>() { }).toInstance(Collections.singleton("Datsun"));
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Z.class));
}
});
ColoredCarFactory carFactory = injector.getInstance(ColoredCarFactory.class);
Z orangeZ = (Z) carFactory.create(Color.ORANGE);
assertEquals(Color.ORANGE, orangeZ.color);
assertEquals("Datsun", orangeZ.manufacturersProvider.get().iterator().next());
}
public static class Z implements Car {
private final Provider<Set<String>> manufacturersProvider;
private final Color color;
@AssistedInject
public Z(Provider<Set<String>> manufacturersProvider, @Assisted Color color) {
this.manufacturersProvider = manufacturersProvider;
this.color = color;
}
}
public static class Prius implements Car {
@SuppressWarnings("unused")
private final Color color;
@AssistedInject
private Prius(@Assisted Color color) {
this.color = color;
}
}
public void testAssistInjectionInNonPublicConstructor() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Prius.class));
}
});
Car car = injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
}
public static class ExplodingCar implements Car {
@AssistedInject
public ExplodingCar(@Assisted Color color) {
throw new IllegalStateException("kaboom!");
}
}
public void testExceptionDuringConstruction() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(ColoredCarFactory.class).toProvider(
FactoryProvider.newFactory(ColoredCarFactory.class, ExplodingCar.class));
}
});
try {
injector.getInstance(ColoredCarFactory.class).create(Color.ORANGE);
fail();
} catch (IllegalStateException e) {
assertEquals("kaboom!", e.getMessage());
}
}
public static class DefectiveCar implements Car {
@AssistedInject
public DefectiveCar() throws ExplosionException, FireException {
throw new ExplosionException();
}
}
public static class ExplosionException extends Exception { }
public static class FireException extends Exception { }
public interface DefectiveCarFactoryWithNoExceptions {
Car createCar();
}
public interface DefectiveCarFactory {
Car createCar() throws FireException;
}
public void testFactoryMethodMustDeclareAllConstructorExceptions() {
try {
FactoryProvider.newFactory(DefectiveCarFactoryWithNoExceptions.class, DefectiveCar.class);
fail();
} catch (IllegalStateException expected) {
assertContains(expected.getMessage(), "no compatible exception is thrown");
}
}
public interface CorrectDefectiveCarFactory {
Car createCar() throws FireException, ExplosionException;
}
public void testConstructorExceptionsAreThrownByFactory() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(CorrectDefectiveCarFactory.class).toProvider(
FactoryProvider.newFactory(
CorrectDefectiveCarFactory.class, DefectiveCar.class));
}
});
try {
injector.getInstance(CorrectDefectiveCarFactory.class).createCar();
fail();
} catch (FireException e) {
fail();
} catch (ExplosionException expected) {
}
}
public static class MultipleConstructorDefectiveCar implements Car {
@AssistedInject
public MultipleConstructorDefectiveCar() throws ExplosionException {
throw new ExplosionException();
}
@AssistedInject
public MultipleConstructorDefectiveCar(@Assisted Color c) throws FireException {
throw new FireException();
}
}
public interface MultipleConstructorDefectiveCarFactory {
Car createCar() throws ExplosionException;
Car createCar(Color r) throws FireException;
}
public void testMultipleConstructorExceptionMatching() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(MultipleConstructorDefectiveCarFactory.class).toProvider(
FactoryProvider.newFactory(
MultipleConstructorDefectiveCarFactory.class,
MultipleConstructorDefectiveCar.class));
}
});
MultipleConstructorDefectiveCarFactory factory
= injector.getInstance(MultipleConstructorDefectiveCarFactory.class);
try {
factory.createCar();
fail();
} catch (ExplosionException expected) {
}
try {
factory.createCar(Color.RED);
fail();
} catch (FireException expected) {
}
}
public static class WildcardCollection {
public interface Factory {
WildcardCollection create(Collection<?> items);
}
@AssistedInject
public WildcardCollection(@Assisted Collection<?> items) { }
}
public void testWildcardGenerics() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(WildcardCollection.Factory.class).toProvider(
FactoryProvider.newFactory(
WildcardCollection.Factory.class,
WildcardCollection.class));
}
});
WildcardCollection.Factory factory = injector.getInstance(WildcardCollection.Factory.class);
factory.create(Collections.emptyList());
}
public static class SteeringWheel {}
public static class Fiat implements Car {
@SuppressWarnings("unused")
private final SteeringWheel steeringWheel;
@SuppressWarnings("unused")
private final Color color;
@AssistedInject
public Fiat(SteeringWheel steeringWheel, @Assisted Color color) {
this.steeringWheel = steeringWheel;
this.color = color;
}
}
public void testFactoryWithImplicitBindings() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(ColoredCarFactory.class).toProvider(
FactoryProvider.newFactory(
ColoredCarFactory.class,
Fiat.class));
}
});
ColoredCarFactory coloredCarFactory = injector.getInstance(ColoredCarFactory.class);
Fiat fiat = (Fiat) coloredCarFactory.create(Color.GREEN);
assertEquals(Color.GREEN, fiat.color);
assertNotNull(fiat.steeringWheel);
}
public void testFactoryFailsWithMissingBinding() {
try {
Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(ColoredCarFactory.class)
.toProvider(FactoryProvider.newFactory(ColoredCarFactory.class, Mustang.class));
}
});
fail();
} catch (ProvisionException expected) {
assertContains(expected.getCause().getMessage(),
"Parameter of type 'double' is not injectable or annotated with @Assisted");
}
}
// TODO(jessewilson): test for duplicate constructors
}