reshape the management interface

now using static functions; which simplifies building
a scoped object to install a mock automatically within
unit tests.
This commit is contained in:
Fischlurch 2013-10-18 20:15:29 +02:00
parent 7000a40602
commit 78c7036678
3 changed files with 115 additions and 22 deletions

View file

@ -38,6 +38,13 @@ This code is heavily inspired by
#include "lib/sync-classlock.hpp"
#include "lib/dependency-factory.hpp"
#include "lib/meta/duck-detector.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
namespace lib {
/**
@ -86,7 +93,20 @@ namespace lib {
return *instance;
}
void
typedef DependencyFactory::InstanceConstructor Constructor;
Depend (Constructor ctor = buildSingleton<SI>())
{
factory.installConstructorFunction (ctor);
}
// standard copy operations applicable
/* === Management / Test support interface === */
static void
shutdown()
{
SyncLock guard;
@ -95,7 +115,7 @@ namespace lib {
instance = NULL;
}
void
static void
injectReplacement (SI* mock)
{
REQUIRE (mock);
@ -106,22 +126,12 @@ namespace lib {
instance = mock;
}
void
static void
dropReplacement()
{
SyncLock guard;
factory.restore (instance); // EX_FREE
factory.restore (instance); // EX_FREE
}
typedef DependencyFactory::InstanceConstructor Constructor;
Depend (Constructor ctor = buildSingleton<SI>())
{
factory.installConstructorFunction (ctor);
}
// standard copy operations applicable
};
@ -135,5 +145,76 @@ namespace lib {
namespace { ///< details: inject a mock automatically in place of a singleton
using boost::enable_if;
using lib::meta::Yes_t;
using lib::meta::No_t;
/**
* Metafunction: does the Type in question
* give us a clue about what service interface to use?
*/
template<class MOCK>
class defines_ServiceInterface
{
META_DETECT_NESTED (ServiceInterface);
public:
enum{ value = HasNested_ServiceInterface<MOCK>::value
};
};
/**
* Policy-Trait: determine the access point to install the mock.
* @note either the mock service implementation needs to provide
* explicitly a typedef for the ServiceInterface, or we're
* just creating a separate new instance of the singleton service,
* while shadowing (but not destroying) the original instance.
*/
template<class I, class YES =void>
struct ServiceInterface
{
typedef I Type;
};
template<class MOCK>
struct ServiceInterface<MOCK, typename enable_if< defines_ServiceInterface<MOCK> >::type>
{
typedef typename MOCK::ServiceInterface Type;
};
}//(End) mock injection details
/**
* Scoped object for installing/deinstalling a mocked service automatically.
* Placing a suitably specialised instance of this template into a local scope
* will inject the corresponding mock installation and remove it when the
* control flow leaves this scope.
* @param TYPE the concrete mock implementation type to inject. It needs to
* be default constructible. If TYPE is a subclass of the service interface,
* it needs to expose a typedef \c ServiceInterface
*/
template<class TYPE>
struct Use4Test
: boost::noncopyable
{
typedef typename ServiceInterface<TYPE>::Type Interface;
Use4Test()
{
Depend<Interface>::injectReplacement (new TYPE);
}
~Use4Test()
{
Depend<Interface>::dropReplacement();
}
};
} // namespace lib
#endif

View file

@ -34,7 +34,7 @@
namespace lib {
/**
* Factory to generate and manage service objects classified by type.
* Factory to generate and manage service objects classified by type.
*/
class DependencyFactory
{

View file

@ -119,7 +119,7 @@ namespace test{
uint id1 = accessor1().instanceID_;
CHECK (id1 == accessor2().instanceID_);
accessor1.shutdown();
Depend<Sub>::shutdown();
Sub & o2 = accessor2();
uint id2 = accessor2().instanceID_;
@ -154,7 +154,7 @@ namespace test{
SubSub& oSub = otherSpecialAccessor();
CHECK ( INSTANCEOF (SubSubSub, &oSub));
otherSpecialAccessor.shutdown();
Depend<SubSub>::shutdown();
Depend<SubSub> yetAnotherSpecialAccessor;
@ -166,7 +166,7 @@ namespace test{
void
verify_customFactory()
{
Depend<SubSubSub> customisedAccessor(useFactory (&customFactoryFunction));
Depend<SubSubSub> customisedAccessor(&customFactoryFunction);
Depend<SubSub> otherSpecialAccessor;
SubSub& oSub = otherSpecialAccessor();
@ -180,7 +180,7 @@ namespace test{
CHECK (MAX_ID + 10 == oSubS.instanceID_);
}
static SubSubSub*
static void*
customFactoryFunction (void)
{
SubSubSub* newObject = static_cast<SubSubSub*> (DependencyFactory::createSingletonInstance<SubSubSub>());
@ -193,11 +193,13 @@ namespace test{
void
verify_temporaryReplacement()
{
Depend<Sub> genericAccessor;
typedef Depend<Sub> GenericAccessor;
GenericAccessor genericAccessor;
Sub& original = genericAccessor();
uint oID = original.instanceID_;
genericAccessor.injectReplacement (new SubSubSub);
GenericAccessor::injectReplacement (new SubSubSub);
Sub& replacement = genericAccessor();
uint repID = replacement.instanceID_;
@ -215,7 +217,7 @@ namespace test{
CHECK(!isSameObject (replacement, special() ));
CHECK(!isSameObject (replacement, custom() ));
genericAccessor.dropReplacement();
GenericAccessor::dropReplacement();
Sub& nextFetch = genericAccessor();
CHECK (isSameObject (original, nextFetch));
@ -248,6 +250,16 @@ namespace test{
CHECK (repID == otherAccess.instanceID_);
CHECK (repID == replacement.instanceID_);
CHECK ( oID == original.instanceID_);
// verify the instrumentation indeed targeted the generic accessor,
// and *not* an accessor of the sub type, i.e Depend<SubSub>
Depend<SubSub> genericSubTypeAccessor;
SubSub& subTypeAccess = genericSubTypeAccessor();
CHECK ( INSTANCEOF (SubSub, &subTypeAccess));
CHECK (!isSameObject (replacement, subTypeAccess));
CHECK (!isSameObject (original, subTypeAccess));
CHECK (repID != subTypeAccess.instanceID_);
CHECK ( oID != subTypeAccess.instanceID_);
}
Sub& nextFetch = genericAccessor();