Synchronize DependencyContainer

The implication is that dependency containers are now always shared data. In the sense of application context, this is acceptable.
This commit is contained in:
Mike Bierlee 2015-03-28 16:36:02 +01:00
parent 8d268846ed
commit 9860624148
4 changed files with 548 additions and 548 deletions

View file

@ -79,7 +79,7 @@ private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAd
* *
* See_Also: Autowire * See_Also: Autowire
*/ */
public void autowire(Type)(DependencyContainer container, Type instance) { public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
debug(poodinisVerbose) { debug(poodinisVerbose) {
printDebugAutowiredInstance(typeid(Type), &instance); printDebugAutowiredInstance(typeid(Type), &instance);
} }
@ -93,7 +93,7 @@ private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void*
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member)); writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member));
} }
private void autowireMember(string member, Type)(DependencyContainer container, Type instance) { private void autowireMember(string member, Type)(shared(DependencyContainer) container, Type instance) {
static if(__traits(compiles, __traits(getMember, instance, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, instance, member)))) { static if(__traits(compiles, __traits(getMember, instance, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, instance, member)))) {
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) { foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) {
static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) { static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) {
@ -138,9 +138,9 @@ public void globalAutowire(Type)(Type instance) {
} }
class AutowiredRegistration(RegistrationType : Object) : Registration { class AutowiredRegistration(RegistrationType : Object) : Registration {
private DependencyContainer container; private shared(DependencyContainer) container;
public this(TypeInfo registeredType, DependencyContainer container) { public this(TypeInfo registeredType, shared(DependencyContainer) container) {
enforce(!(container is null), "Argument 'container' is null. Autowired registrations need to autowire using a container."); enforce(!(container is null), "Argument 'container' is null. Autowired registrations need to autowire using a container.");
this.container = container; this.container = container;
super(registeredType, typeid(RegistrationType)); super(registeredType, typeid(RegistrationType));

View file

@ -44,7 +44,7 @@ class ResolveException : Exception {
* In most cases you want to use a global singleton dependency container provided by getInstance() to manage all dependencies. * In most cases you want to use a global singleton dependency container provided by getInstance() to manage all dependencies.
* You can still create new instances of this class for exceptional situations. * You can still create new instances of this class for exceptional situations.
*/ */
class DependencyContainer { synchronized class DependencyContainer {
private Registration[][TypeInfo] registrations; private Registration[][TypeInfo] registrations;
private Registration[] autowireStack; private Registration[] autowireStack;
@ -111,16 +111,16 @@ class DependencyContainer {
return existingRegistration; return existingRegistration;
} }
AutowiredRegistration!ConcreteType newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this); auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this);
newRegistration.singleInstance(); newRegistration.singleInstance();
registrations[registeredType] ~= newRegistration; registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
return newRegistration; return newRegistration;
} }
private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) { private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) {
auto existingCandidates = registrationType in registrations; auto existingCandidates = registrationType in registrations;
if (existingCandidates) { if (existingCandidates) {
return getRegistration(*existingCandidates, qualifierType); return getRegistration(cast(Registration[]) *existingCandidates, qualifierType);
} }
return null; return null;
@ -218,13 +218,13 @@ class DependencyContainer {
throw new ResolveException("Type not registered.", resolveType); throw new ResolveException("Type not registered.", resolveType);
} }
Registration registration = getQualifiedRegistration(resolveType, qualifierType, *candidates); Registration registration = getQualifiedRegistration(resolveType, qualifierType, cast(Registration[]) *candidates);
QualifierType instance; QualifierType instance;
if (!autowireStack.canFind(registration)) { if (!(cast(Registration[]) autowireStack).canFind(registration)) {
autowireStack ~= registration; autowireStack ~= cast(shared(Registration)) registration;
instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext()); instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext());
autowireStack.popBack(); autowireStack = autowireStack[0 .. $-1];
} else { } else {
auto autowireContext = new AutowireInstantiationContext(); auto autowireContext = new AutowireInstantiationContext();
autowireContext.autowireInstance = false; autowireContext.autowireInstance = false;

View file

@ -1,164 +1,164 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
import poodinis.autowire; import poodinis.autowire;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
class ComponentA {} class ComponentA {}
class ComponentB { class ComponentB {
public @Autowire ComponentA componentA; public @Autowire ComponentA componentA;
public bool componentIsNull() { public bool componentIsNull() {
return componentA is null; return componentA is null;
} }
} }
interface InterfaceA {} interface InterfaceA {}
class ComponentC : InterfaceA {} class ComponentC : InterfaceA {}
class ComponentD { class ComponentD {
public @Autowire InterfaceA componentC = null; public @Autowire InterfaceA componentC = null;
public bool componentIsNull() { public bool componentIsNull() {
return componentC is null; return componentC is null;
} }
} }
class DummyAttribute{}; class DummyAttribute{};
class ComponentE { class ComponentE {
@DummyAttribute @DummyAttribute
public ComponentC componentC; public ComponentC componentC;
} }
class ComponentDeclarationCocktail { class ComponentDeclarationCocktail {
alias noomer = int; alias noomer = int;
@Autowire @Autowire
public ComponentA componentA; public ComponentA componentA;
public void doesNothing() { public void doesNothing() {
} }
~this(){ ~this(){
} }
} }
class ComponentX : InterfaceA {} class ComponentX : InterfaceA {}
class MonkeyShine { class MonkeyShine {
@Autowire!ComponentX @Autowire!ComponentX
public InterfaceA component; public InterfaceA component;
} }
class BootstrapBootstrap { class BootstrapBootstrap {
@Autowire!ComponentX @Autowire!ComponentX
public InterfaceA componentX; public InterfaceA componentX;
@Autowire!ComponentC @Autowire!ComponentC
public InterfaceA componentC; public InterfaceA componentC;
} }
// Test autowiring concrete type to existing instance // Test autowiring concrete type to existing instance
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto componentB = new ComponentB(); auto componentB = new ComponentB();
container.autowire!(ComponentB)(componentB); container.autowire!(ComponentB)(componentB);
assert(!componentB.componentIsNull(), "Autowirable dependency failed to autowire"); assert(!componentB.componentIsNull(), "Autowirable dependency failed to autowire");
} }
// Test autowiring interface type to existing instance // Test autowiring interface type to existing instance
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
auto componentD = new ComponentD(); auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD); container.autowire!(ComponentD)(componentD);
assert(!componentD.componentIsNull(), "Autowirable dependency failed to autowire"); assert(!componentD.componentIsNull(), "Autowirable dependency failed to autowire");
} }
// Test autowiring will only happen once // Test autowiring will only happen once
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC).newInstance(); container.register!(InterfaceA, ComponentC).newInstance();
auto componentD = new ComponentD(); auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD); container.autowire!(ComponentD)(componentD);
auto expectedComponent = componentD.componentC; auto expectedComponent = componentD.componentC;
container.autowire!(ComponentD)(componentD); container.autowire!(ComponentD)(componentD);
auto actualComponent = componentD.componentC; auto actualComponent = componentD.componentC;
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance"); assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
} }
// Test autowiring unregistered type // Test autowiring unregistered type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto componentD = new ComponentD(); auto componentD = new ComponentD();
assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException"); assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException");
} }
// Test autowiring member with non-autowire attribute does not autowire // Test autowiring member with non-autowire attribute does not autowire
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto componentE = new ComponentE(); auto componentE = new ComponentE();
container.autowire!ComponentE(componentE); container.autowire!ComponentE(componentE);
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire"); assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
} }
// Test autowire class with alias declaration // Test autowire class with alias declaration
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto componentDeclarationCocktail = new ComponentDeclarationCocktail(); auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
container.autowire(componentDeclarationCocktail); container.autowire(componentDeclarationCocktail);
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed"); assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
} }
// Test autowire class with qualifier // Test autowire class with qualifier
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX); container.register!(InterfaceA, ComponentX);
auto componentX = container.resolve!(InterfaceA, ComponentX); auto componentX = container.resolve!(InterfaceA, ComponentX);
auto monkeyShine = new MonkeyShine(); auto monkeyShine = new MonkeyShine();
container.autowire(monkeyShine); container.autowire(monkeyShine);
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed"); assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
} }
// Test autowire class with multiple qualifiers // Test autowire class with multiple qualifiers
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX); container.register!(InterfaceA, ComponentX);
auto componentC = container.resolve!(InterfaceA, ComponentC); auto componentC = container.resolve!(InterfaceA, ComponentC);
auto componentX = container.resolve!(InterfaceA, ComponentX); auto componentX = container.resolve!(InterfaceA, ComponentX);
auto bootstrapBootstrap = new BootstrapBootstrap(); auto bootstrapBootstrap = new BootstrapBootstrap();
container.autowire(bootstrapBootstrap); container.autowire(bootstrapBootstrap);
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed"); assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed"); assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
} }
// Test getting instance from autowired registration will autowire instance // Test getting instance from autowired registration will autowire instance
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance(); auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext()); auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
assert(!instance.componentIsNull()); assert(!instance.componentIsNull());
} }
} }

