diff --git a/src/lib/depend.hpp b/src/lib/depend.hpp index f9c90fa61..3a36c6d4d 100644 --- a/src/lib/depend.hpp +++ b/src/lib/depend.hpp @@ -40,8 +40,8 @@ This code is heavily inspired by #include "lib/meta/duck-detector.hpp" ////TODO move in separate header +#include ////TODO move in separate header #include ////TODO move in separate header -#include ////TODO move in separate header #include ////TODO move in separate header @@ -151,29 +151,24 @@ namespace lib { instance = NULL; } - /** temporarily shadow the service instance with the given replacement. + /** temporarily replace the service instance. * The purpose of this operation is to support unit testing. - * @throw error::State in case there is already an installed replacement - * @param mock heap allocated instance of the replacement (mock). + * @param mock reference to an existing service instance (mock). + * @return reference to the currently active service instance. * @warning not threadsafe. Same considerations as for \c shutdown() apply - * @note ownership of mock will be transferred; the mock instance - * will be destroyed automatically when deconfigured. + * @remark the replacement is not actively managed by the DependencyFactory, + * it remains in ownership of the calling client (unit test). Typically + * this test will keep the returned original service reference and + * care for restoring the original state when done. + * @see Depend4Test scoped object for automated test mock injection */ - static void + static SI* injectReplacement (SI* mock) { - REQUIRE (mock); - factory.takeOwnership (mock); // EX_SANE - SyncLock guard; - factory.shaddow (instance, mock); // EX_FREE - } - - static void - dropReplacement() - { - SyncLock guard; - factory.restore (instance); // EX_FREE + SI* currentInstance = instance; + instance = mock; + return currentInstance; } }; @@ -243,19 +238,23 @@ namespace lib { * it needs to expose a typedef \c ServiceInterface */ template - struct Use4Test + struct Depend4Test : boost::noncopyable { typedef typename ServiceInterface::Type Interface; - Use4Test() - { - Depend::injectReplacement (new TYPE); - } + boost::scoped_ptr mock_; + Interface* shadowedOriginal_; - ~Use4Test() + Depend4Test() + : mock_(new TYPE) + , shadowedOriginal_(Depend::injectReplacement (mock_.get())) + { } + + ~Depend4Test() { - Depend::dropReplacement(); + ENSURE (mock_); + Depend::injectReplacement (shadowedOriginal_); } }; diff --git a/src/lib/dependency-factory.cpp b/src/lib/dependency-factory.cpp index 7153fbda0..6a3824078 100644 --- a/src/lib/dependency-factory.cpp +++ b/src/lib/dependency-factory.cpp @@ -82,43 +82,10 @@ namespace lib { bool AutoDestructor::shutdownLock = false; - - - class TemporaryShadowedInstanceFactory - : boost::noncopyable - { - typedef DependencyFactory::InstanceConstructor Ctor; - - void* originalInstance_; - Ctor originalCtor_; - - public: - TemporaryShadowedInstanceFactory(void* instance, Ctor ctor) - : originalInstance_(instance) - , originalCtor_(ctor) - { } - - void* - constructorInvocation() - { - REQUIRE (originalCtor_); - - if (!originalInstance_) - return originalCtor_(); - - void* existingInstance = originalInstance_; - originalInstance_ = NULL; - return existingInstance; - } - }; - } - /** */ - - /** */ void DependencyFactory::deconfigure (void* existingInstance) @@ -126,6 +93,12 @@ namespace lib { AutoDestructor::kill (existingInstance); } + /** hook to install a deleter function to clean up a service object. + * The standard constructor function uses this hook to schedule the + * destructor invocation on application shutdown; custom constructors + * are free to use this mechanism (or care for clean-up otherwise) + * @see lib::DelStash + */ void DependencyFactory::scheduleDestruction (void* object, KillFun customDeleter) { diff --git a/src/lib/dependency-factory.hpp b/src/lib/dependency-factory.hpp index e59d7be13..26ef30178 100644 --- a/src/lib/dependency-factory.hpp +++ b/src/lib/dependency-factory.hpp @@ -30,7 +30,6 @@ #include "lib/nobug-init.hpp" #include "lib/error.hpp" -//#include "lib/sync-classlock.hpp" namespace lib { @@ -98,26 +97,6 @@ namespace lib { void deconfigure (void* existingInstance); - - template - void takeOwnership (TAR*); - - - template - void shaddow (TAR* volatile & activeInstance, TAR* replacement); - - template - void restore (TAR* volatile & activeInstance); - - - - - /** hook to install a deleter function to clean up a service object. - * The standard constructor function uses this hook to schedule the - * destructor invocation on application shutdown; custom constructors - * are free to use this mechanism (or care for clean-up otherwise) - * @see lib::DelStash - */ static void scheduleDestruction (void*, KillFun); @@ -206,56 +185,6 @@ namespace lib { }; - namespace { - /** helper: destroy heap allocated object. - * This deleter function is used to clean-up - * a heap allocated mock object, which was installed - * as a temporary replacement for some service, - * typically during an unit test - */ - template - inline void - releaseOnHeap (void* o) - { - if (!o) return; - X* instance = static_cast (o); - delete instance; - } - } - - - template - void - DependencyFactory::takeOwnership (TAR* newInstance) - { - scheduleDestruction (newInstance, &releaseOnHeap); - } - - /** - * set up a temporary replacement, allowing to restore the original later - * @param activeInstance - * @param replacement - */ - template - void - DependencyFactory::shaddow (TAR* volatile & activeInstance, TAR* replacement) - { - ctorFunction_ = 0; /////TODO how to implement a functor without explicitly allocated storage?? - activeInstance = replacement; - } - - /** - * disable and destroy temporary shadowing instance and restore the dormant original instance - * @param activeInstance - */ - template - void - DependencyFactory::restore (TAR* volatile & activeInstance) - { - deconfigure (activeInstance); - activeInstance = NULL; - } - } // namespace lib #endif diff --git a/tests/40core.tests b/tests/40core.tests index ed127f57f..e46c259df 100644 --- a/tests/40core.tests +++ b/tests/40core.tests @@ -231,6 +231,25 @@ return: 0 END +TEST "Dependency injection" DependencyFactory_test < special; Depend custom; @@ -220,7 +218,7 @@ namespace test{ CHECK(!isSameObject (replacement, special() )); CHECK(!isSameObject (replacement, custom() )); - GenericAccessor::dropReplacement(); + GenericAccessor::injectReplacement (shaddowedOriginal); Sub& nextFetch = genericAccessor(); CHECK (isSameObject (original, nextFetch)); @@ -237,7 +235,7 @@ namespace test{ uint oID = original.instanceID_; { - Use4Test withinThisScope; + Depend4Test withinThisScope; Sub& replacement = genericAccessor(); uint repID = replacement.instanceID_;