1 /**
2  * Contains the implementation of the dependency container.
3  *
4  * Part of the Poodinis Dependency Injection framework.
5  *
6  * Authors:
7  *  Mike Bierlee, m.bierlee@lostmoment.com
8  * Copyright: 2014-2025 Mike Bierlee
9  * License:
10  *  This software is licensed under the terms of the MIT license.
11  *  The full terms of the license can be found in the LICENSE file.
12  */
13 
14 module poodinis.container;
15 
16 import poodinis.registration : Registration, singleInstance,
17     toConcreteTypeListString, initializeFactoryType;
18 import poodinis.autowire : AutowiredRegistration, AutowireInstantiationContext;
19 import poodinis.factory : ConstructorInjectingInstanceFactory;
20 import poodinis.valueinjection : ValueInjectionException;
21 import poodinis.altphobos : isFunction;
22 import poodinis.imports : createImportsString;
23 
24 import std.string : format;
25 import std.algorithm : canFind;
26 import std.traits : fullyQualifiedName, hasUDA, BaseTypeTuple;
27 import std.meta : AliasSeq;
28 
29 debug (poodinisVerbose) {
30     import std.stdio : writeln;
31 }
32 
33 /**
34  * Exception thrown when errors occur while resolving a type in a dependency container.
35  */
36 class ResolveException : Exception {
37     this(string message, TypeInfo resolveType) {
38         super(format("Exception while resolving type %s: %s", resolveType.toString(), message));
39     }
40 
41     this(Throwable cause, TypeInfo resolveType) {
42         super(format("Exception while resolving type %s", resolveType.toString()), cause);
43     }
44 }
45 
46 /**
47  * Exception thrown when errors occur while registering a type in a dependency container.
48  */
49 class RegistrationException : Exception {
50     this(string message, TypeInfo registrationType) {
51         super(format("Exception while registering type %s: %s",
52                 registrationType.toString(), message));
53     }
54 }
55 
56 /**
57  * Options which influence the process of registering dependencies
58  */
59 enum RegistrationOption {
60     none = 0,
61     /**
62      * Prevent a concrete type being registered on itself. With this option you will always need
63      * to use the supertype as the type of the dependency.
64      */
65     doNotAddConcreteTypeRegistration = 1 << 0,
66 }
67 
68 /**
69  * Options which influence the process of resolving dependencies
70  */
71 enum ResolveOption {
72     none = 0,
73     /**
74      * Registers the type you're trying to resolve before returning it.
75      * This essentially makes registration optional for resolving by concerete types.
76      * Resolinvg will still fail when trying to resolve a dependency by supertype.
77      */
78     registerBeforeResolving = 1 << 0,
79 
80     /**
81      * Does not throw a resolve exception when a type is not registered but will
82      * return null instead. If the type is an array, an empty array is returned instead.
83      */
84     noResolveException = 1 << 1
85 }
86 
87 /**
88  * Methods marked with this UDA within dependencies are called after that dependency
89  * is constructed by the dependency container.
90  *
91  * Multiple methods can be marked and will all be called after construction. The order in which
92  * methods are called is undetermined. Methods should have the signature void(void).
93  */
94 struct PostConstruct {
95 }
96 
97 /**
98  * Methods marked with this UDA within dependencies are called before the container
99  * loses the dependency's registration.
100  *
101  * This method is called when removeRegistration or clearAllRegistrations is called.
102  * It will also be called when the container's destructor is called.
103  */
104 struct PreDestroy {
105 }
106 
107 /**
108  * The dependency container maintains all dependencies registered with it.
109  *
110  * Dependencies registered by a container can be resolved as long as they are still registered with the container.
111  * Upon resolving a dependency, an instance is fetched according to a specific scope which dictates how instances of
112  * dependencies are created. Resolved dependencies will be autowired before being returned.
113  *
114  * In most cases you want to use a global singleton dependency container provided by getInstance() to manage all dependencies.
115  * You can still create new instances of this class for exceptional situations.
116  */
117 synchronized class DependencyContainer {
118     private Registration[][TypeInfo] registrations;
119 
120     private Registration[] autowireStack;
121 
122     private RegistrationOption persistentRegistrationOptions;
123     private ResolveOption persistentResolveOptions;
124 
125     ~this() {
126         clearAllRegistrations();
127     }
128 
129     /**
130      * Register a dependency by concrete class type.
131      *
132      * A dependency registered by concrete class type can only be resolved by concrete class type.
133      * No qualifiers can be used when resolving dependencies which are registered by concrete type.
134      *
135      * The default registration scope is "single instance" scope.
136      *
137      * Returns:
138      * A registration is returned which can be used to change the registration scope.
139      *
140      * Examples:
141      * Register and resolve a class by concrete type:
142      * ---
143      * class Cat : Animal { ... }
144      * container.register!Cat;
145      * ---
146      *
147      * See_Also: singleInstance, newInstance, existingInstance
148      */
149     Registration register(ConcreteType)(RegistrationOption options = RegistrationOption.none) {
150         return register!(ConcreteType, ConcreteType)(options);
151     }
152 
153     /**
154      * Register a dependency by super type.
155      *
156      * A dependency registered by super type can only be resolved by super type. A qualifier is typically
157      * used to resolve dependencies registered by super type.
158      *
159      * The default registration scope is "single instance" scope.
160      *
161      * Examples:
162      * Register and resolve by super type
163      * ---
164      * class Cat : Animal { ... }
165      * container.register!(Animal, Cat);
166      * ---
167      *
168      * See_Also: singleInstance, newInstance, existingInstance, RegistrationOption
169      */
170     Registration register(SuperType, ConcreteType:
171         SuperType)(RegistrationOption options = RegistrationOption.none)
172             if (!is(ConcreteType == struct)) {
173         TypeInfo registeredType = typeid(SuperType);
174         TypeInfo_Class concreteType = typeid(ConcreteType);
175 
176         debug (poodinisVerbose) {
177             writeln(format("DEBUG: Register type %s (as %s)",
178                     concreteType.toString(), registeredType.toString()));
179         }
180 
181         auto existingRegistration = getExistingRegistration(registeredType, concreteType);
182         if (existingRegistration) {
183             return existingRegistration;
184         }
185 
186         auto instanceFactory = new ConstructorInjectingInstanceFactory!ConcreteType(this);
187         auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType,
188             instanceFactory, this);
189         newRegistration.initializeFactoryType().singleInstance();
190 
191         static if (!is(SuperType == ConcreteType)) {
192             if (!hasOption(options, persistentRegistrationOptions,
193                     RegistrationOption.doNotAddConcreteTypeRegistration)) {
194                 auto concreteTypeRegistration = register!ConcreteType;
195                 concreteTypeRegistration.linkTo(newRegistration);
196             }
197         }
198 
199         registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
200         return newRegistration;
201     }
202 
203     private bool hasOption(OptionType)(OptionType options,
204         OptionType persistentOptions, OptionType option) {
205         return ((options | persistentOptions) & option) != 0;
206     }
207 
208     private OptionType buildFlags(OptionType)(OptionType[] options) {
209         OptionType flags;
210         foreach (option; options) {
211             flags |= option;
212         }
213         return flags;
214     }
215 
216     private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) {
217         auto existingCandidates = registrationType in registrations;
218         if (existingCandidates) {
219             return getRegistration(cast(Registration[])*existingCandidates, qualifierType);
220         }
221 
222         return null;
223     }
224 
225     private Registration getRegistration(Registration[] candidates, TypeInfo concreteType) {
226         foreach (existingRegistration; candidates) {
227             if (existingRegistration.instanceType == concreteType) {
228                 return existingRegistration;
229             }
230         }
231 
232         return null;
233     }
234 
235     /**
236      * Resolve dependencies.
237      *
238      * Dependencies can only resolved using this method if they are registered by concrete type or the only
239      * concrete type registered by super type.
240      *
241      * Resolved dependencies are automatically autowired before being returned.
242      *
243      * Returns:
244      * An instance is returned which is created according to the registration scope with which they are registered.
245      *
246      * Throws:
247      * ResolveException when type is not registered.
248      *
249      * Examples:
250      * Resolve dependencies registered by super type and concrete type:
251      * ---
252      * class Cat : Animal { ... }
253      * class Dog : Animal { ... }
254      *
255      * container.register!(Animal, Cat);
256      * container.register!Dog;
257      *
258      * container.resolve!Animal;
259      * container.resolve!Dog;
260      * ---
261      * You cannot resolve a dependency when it is registered by multiple super types:
262      * ---
263      * class Cat : Animal { ... }
264      * class Dog : Animal { ... }
265      *
266      * container.register!(Animal, Cat);
267      * container.register!(Animal, Dog);
268      *
269      * container.resolve!Animal; // Error: multiple candidates for type "Animal"
270      * container.resolve!Dog; // Error: No type is registered by concrete type "Dog", only by super type "Animal"
271      * ---
272      * You need to use the resolve method which allows you to specify a qualifier.
273      */
274     RegistrationType resolve(RegistrationType)(
275         ResolveOption resolveOptions = ResolveOption.none)
276             if (!is(RegistrationType == struct)) {
277         return resolve!(RegistrationType, RegistrationType)(resolveOptions);
278     }
279 
280     /**
281      * Resolve dependencies using a qualifier.
282      *
283      * Dependencies can only resolved using this method if they are registered by super type.
284      *
285      * Resolved dependencies are automatically autowired before being returned.
286      *
287      * Returns:
288      * An instance is returned which is created according to the registration scope with which they are registered.
289      *
290      * Throws:
291      * ResolveException when type is not registered or there are multiple candidates available for type.
292      *
293      * Examples:
294      * Resolve dependencies registered by super type:
295      * ---
296      * class Cat : Animal { ... }
297      * class Dog : Animal { ... }
298      *
299      * container.register!(Animal, Cat);
300      * container.register!(Animal, Dog);
301      *
302      * container.resolve!(Animal, Cat);
303      * container.resolve!(Animal, Dog);
304      * ---
305      */
306     QualifierType resolve(RegistrationType, QualifierType:
307         RegistrationType)(ResolveOption resolveOptions = ResolveOption.none)
308             if (!is(QualifierType == struct)) {
309         TypeInfo resolveType = typeid(RegistrationType);
310         TypeInfo qualifierType = typeid(QualifierType);
311 
312         debug (poodinisVerbose) {
313             writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
314         }
315 
316         auto candidates = resolveType in registrations;
317         if (!candidates) {
318             static if (is(typeof(typeid(QualifierType)) == TypeInfo_Class) && !__traits(isAbstractClass, QualifierType)) {
319                 if (hasOption(resolveOptions, persistentResolveOptions, ResolveOption
320                         .registerBeforeResolving)) {
321                     register!(RegistrationType, QualifierType)();
322                     return resolve!(RegistrationType, QualifierType)(resolveOptions);
323                 }
324             }
325 
326             if (hasOption(resolveOptions, persistentResolveOptions,
327                     ResolveOption.noResolveException)) {
328                 return null;
329             }
330 
331             throw new ResolveException("Type not registered.", resolveType);
332         }
333 
334         Registration registration = getQualifiedRegistration(resolveType,
335             qualifierType, cast(Registration[])*candidates);
336 
337         try {
338             QualifierType newInstance = resolveAutowiredInstance!QualifierType(registration);
339             callPostConstructors(newInstance);
340             return newInstance;
341         } catch (ValueInjectionException e) {
342             throw new ResolveException(e, resolveType);
343         }
344     }
345 
346     bool isRegistered(RegistrationType)() {
347         TypeInfo typeInfo = typeid(RegistrationType);
348         auto candidates = typeInfo in registrations;
349         return candidates !is null;
350     }
351 
352     private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration) {
353         QualifierType instance;
354         if (!(cast(Registration[]) autowireStack).canFind(registration)) {
355             autowireStack ~= cast(shared(Registration)) registration;
356             instance = cast(QualifierType) registration.getInstance(
357                 new AutowireInstantiationContext());
358             autowireStack = autowireStack[0 .. $ - 1];
359         } else {
360             auto autowireContext = new AutowireInstantiationContext();
361             autowireContext.autowireInstance = false;
362             instance = cast(QualifierType) registration.getInstance(autowireContext);
363         }
364         return instance;
365     }
366 
367     /**
368      * Resolve all dependencies registered to a super type.
369      *
370      * Returns:
371      * An array of autowired instances is returned. The order is undetermined.
372      *
373      * Examples:
374      * ---
375      * class Cat : Animal { ... }
376      * class Dog : Animal { ... }
377      *
378      * container.register!(Animal, Cat);
379      * container.register!(Animal, Dog);
380      *
381      * Animal[] animals = container.resolveAll!Animal;
382      * ---
383      */
384     RegistrationType[] resolveAll(RegistrationType)(
385         ResolveOption resolveOptions = ResolveOption.none) {
386         RegistrationType[] instances;
387         TypeInfo resolveType = typeid(RegistrationType);
388 
389         auto qualifiedRegistrations = resolveType in registrations;
390         if (!qualifiedRegistrations) {
391             if (hasOption(resolveOptions, persistentResolveOptions,
392                     ResolveOption.noResolveException)) {
393                 return [];
394             }
395 
396             throw new ResolveException("Type not registered.", resolveType);
397         }
398 
399         foreach (registration; cast(Registration[])*qualifiedRegistrations) {
400             instances ~= resolveAutowiredInstance!RegistrationType(registration);
401         }
402 
403         return instances;
404     }
405 
406     private Registration getQualifiedRegistration(TypeInfo resolveType,
407         TypeInfo qualifierType, Registration[] candidates) {
408         if (resolveType == qualifierType) {
409             if (candidates.length > 1) {
410                 string candidateList = candidates.toConcreteTypeListString();
411                 throw new ResolveException(
412                     "Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.",
413                     resolveType);
414             }
415 
416             return candidates[0];
417         }
418 
419         return getRegistration(candidates, qualifierType);
420     }
421 
422     private void callPostConstructors(Type)(Type instance) {
423         static foreach (memberName; __traits(allMembers, Type)) {
424             static foreach (overload; __traits(getOverloads, Type, memberName)) {
425                 static if (__traits(compiles, __traits(getProtection, overload))
426                     && __traits(getProtection, overload) == "public"
427                     && isFunction!overload
428                     && hasUDA!(overload, PostConstruct)) {
429                     __traits(getMember, instance, memberName)();
430                 }
431             }
432         }
433     }
434 
435     /**
436      * Clears all dependency registrations managed by this container.
437      */
438     void clearAllRegistrations() {
439         foreach (registrationsOfType; registrations) {
440             callPreDestructorsOfRegistrations(registrationsOfType);
441         }
442         registrations.destroy();
443     }
444 
445     /**
446      * Removes a registered dependency by type.
447      *
448      * A dependency can be removed either by super type or concrete type, depending on how they are registered.
449      *
450      * Examples:
451      * ---
452      * container.removeRegistration!Animal;
453      * ---
454      */
455     void removeRegistration(RegistrationType)() {
456         auto registrationsOfType = *(typeid(RegistrationType) in registrations);
457         callPreDestructorsOfRegistrations(registrationsOfType);
458         registrations.remove(typeid(RegistrationType));
459     }
460 
461     private void callPreDestructorsOfRegistrations(shared(Registration[]) registrations) {
462         foreach (registration; registrations) {
463             Registration unsharedRegistration = cast(Registration) registration;
464             if (unsharedRegistration.preDestructor !is null) {
465                 unsharedRegistration.preDestructor()();
466             }
467         }
468     }
469 
470     /**
471      * Apply persistent registration options which will be used everytime register() is called.
472      */
473     void setPersistentRegistrationOptions(RegistrationOption options) {
474         persistentRegistrationOptions = options;
475     }
476 
477     /**
478      * Unsets all applied persistent registration options
479      */
480     void unsetPersistentRegistrationOptions() {
481         persistentRegistrationOptions = RegistrationOption.none;
482     }
483 
484     /**
485      * Apply persistent resolve options which will be used everytime resolve() is called.
486      */
487     void setPersistentResolveOptions(ResolveOption options) {
488         persistentResolveOptions = options;
489     }
490 
491     /**
492      * Unsets all applied persistent resolve options
493      */
494     void unsetPersistentResolveOptions() {
495         persistentResolveOptions = ResolveOption.none;
496     }
497 
498 }
499 
500 version (unittest)  :  //
501 
502 import poodinis;
503 import poodinis.testclasses;
504 import poodinis.foreigndependencies;
505 
506 import std.exception;
507 import core.thread;
508 
509 // Test register concrete type
510 unittest {
511     auto container = new shared DependencyContainer();
512     auto registration = container.register!TestClass;
513     assert(registration.registeredType == typeid(TestClass),
514         "Type of registered type not the same");
515 }
516 
517 // Test resolve registered type
518 unittest {
519     auto container = new shared DependencyContainer();
520     container.register!TestClass;
521     TestClass actualInstance = container.resolve!TestClass;
522     assert(actualInstance !is null, "Resolved type is null");
523     assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected");
524 }
525 
526 // Test register interface
527 unittest {
528     auto container = new shared DependencyContainer();
529     container.register!(TestInterface, TestClass);
530     TestInterface actualInstance = container.resolve!TestInterface;
531     assert(actualInstance !is null, "Resolved type is null");
532     assert(cast(TestInterface) actualInstance,
533         "Resolved class is not the same type as expected");
534 }
535 
536 // Test resolve non-registered type
537 unittest {
538     auto container = new shared DependencyContainer();
539     assertThrown!ResolveException(container.resolve!TestClass,
540         "Resolving non-registered type does not fail");
541 }
542 
543 // Test clear registrations
544 unittest {
545     auto container = new shared DependencyContainer();
546     container.register!TestClass;
547     container.clearAllRegistrations();
548     assertThrown!ResolveException(container.resolve!TestClass,
549         "Resolving cleared type does not fail");
550 }
551 
552 // Test resolve single instance for type
553 unittest {
554     auto container = new shared DependencyContainer();
555     container.register!TestClass.singleInstance();
556     auto instance1 = container.resolve!TestClass;
557     auto instance2 = container.resolve!TestClass;
558     assert(instance1 is instance2,
559         "Resolved instance from single instance scope is not the each time it is resolved");
560 }
561 
562 // Test resolve new instance for type
563 unittest {
564     auto container = new shared DependencyContainer();
565     container.register!TestClass.newInstance();
566     auto instance1 = container.resolve!TestClass;
567     auto instance2 = container.resolve!TestClass;
568     assert(instance1 !is instance2,
569         "Resolved instance from new instance scope is the same each time it is resolved");
570 }
571 
572 // Test resolve existing instance for type
573 unittest {
574     auto container = new shared DependencyContainer();
575     auto expectedInstance = new TestClass();
576     container.register!TestClass.existingInstance(expectedInstance);
577     auto actualInstance = container.resolve!TestClass;
578     assert(expectedInstance is actualInstance,
579         "Resolved instance from existing instance scope is not the same as the registered instance");
580 }
581 
582 // Test creating instance via custom initializer on resolve
583 unittest {
584     auto container = new shared DependencyContainer();
585     auto expectedInstance = new TestClass();
586     container.register!TestClass.initializedBy({ return expectedInstance; });
587     auto actualInstance = container.resolve!TestClass;
588     assert(expectedInstance is actualInstance,
589         "Resolved instance does not come from the custom initializer");
590 }
591 
592 // Test creating instance via initializedBy creates new instance every time
593 unittest {
594     auto container = new shared DependencyContainer();
595     container.register!TestClass.initializedBy({ return new TestClass(); });
596     auto firstInstance = container.resolve!TestClass;
597     auto secondInstance = container.resolve!TestClass;
598     assert(firstInstance !is secondInstance, "Resolved instance are not different instances");
599 }
600 
601 // Test creating instance via initializedOnceBy creates a singleton instance
602 unittest {
603     auto container = new shared DependencyContainer();
604     container.register!TestClass.initializedOnceBy({ return new TestClass(); });
605     auto firstInstance = container.resolve!TestClass;
606     auto secondInstance = container.resolve!TestClass;
607     assert(firstInstance is secondInstance, "Resolved instance are different instances");
608 }
609 
610 // Test autowire resolved instances
611 unittest {
612     auto container = new shared DependencyContainer();
613     container.register!AutowiredClass;
614     container.register!ComponentClass;
615     auto componentInstance = container.resolve!ComponentClass;
616     auto autowiredInstance = container.resolve!AutowiredClass;
617     assert(componentInstance.autowiredClass is autowiredInstance,
618         "Member is not autwired upon resolving");
619 }
620 
621 // Test circular autowiring
622 unittest {
623     auto container = new shared DependencyContainer();
624     container.register!ComponentMouse;
625     container.register!ComponentCat;
626     auto mouse = container.resolve!ComponentMouse;
627     auto cat = container.resolve!ComponentCat;
628     assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat,
629         "Circular dependencies should be autowirable");
630 }
631 
632 // Test remove registration
633 unittest {
634     auto container = new shared DependencyContainer();
635     container.register!TestClass;
636     container.removeRegistration!TestClass;
637     assertThrown!ResolveException(container.resolve!TestClass);
638 }
639 
640 // Test autowiring does not autowire member where instance is non-null
641 unittest {
642     auto container = new shared DependencyContainer();
643     auto existingA = new AutowiredClass();
644     auto existingB = new ComponentClass();
645     existingB.autowiredClass = existingA;
646 
647     container.register!AutowiredClass;
648     container.register!ComponentClass.existingInstance(existingB);
649     auto resolvedA = container.resolve!AutowiredClass;
650     auto resolvedB = container.resolve!ComponentClass;
651 
652     assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA,
653         "Autowiring shouldn't rewire member when it is already wired to an instance");
654 }
655 
656 // Test autowiring circular dependency by third-degree
657 unittest {
658     auto container = new shared DependencyContainer();
659     container.register!Eenie;
660     container.register!Meenie;
661     container.register!Moe;
662 
663     auto eenie = container.resolve!Eenie;
664 
665     assert(eenie.meenie.moe.eenie is eenie,
666         "Autowiring third-degree circular dependency failed");
667 }
668 
669 // Test autowiring deep circular dependencies
670 unittest {
671     auto container = new shared DependencyContainer();
672     container.register!Ittie;
673     container.register!Bittie;
674     container.register!Bunena;
675 
676     auto ittie = container.resolve!Ittie;
677 
678     assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed.");
679 }
680 
681 // Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
682 unittest {
683     auto container = new shared DependencyContainer();
684     container.register!Ittie.newInstance();
685     container.register!Bittie.newInstance();
686     container.register!Bunena.newInstance();
687 
688     auto ittie = container.resolve!Ittie;
689 
690     assert(ittie.bittie.banana.bittie.banana is null,
691         "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
692 }
693 
694 // Test autowiring type registered by interface
695 unittest {
696     auto container = new shared DependencyContainer();
697     container.register!Bunena;
698     container.register!Bittie;
699     container.register!(SuperInterface, SuperImplementation);
700 
701     SuperImplementation superInstance = cast(SuperImplementation) container
702         .resolve!SuperInterface;
703 
704     assert(!(superInstance.banana is null),
705         "Instance which was resolved by interface type was not autowired.");
706 }
707 
708 // Test reusing a container after clearing all registrations
709 unittest {
710     auto container = new shared DependencyContainer();
711     container.register!Banana;
712     container.clearAllRegistrations();
713     try {
714         container.resolve!Banana;
715     } catch (ResolveException e) {
716         container.register!Banana;
717         return;
718     }
719     assert(false);
720 }
721 
722 // Test register multiple concrete classess to same interface type
723 unittest {
724     auto container = new shared DependencyContainer();
725     container.register!(Color, Blue);
726     container.register!(Color, Red);
727 }
728 
729 // Test removing all registrations for type with multiple registrations.
730 unittest {
731     auto container = new shared DependencyContainer();
732     container.register!(Color, Blue);
733     container.register!(Color, Red);
734     container.removeRegistration!Color;
735 }
736 
737 // Test registering same registration again
738 unittest {
739     auto container = new shared DependencyContainer();
740     auto firstRegistration = container.register!(Color, Blue);
741     auto secondRegistration = container.register!(Color, Blue);
742 
743     assert(firstRegistration is secondRegistration,
744         "First registration is not the same as the second of equal types");
745 }
746 
747 // Test resolve registration with multiple qualifiers
748 unittest {
749     auto container = new shared DependencyContainer();
750     container.register!(Color, Blue);
751     container.register!(Color, Red);
752     try {
753         container.resolve!Color;
754     } catch (ResolveException e) {
755         return;
756     }
757     assert(false);
758 }
759 
760 // Test resolve registration with multiple qualifiers using a qualifier
761 unittest {
762     auto container = new shared DependencyContainer();
763     container.register!(Color, Blue);
764     container.register!(Color, Red);
765     auto blueInstance = container.resolve!(Color, Blue);
766     auto redInstance = container.resolve!(Color, Red);
767 
768     assert(blueInstance !is redInstance,
769         "Resolving type with multiple, different registrations yielded the same instance");
770     assert(blueInstance !is null, "Resolved blue instance to null");
771     assert(redInstance !is null, "Resolved red instance to null");
772 }
773 
774 // Test autowire of unqualified member typed by interface.
775 unittest {
776     auto container = new shared DependencyContainer();
777     container.register!Spiders;
778     container.register!(TestInterface, TestClass);
779 
780     auto instance = container.resolve!Spiders;
781 
782     assert(!(instance is null), "Container failed to autowire member by interface");
783 }
784 
785 // Register existing registration
786 unittest {
787     auto container = new shared DependencyContainer();
788 
789     auto firstRegistration = container.register!TestClass;
790     auto secondRegistration = container.register!TestClass;
791 
792     assert(firstRegistration is secondRegistration,
793         "Registering the same registration twice registers the dependencies twice.");
794 }
795 
796 // Register existing registration by supertype
797 unittest {
798     auto container = new shared DependencyContainer();
799 
800     auto firstRegistration = container.register!(TestInterface, TestClass);
801     auto secondRegistration = container.register!(TestInterface, TestClass);
802 
803     assert(firstRegistration is secondRegistration,
804         "Registering the same registration by super type twice registers the dependencies twice.");
805 }
806 
807 // Resolve dependency depending on itself
808 unittest {
809     auto container = new shared DependencyContainer();
810     container.register!Recursive;
811 
812     auto instance = container.resolve!Recursive;
813 
814     assert(instance.recursive is instance, "Resolving dependency that depends on itself fails.");
815     assert(instance.recursive.recursive is instance,
816         "Resolving dependency that depends on itself fails.");
817 }
818 
819 // Test autowire stack pop-back
820 unittest {
821     auto container = new shared DependencyContainer();
822     container.register!Moolah;
823     container.register!Wants.newInstance();
824     container.register!John;
825 
826     container.resolve!Wants;
827     auto john = container.resolve!John;
828 
829     assert(john.wants.moolah !is null, "Autowire stack did not clear entries properly");
830 }
831 
832 // Test resolving registration registered in different thread
833 unittest {
834     auto container = new shared DependencyContainer();
835 
836     auto thread = new Thread(delegate() { container.register!TestClass; });
837     thread.start();
838     thread.join();
839 
840     container.resolve!TestClass;
841 }
842 
843 // Test resolving instance previously resolved in different thread
844 unittest {
845     auto container = new shared DependencyContainer();
846     shared(TestClass) actualTestClass;
847 
848     container.register!TestClass;
849 
850     auto thread = new Thread(delegate() {
851         actualTestClass = cast(shared(TestClass)) container.resolve!TestClass;
852     });
853     thread.start();
854     thread.join();
855 
856     shared(TestClass) expectedTestClass = cast(shared(TestClass)) container.resolve!TestClass;
857 
858     assert(expectedTestClass is actualTestClass,
859         "Instance resolved in main thread is not the one resolved in thread");
860 }
861 
862 // Test registering type with option doNotAddConcreteTypeRegistration
863 unittest {
864     auto container = new shared DependencyContainer();
865     container.register!(TestInterface,
866         TestClass)(RegistrationOption.doNotAddConcreteTypeRegistration);
867 
868     auto firstInstance = container.resolve!TestInterface;
869     assertThrown!ResolveException(container.resolve!TestClass);
870 }
871 
872 // Test registering conrete type with registration option doNotAddConcreteTypeRegistration does nothing
873 unittest {
874     auto container = new shared DependencyContainer();
875     container.register!TestClass(RegistrationOption.doNotAddConcreteTypeRegistration);
876     container.resolve!TestClass;
877 }
878 
879 // Test registering type will register by contrete type by default
880 unittest {
881     auto container = new shared DependencyContainer();
882     container.register!(TestInterface, TestClass);
883 
884     auto firstInstance = container.resolve!TestInterface;
885     auto secondInstance = container.resolve!TestClass;
886 
887     assert(firstInstance is secondInstance);
888 }
889 
890 // Test resolving all registrations to an interface
891 unittest {
892     auto container = new shared DependencyContainer();
893     container.register!(Color, Blue);
894     container.register!(Color, Red);
895 
896     auto colors = container.resolveAll!Color;
897 
898     assert(colors.length == 2, "resolveAll did not yield all instances of interface type");
899 }
900 
901 // Test autowiring instances resolved in array
902 unittest {
903     auto container = new shared DependencyContainer();
904     container.register!UnrelatedClass;
905     container.register!(TestInterface, TestClassDeux);
906 
907     auto instances = container.resolveAll!TestInterface;
908     auto instance = cast(TestClassDeux) instances[0];
909 
910     assert(instance.unrelated !is null);
911 }
912 
913 // Test set persistent registration options
914 unittest {
915     auto container = new shared DependencyContainer();
916     container.setPersistentRegistrationOptions(
917         RegistrationOption.doNotAddConcreteTypeRegistration);
918     container.register!(TestInterface, TestClass);
919     assertThrown!ResolveException(container.resolve!TestClass);
920 }
921 
922 // Test unset persistent registration options
923 unittest {
924     auto container = new shared DependencyContainer();
925     container.setPersistentRegistrationOptions(
926         RegistrationOption.doNotAddConcreteTypeRegistration);
927     container.unsetPersistentRegistrationOptions();
928     container.register!(TestInterface, TestClass);
929     container.resolve!TestClass;
930 }
931 
932 // Test registration when resolving
933 unittest {
934     auto container = new shared DependencyContainer();
935     container.resolve!(TestInterface, TestClass)(ResolveOption.registerBeforeResolving);
936     container.resolve!TestClass;
937 }
938 
939 // Test set persistent resolve options
940 unittest {
941     auto container = new shared DependencyContainer();
942     container.setPersistentResolveOptions(ResolveOption.registerBeforeResolving);
943     container.resolve!TestClass;
944 }
945 
946 // Test unset persistent resolve options
947 unittest {
948     auto container = new shared DependencyContainer();
949     container.setPersistentResolveOptions(ResolveOption.registerBeforeResolving);
950     container.unsetPersistentResolveOptions();
951     assertThrown!ResolveException(container.resolve!TestClass);
952 }
953 
954 // Test ResolveOption registerBeforeResolving fails for interfaces
955 unittest {
956     auto container = new shared DependencyContainer();
957     assertThrown!ResolveException(
958         container.resolve!TestInterface(ResolveOption.registerBeforeResolving));
959 }
960 
961 // Test ResolveOption noResolveException does not throw
962 unittest {
963     auto container = new shared DependencyContainer();
964     auto instance = container.resolve!TestInterface(ResolveOption.noResolveException);
965     assert(instance is null);
966 }
967 
968 // ResolveOption noResolveException does not throw for resolveAll
969 unittest {
970     auto container = new shared DependencyContainer();
971     auto instances = container.resolveAll!TestInterface(ResolveOption.noResolveException);
972     assert(instances.length == 0);
973 }
974 
975 // Test autowired, constructor injected class
976 unittest {
977     auto container = new shared DependencyContainer();
978     container.register!Red;
979     container.register!Moolah;
980     container.register!Cocktail;
981 
982     auto instance = container.resolve!Cocktail;
983 
984     assert(instance !is null);
985     assert(instance.moolah is container.resolve!Moolah);
986     assert(instance.red is container.resolve!Red);
987 }
988 
989 // Test autowired, constructor injected class where constructor argument is templated
990 unittest {
991     auto container = new shared DependencyContainer();
992     container.register!PieChart;
993     container.register!(TemplatedComponent!PieChart);
994     container.register!(ClassWithTemplatedConstructorArg!PieChart);
995     auto instance = container.resolve!(ClassWithTemplatedConstructorArg!PieChart);
996 
997     assert(instance !is null);
998     assert(instance.dependency !is null);
999     assert(instance.dependency.instance !is null);
1000 }
1001 
1002 // Test injecting constructor with super-type parameter
1003 unittest {
1004     auto container = new shared DependencyContainer();
1005     container.register!Wallpaper;
1006     container.register!(Color, Blue);
1007 
1008     auto instance = container.resolve!Wallpaper;
1009     assert(instance !is null);
1010     assert(instance.color is container.resolve!Blue);
1011 }
1012 
1013 // Test prevention of circular dependencies during constructor injection
1014 unittest {
1015     auto container = new shared DependencyContainer();
1016     container.register!Pot;
1017     container.register!Kettle;
1018 
1019     assertThrown!InstanceCreationException(container.resolve!Pot);
1020 }
1021 
1022 // Test prevention of transitive circular dependencies during constructor injection
1023 unittest {
1024     auto container = new shared DependencyContainer();
1025     container.register!Rock;
1026     container.register!Paper;
1027     container.register!Scissors;
1028 
1029     assertThrown!InstanceCreationException(container.resolve!Rock);
1030 }
1031 
1032 // Test injection of foreign dependency in constructor
1033 unittest {
1034     auto container = new shared DependencyContainer();
1035     container.register!Ola;
1036     container.register!Hello;
1037     container.resolve!Hello;
1038 }
1039 
1040 // Test PostConstruct method is called after resolving a dependency
1041 unittest {
1042     auto container = new shared DependencyContainer();
1043     container.register!PostConstructionDependency;
1044 
1045     auto instance = container.resolve!PostConstructionDependency;
1046     assert(instance.postConstructWasCalled == true);
1047 }
1048 
1049 // Test PostConstruct of base type is called
1050 unittest {
1051     auto container = new shared DependencyContainer();
1052     container.register!ChildOfPostConstruction;
1053 
1054     auto instance = container.resolve!ChildOfPostConstruction;
1055     assert(instance.postConstructWasCalled == true);
1056 }
1057 
1058 // Test PostConstruct of class implementing interface is not called
1059 unittest {
1060     auto container = new shared DependencyContainer();
1061     container.register!ButThereWontBe;
1062 
1063     auto instance = container.resolve!ButThereWontBe;
1064     assert(instance.postConstructWasCalled == false);
1065 }
1066 
1067 // Test postconstruction happens after autowiring and value injection
1068 unittest {
1069     auto container = new shared DependencyContainer();
1070     container.register!(ValueInjector!int, PostConstructingIntInjector);
1071     container.register!PostConstructionDependency;
1072     container.register!PostConstructWithAutowiring;
1073     auto instance = container.resolve!PostConstructWithAutowiring;
1074 }
1075 
1076 // Test PreDestroy is called when removing a registration
1077 unittest {
1078     auto container = new shared DependencyContainer();
1079     container.register!PreDestroyerOfFates;
1080     auto instance = container.resolve!PreDestroyerOfFates;
1081     container.removeRegistration!PreDestroyerOfFates;
1082     assert(instance.preDestroyWasCalled == true);
1083 }
1084 
1085 // Test PreDestroy is called when removing all registrations
1086 unittest {
1087     auto container = new shared DependencyContainer();
1088     container.register!PreDestroyerOfFates;
1089     auto instance = container.resolve!PreDestroyerOfFates;
1090     container.clearAllRegistrations();
1091     assert(instance.preDestroyWasCalled == true);
1092 }
1093 
1094 // Test PreDestroy is called when the container is destroyed
1095 unittest {
1096     auto container = new shared DependencyContainer();
1097     container.register!PreDestroyerOfFates;
1098     auto instance = container.resolve!PreDestroyerOfFates;
1099     container.destroy();
1100 
1101     assert(instance.preDestroyWasCalled == true);
1102 }
1103 
1104 // Test register class by ancestor type
1105 unittest {
1106     auto container = new shared DependencyContainer();
1107     container.register!(Grandma, Kid);
1108     auto instance = container.resolve!Grandma;
1109 
1110     assert(instance !is null);
1111 }
1112 
1113 // Test autowiring classes with recursive template parameters
1114 unittest {
1115     auto container = new shared DependencyContainer();
1116     container.register!CircularTemplateComponentA;
1117     container.register!CircularTemplateComponentB;
1118 
1119     auto componentA = container.resolve!CircularTemplateComponentA;
1120     auto componentB = container.resolve!CircularTemplateComponentB;
1121 
1122     assert(componentA !is null);
1123     assert(componentB !is null);
1124 
1125     assert(componentA.instance is componentB);
1126     assert(componentB.instance is componentA);
1127 }
1128 
1129 // Test autowiring class where a method is marked with @Autowire does nothing
1130 unittest {
1131     // It should also not show deprecation warning:
1132     // Deprecation: `__traits(getAttributes)` may only be used for individual functions, not overload sets such as: `lala`
1133     //      the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from
1134 
1135     auto container = new shared DependencyContainer();
1136     container.register!AutowiredMethod;
1137     auto instance = container.resolve!AutowiredMethod;
1138 
1139     assert(instance !is null);
1140     assert(instance.lala == 42);
1141     assert(instance.lala(77) == 77);
1142 }
1143 
1144 // Test autowiring using @Autowire attribute
1145 unittest {
1146     auto container = new shared DependencyContainer();
1147     container.register!ComponentA;
1148     container.register!WithAutowireAttribute;
1149 
1150     auto instance = container.resolve!WithAutowireAttribute;
1151     assert(instance.componentA is container.resolve!ComponentA);
1152 }