View file

@ -1,372 +1,372 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
import poodinis.container; import poodinis.container;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
interface TestInterface { interface TestInterface {
} }
class TestClass : TestInterface { class TestClass : TestInterface {
} }
class UnrelatedClass{ class UnrelatedClass{
} }
class FailOnCreationClass { class FailOnCreationClass {
this() { this() {
throw new Exception("This class should not be instantiated"); throw new Exception("This class should not be instantiated");
} }
} }
class AutowiredClass { class AutowiredClass {
} }
class ComponentClass { class ComponentClass {
@Autowire @Autowire
public AutowiredClass autowiredClass; public AutowiredClass autowiredClass;
} }
class ComponentCat { class ComponentCat {
@Autowire @Autowire
public ComponentMouse mouse; public ComponentMouse mouse;
} }
class ComponentMouse { class ComponentMouse {
@Autowire @Autowire
public ComponentCat cat; public ComponentCat cat;
} }
class Eenie { class Eenie {
@Autowire @Autowire
public Meenie meenie; public Meenie meenie;
} }
class Meenie { class Meenie {
@Autowire @Autowire
public Moe moe; public Moe moe;
} }
class Moe { class Moe {
@Autowire @Autowire
public Eenie eenie; public Eenie eenie;
} }
class Ittie { class Ittie {
@Autowire @Autowire
public Bittie bittie; public Bittie bittie;
} }
class Bittie { class Bittie {
@Autowire @Autowire
public Banana banana; public Banana banana;
} }
class Banana { class Banana {
@Autowire @Autowire
public Bittie bittie; public Bittie bittie;
} }
interface SuperInterface { interface SuperInterface {
} }
class SuperImplementation : SuperInterface { class SuperImplementation : SuperInterface {
@Autowire @Autowire
public Banana banana; public Banana banana;
} }
interface Color { interface Color {
} }
class Blue : Color { class Blue : Color {
} }
class Red : Color { class Red : Color {
} }
class Spiders { class Spiders {
@Autowire @Autowire
public TestInterface testMember; public TestInterface testMember;
} }
class Recursive { class Recursive {
@Autowire @Autowire
public Recursive recursive; public Recursive recursive;
} }
// Test register concrete type // Test register concrete type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto registration = container.register!(TestClass)(); auto registration = container.register!(TestClass)();
assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same"); assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same");
} }
// Test resolve registered type // Test resolve registered type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(TestClass)(); container.register!(TestClass)();
TestClass actualInstance = container.resolve!(TestClass)(); TestClass actualInstance = container.resolve!(TestClass)();
assert(actualInstance !is null, "Resolved type is null"); assert(actualInstance !is null, "Resolved type is null");
assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected"); assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected");
} }
// Test register interface // Test register interface
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(TestInterface, TestClass)(); container.register!(TestInterface, TestClass)();
TestInterface actualInstance = container.resolve!(TestInterface)(); TestInterface actualInstance = container.resolve!(TestInterface)();
assert(actualInstance !is null, "Resolved type is null"); assert(actualInstance !is null, "Resolved type is null");
assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected"); assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected");
} }
// Test resolve non-registered type // Test resolve non-registered type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving non-registered type does not fail"); assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving non-registered type does not fail");
} }
// Test clear registrations // Test clear registrations
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(TestClass)(); container.register!(TestClass)();
container.clearAllRegistrations(); container.clearAllRegistrations();
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving cleared type does not fail"); assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving cleared type does not fail");
} }
// Test get singleton of container // Test get singleton of container
unittest { unittest {
auto instance1 = DependencyContainer.getInstance(); auto instance1 = DependencyContainer.getInstance();
auto instance2 = DependencyContainer.getInstance(); auto instance2 = DependencyContainer.getInstance();
assert(instance1 is instance2, "getInstance does not return the same instance"); assert(instance1 is instance2, "getInstance does not return the same instance");
} }
// Test resolve single instance for type // Test resolve single instance for type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(TestClass)().singleInstance(); container.register!(TestClass)().singleInstance();
auto instance1 = container.resolve!(TestClass); auto instance1 = container.resolve!(TestClass);
auto instance2 = container.resolve!(TestClass); auto instance2 = container.resolve!(TestClass);
assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved"); assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved");
} }
// Test resolve new instance for type // Test resolve new instance for type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(TestClass)().newInstance(); container.register!(TestClass)().newInstance();
auto instance1 = container.resolve!(TestClass); auto instance1 = container.resolve!(TestClass);
auto instance2 = container.resolve!(TestClass); auto instance2 = container.resolve!(TestClass);
assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved"); assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved");
} }
// Test resolve existing instance for type // Test resolve existing instance for type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto expectedInstance = new TestClass(); auto expectedInstance = new TestClass();
container.register!(TestClass)().existingInstance(expectedInstance); container.register!(TestClass)().existingInstance(expectedInstance);
auto actualInstance = container.resolve!(TestClass); auto actualInstance = container.resolve!(TestClass);
assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance"); assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance");
} }
// Test autowire resolved instances // Test autowire resolved instances
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!AutowiredClass; container.register!AutowiredClass;
container.register!ComponentClass; container.register!ComponentClass;
auto componentInstance = container.resolve!ComponentClass; auto componentInstance = container.resolve!ComponentClass;
auto autowiredInstance = container.resolve!AutowiredClass; auto autowiredInstance = container.resolve!AutowiredClass;
assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving"); assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving");
} }
// Test circular autowiring // Test circular autowiring
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentMouse; container.register!ComponentMouse;
container.register!ComponentCat; container.register!ComponentCat;
auto mouse = container.resolve!ComponentMouse; auto mouse = container.resolve!ComponentMouse;
auto cat = container.resolve!ComponentCat; auto cat = container.resolve!ComponentCat;
assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable"); assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable");
} }
// Test remove registration // Test remove registration
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!TestClass; container.register!TestClass;
container.removeRegistration!TestClass; container.removeRegistration!TestClass;
assertThrown!ResolveException(container.resolve!TestClass); assertThrown!ResolveException(container.resolve!TestClass);
} }
// Test autowiring does not autowire member where instance is non-null // Test autowiring does not autowire member where instance is non-null
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto existingA = new AutowiredClass(); auto existingA = new AutowiredClass();
auto existingB = new ComponentClass(); auto existingB = new ComponentClass();
existingB.autowiredClass = existingA; existingB.autowiredClass = existingA;
container.register!AutowiredClass; container.register!AutowiredClass;
container.register!(ComponentClass).existingInstance(existingB); container.register!(ComponentClass).existingInstance(existingB);
auto resolvedA = container.resolve!AutowiredClass; auto resolvedA = container.resolve!AutowiredClass;
auto resolvedB = container.resolve!ComponentClass; auto resolvedB = container.resolve!ComponentClass;
assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance"); assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance");
} }
// Test autowiring circular dependency by third-degree // Test autowiring circular dependency by third-degree
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!Eenie; container.register!Eenie;
container.register!Meenie; container.register!Meenie;
container.register!Moe; container.register!Moe;
auto eenie = container.resolve!Eenie; auto eenie = container.resolve!Eenie;
assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed"); assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed");
} }
// Test autowiring deep circular dependencies // Test autowiring deep circular dependencies
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!Ittie; container.register!Ittie;
container.register!Bittie; container.register!Bittie;
container.register!Banana; container.register!Banana;
auto ittie = container.resolve!Ittie; auto ittie = container.resolve!Ittie;
assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed."); assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed.");
} }
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time // Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(Ittie).newInstance(); container.register!(Ittie).newInstance();
container.register!(Bittie).newInstance(); container.register!(Bittie).newInstance();
container.register!(Banana).newInstance(); container.register!(Banana).newInstance();
auto ittie = container.resolve!Ittie; auto ittie = container.resolve!Ittie;
assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type."); assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
} }
// Test autowiring type registered by interface // Test autowiring type registered by interface
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!Banana; container.register!Banana;
container.register!Bittie; container.register!Bittie;
container.register!(SuperInterface, SuperImplementation); container.register!(SuperInterface, SuperImplementation);
SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface; SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface;
assert(!(superInstance.banana is null), "Instance which was resolved by interface type was not autowired."); assert(!(superInstance.banana is null), "Instance which was resolved by interface type was not autowired.");
} }
// Test reusing a container after clearing all registrations // Test reusing a container after clearing all registrations
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!Banana; container.register!Banana;
container.clearAllRegistrations(); container.clearAllRegistrations();
try { try {
container.resolve!Banana; container.resolve!Banana;
} catch (ResolveException e) { } catch (ResolveException e) {
container.register!Banana; container.register!Banana;
return; return;
} }
assert(false); assert(false);
} }
// Test register multiple concrete classess to same interface type // Test register multiple concrete classess to same interface type
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(Color, Blue); container.register!(Color, Blue);
container.register!(Color, Red); container.register!(Color, Red);
} }
// Test removing all registrations for type with multiple registrations. // Test removing all registrations for type with multiple registrations.
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(Color, Blue); container.register!(Color, Blue);
container.register!(Color, Red); container.register!(Color, Red);
container.removeRegistration!Color; container.removeRegistration!Color;
} }
// Test registering same registration again // Test registering same registration again
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto firstRegistration = container.register!(Color, Blue); auto firstRegistration = container.register!(Color, Blue);
auto secondRegistration = container.register!(Color, Blue); auto secondRegistration = container.register!(Color, Blue);
assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types"); assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types");
} }
// Test resolve registration with multiple qualifiers // Test resolve registration with multiple qualifiers
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(Color, Blue); container.register!(Color, Blue);
container.register!(Color, Red); container.register!(Color, Red);
try { try {
container.resolve!Color; container.resolve!Color;
} catch (ResolveException e) { } catch (ResolveException e) {
return; return;
} }
assert(false); assert(false);
} }
// Test resolve registration with multiple qualifiers using a qualifier // Test resolve registration with multiple qualifiers using a qualifier
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(Color, Blue); container.register!(Color, Blue);
container.register!(Color, Red); container.register!(Color, Red);
auto blueInstance = container.resolve!(Color, Blue); auto blueInstance = container.resolve!(Color, Blue);
auto redInstance = container.resolve!(Color, Red); auto redInstance = container.resolve!(Color, Red);
assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance"); assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance");
assert(blueInstance !is null, "Resolved blue instance to null"); assert(blueInstance !is null, "Resolved blue instance to null");
assert(redInstance !is null, "Resolved red instance to null"); assert(redInstance !is null, "Resolved red instance to null");
} }
// Test autowire of unqualified member typed by interface. // Test autowire of unqualified member typed by interface.
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!Spiders; container.register!Spiders;
container.register!(TestInterface, TestClass); container.register!(TestInterface, TestClass);
auto instance = container.resolve!Spiders; auto instance = container.resolve!Spiders;
assert(!(instance is null), "Container failed to autowire member by interface"); assert(!(instance is null), "Container failed to autowire member by interface");
} }
// Register existing registration // Register existing registration
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto firstRegistration = container.register!TestClass; auto firstRegistration = container.register!TestClass;
auto secondRegistration = container.register!TestClass; auto secondRegistration = container.register!TestClass;
assert(firstRegistration is secondRegistration, "Registering the same registration twice registers the dependencies twice."); assert(firstRegistration is secondRegistration, "Registering the same registration twice registers the dependencies twice.");
} }
// Register existing registration by supertype // Register existing registration by supertype
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto firstRegistration = container.register!(TestInterface, TestClass); auto firstRegistration = container.register!(TestInterface, TestClass);
auto secondRegistration = container.register!(TestInterface, TestClass); auto secondRegistration = container.register!(TestInterface, TestClass);
assert(firstRegistration is secondRegistration, "Registering the same registration by super type twice registers the dependencies twice."); assert(firstRegistration is secondRegistration, "Registering the same registration by super type twice registers the dependencies twice.");
} }
// Resolve dependency depending on itself // Resolve dependency depending on itself
unittest { unittest {
auto container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!Recursive; container.register!Recursive;
auto instance = container.resolve!Recursive; auto instance = container.resolve!Recursive;
assert(instance.recursive is instance, "Resolving dependency that depends on itself fails."); assert(instance.recursive is instance, "Resolving dependency that depends on itself fails.");
assert(instance.recursive.recursive is instance, "Resolving dependency that depends on itself fails."); assert(instance.recursive.recursive is instance, "Resolving dependency that depends on itself fails.");
} }
} }