reworkted Singleton / DependencyFactory unit test pass

This commit is contained in:
Fischlurch 2013-10-20 00:34:21 +02:00
parent 24792c1f19
commit b225120d09
5 changed files with 58 additions and 140 deletions

View file

@ -40,8 +40,8 @@ This code is heavily inspired by
#include "lib/meta/duck-detector.hpp" ////TODO move in separate header
#include <boost/scoped_ptr.hpp> ////TODO move in separate header
#include <boost/noncopyable.hpp> ////TODO move in separate header
#include <boost/static_assert.hpp> ////TODO move in separate header
#include <boost/utility/enable_if.hpp> ////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<class TYPE>
struct Use4Test
struct Depend4Test
: boost::noncopyable
{
typedef typename ServiceInterface<TYPE>::Type Interface;
Use4Test()
{
Depend<Interface>::injectReplacement (new TYPE);
}
boost::scoped_ptr<TYPE> mock_;
Interface* shadowedOriginal_;
~Use4Test()
Depend4Test()
: mock_(new TYPE)
, shadowedOriginal_(Depend<Interface>::injectReplacement (mock_.get()))
{ }
~Depend4Test()
{
Depend<Interface>::dropReplacement();
ENSURE (mock_);
Depend<Interface>::injectReplacement (shadowedOriginal_);
}
};

View file

@ -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)
{

View file

@ -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<class TAR>
void takeOwnership (TAR*);
template<class TAR>
void shaddow (TAR* volatile & activeInstance, TAR* replacement);
template<class TAR>
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<class X>
inline void
releaseOnHeap (void* o)
{
if (!o) return;
X* instance = static_cast<X*> (o);
delete instance;
}
}
template<class TAR>
void
DependencyFactory::takeOwnership (TAR* newInstance)
{
scheduleDestruction (newInstance, &releaseOnHeap<TAR>);
}
/**
* set up a temporary replacement, allowing to restore the original later
* @param activeInstance
* @param replacement
*/
template<class TAR>
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<class TAR>
void
DependencyFactory::restore (TAR* volatile & activeInstance)
{
deconfigure (activeInstance);
activeInstance = NULL;
}
} // namespace lib
#endif

View file

@ -231,6 +231,25 @@ return: 0
END
TEST "Dependency injection" DependencyFactory_test <<END
out-lit: ctor TargetObj(0) successful
out-lit: dtor ~TargetObj(0) successful
out-lit: ctor TargetObj(1) successful
out-lit: ctor TargetObj(2) successful
out-lit: dtor ~TargetObj(2) successful
out-lit: ctor TargetObj(3) successful
out-lit: ctor TargetObj(4) successful
out-lit: ctor TargetObj(5) successful
out-lit: dtor ~TargetObj(5) successful
out-lit: ctor TargetObj(6) successful
out-lit: dtor ~TargetObj(6) successful
out-lit: dtor ~TargetObj(4) successful
out-lit: dtor ~TargetObj(3) successful
out-lit: dtor ~TargetObj(1) successful
return: 0
END
TEST "A Digxel (numeric component)" Digxel_test <<END
out-lit: empty____## +0.0 ##
out-lit: value____##-88.8 ##

View file

@ -65,7 +65,9 @@ namespace test{
struct SubSub
: Sub
{ };
{
typedef Sub ServiceInterface;
};
struct SubSubSub
: SubSub
@ -202,17 +204,13 @@ namespace test{
Sub& original = genericAccessor();
uint oID = original.instanceID_;
GenericAccessor::injectReplacement (new SubSubSub);
SubSubSub mockObject;
Sub* shaddowedOriginal = GenericAccessor::injectReplacement (&mockObject);
Sub& replacement = genericAccessor();
uint repID = replacement.instanceID_;
CHECK (!INSTANCEOF (SubSubSub, &original));
CHECK ( INSTANCEOF (SubSubSub, &replacement));
CHECK ( isSameObject (replacement, mockObject));
CHECK (!isSameObject (original, replacement));
CHECK (oID != repID);
CHECK (oID == original.instanceID_);
CHECK ( isSameObject (original, *shaddowedOriginal));
Depend<SubSub> special;
Depend<SubSubSub> 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<SubSub> withinThisScope;
Depend4Test<SubSub> withinThisScope;
Sub& replacement = genericAccessor();
uint repID = replacement.instanceID_;