diff --git a/src/lib/depend.hpp b/src/lib/depend.hpp index 782bdc4bd..3c4ccb577 100644 --- a/src/lib/depend.hpp +++ b/src/lib/depend.hpp @@ -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 ////TODO move in separate header +#include ////TODO move in separate header +#include ////TODO move in separate header + + namespace lib { /** @@ -86,7 +93,20 @@ namespace lib { return *instance; } - void + + typedef DependencyFactory::InstanceConstructor Constructor; + + Depend (Constructor ctor = buildSingleton()) + { + 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()) - { - 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 defines_ServiceInterface + { + META_DETECT_NESTED (ServiceInterface); + + public: + enum{ value = HasNested_ServiceInterface::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 + struct ServiceInterface + { + typedef I Type; + }; + + + template + struct ServiceInterface >::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 + struct Use4Test + : boost::noncopyable + { + typedef typename ServiceInterface::Type Interface; + + Use4Test() + { + Depend::injectReplacement (new TYPE); + } + + ~Use4Test() + { + Depend::dropReplacement(); + } + }; + + } // namespace lib #endif diff --git a/src/lib/dependency-factory.hpp b/src/lib/dependency-factory.hpp index de9926ba0..1c74cb99d 100644 --- a/src/lib/dependency-factory.hpp +++ b/src/lib/dependency-factory.hpp @@ -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 { diff --git a/tests/library/dependency-factory-test.cpp b/tests/library/dependency-factory-test.cpp index 76c1d59a1..151e29e5f 100644 --- a/tests/library/dependency-factory-test.cpp +++ b/tests/library/dependency-factory-test.cpp @@ -119,7 +119,7 @@ namespace test{ uint id1 = accessor1().instanceID_; CHECK (id1 == accessor2().instanceID_); - accessor1.shutdown(); + Depend::shutdown(); Sub & o2 = accessor2(); uint id2 = accessor2().instanceID_; @@ -154,7 +154,7 @@ namespace test{ SubSub& oSub = otherSpecialAccessor(); CHECK ( INSTANCEOF (SubSubSub, &oSub)); - otherSpecialAccessor.shutdown(); + Depend::shutdown(); Depend yetAnotherSpecialAccessor; @@ -166,7 +166,7 @@ namespace test{ void verify_customFactory() { - Depend customisedAccessor(useFactory (&customFactoryFunction)); + Depend customisedAccessor(&customFactoryFunction); Depend 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 (DependencyFactory::createSingletonInstance()); @@ -193,11 +193,13 @@ namespace test{ void verify_temporaryReplacement() { - Depend genericAccessor; + typedef Depend 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 + Depend 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();