DI: clean-up and document the TDD test

...written as byproduct from the reimplementation draft.

NOTE there is a quite similar test from 2013, DependencyFactory_test
For now I prefer to retain both, since the old one should just continue
to work with minor API adjustments (and thus prove this rewrite is a
drop-in replacement).

On the long run those two tests could be merged eventually...
This commit is contained in:
Fischlurch 2018-03-19 05:27:37 +01:00
parent 83476b3ef1
commit 5c39498929
6 changed files with 256 additions and 149 deletions

View file

@ -1,8 +1,8 @@
/*
DEPENDENCY-FACTORY.hpp - managing the lifecycle of singletons and dependencies
DEPEND-INJECT.hpp - managing the lifecycle of singletons and dependencies
Copyright (C) Lumiera.org
2013, Hermann Vosseler <Ichthyostega@web.de>
2018, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@ -22,10 +22,9 @@
/** @file dependency-factory.hpp
** Implementation of a singleton factory used to bring up services as dependency.
** @internal this implementation header belongs to our framework to deal with
** [service dependencies](\ref depend.hpp) and should not be used directly.
** Per type specific configuration of instances created as service dependencies.
** @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway
** @see DependencyConfiguration_test
*/

View file

@ -3,6 +3,7 @@
Copyright (C) Lumiera.org
2013, Hermann Vosseler <Ichthyostega@web.de>
2018, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@ -18,19 +19,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
====================================================================
This code is heavily inspired by
The Loki Library (loki-lib/trunk/include/loki/Singleton.h)
Copyright (c) 2001 by Andrei Alexandrescu
Loki code accompanies the book:
Alexandrescu, Andrei. "Modern C++ Design: Generic Programming
and Design Patterns Applied".
Copyright (c) 2001. Addison-Wesley. ISBN 0201704315
*/
/** @file depend.hpp
** Singleton services and Dependency Injection.
** The <b>Singleton Pattern</b> provides a single access point to a class or
@ -72,8 +63,8 @@ This code is heavily inspired by
** @see lib::Depend
** @see lib::DependencyFactory
** @see lib::test::Depend4Test
** @see singleton-test.cpp
** @see dependency-factory-test.cpp
** @see Singleton_test
** @see DependencyConfiguration_test
*/

View file

@ -148,7 +148,7 @@ return: 0
END
PLANNED "Dependency injection configuration" DependencyConfiguration_test <<END
TEST "Dependency injection configuration" DependencyConfiguration_test <<END
return: 0
END

View file

@ -22,6 +22,9 @@
/** @file dependency-configuration-test.cpp
** unit test \ref DependencyConfiguration_test
** @remark this test was written 3/2018 as a byproduct of the third rewrite
** of the framework for singletons and dependency-injection. It is
** quite redundant with the previously existing DependencyFactory_test
*/
@ -34,50 +37,46 @@
#include "lib/depend2.hpp"
#include "lib/depend-inject.hpp"
#define SHOW_TYPE(_TY_) \
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
#define SHOW_EXPR(_XX_) \
cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
#include <boost/noncopyable.hpp>
namespace lib {
namespace test{
using ::Test;
// using util::isSameObject;
using util::isSameObject;
namespace {
struct Dum
: boost::noncopyable
{
virtual ~Dum() { }
virtual int probe() =0;
};
int checksum = 0;
template<int N>
struct Dummy
: Dum
{
Dummy() { checksum += N; }
~Dummy() { checksum -= N; cout << "~Dummy<"<<N<<">"<<endl;}
virtual int
probe() override
struct Dum
: boost::noncopyable
{
return N * checksum;
}
};
using error::LUMIERA_ERROR_LIFECYCLE;
using error::LUMIERA_ERROR_FATAL;
virtual ~Dum() { }
virtual int probe() =0;
};
int checksum = 0;
template<int N>
struct Dummy
: Dum
{
Dummy() { checksum += N; }
~Dummy() { checksum -= N; }
virtual int
probe() override
{
return N * checksum;
}
};
}
using error::LUMIERA_ERROR_LIFECYCLE;
using error::LUMIERA_ERROR_FATAL;
@ -86,47 +85,92 @@ using error::LUMIERA_ERROR_FATAL;
* @test verify the various modes of creating dependencies.
* - standard case is singleton creation
* - configuration of a specific subclass for the singleton
* - expose a service with explicit lifecycle
* - use of a custom factory function
* - injection of a mock implementation for unit tests
*
* @remark this test basically covers the same ground as DependencyFactory_test;
* but while the latter exists since our second rewrite of lib::Depend (2013),
* this test here is a byproduct of the third rewrite from 2018 and focuses
* more on the configuration and instance identities.
* @see lib::Dependency
* @see Singleton_test
*/
class DependencyConfiguration_test : public Test
class DependencyConfiguration_test
: public Test
{
virtual void
run (Arg)
{
verify_SubclassCreation();
checksum = 0;
verify_Singleton();
verify_SubclassSingleton();
verify_expose_Service_with_Lifecycle();
verify_customFactory();
verify_automaticReplacement();
CHECK (7+5+1 == checksum); // singletons stay alive until application shutdown
}
/** @test without special configuration, singletons are injected as dependency */
void
verify_Singleton()
{
Depend<Dummy<1>> dep11;
Depend<Dummy<5>> dep5;
Depend<Dummy<1>> dep12;
cout << "Siz-DUM : " << lib::test::showSizeof(dep11) << " " << lib::test::showSizeof(dep5) << endl;
cout << "check-vor="<<checksum<<endl;
CHECK (1 == sizeof(dep11));
CHECK (1 == sizeof(dep12));
CHECK (1 == sizeof(dep5));
SHOW_EXPR( dep11().probe() );
SHOW_EXPR( checksum );
SHOW_EXPR( dep5().probe() );
SHOW_EXPR( checksum );
SHOW_EXPR( dep12().probe() );
SHOW_EXPR( checksum );
// no singleton instance created yet
CHECK ( 0 == checksum );
CHECK ( 1*1 == dep11().probe() );
CHECK ( 1 == checksum );
CHECK ((1+5)*5 == dep5().probe() );
CHECK ((1+5) == checksum );
CHECK ((1+5)*1 == dep12().probe() );
CHECK ((1+5) == checksum );
CHECK (not isSameObject (dep11, dep12));
CHECK ( isSameObject (dep11(), dep12()));
}
/** @test preconfigure a specific subclass to be injected as singleton dependency */
void
verify_SubclassSingleton()
{
// unable to create singleton instance of abstract baseclass
VERIFY_ERROR (FATAL, Depend<Dum>{}() );
CHECK ((1+5) == checksum );
Depend<Dum> dumm;
DependInject<Dum>::useSingleton<Dummy<7>>();
SHOW_EXPR( dumm().probe() );
SHOW_EXPR( checksum );
CHECK ((1+5) == checksum );
CHECK ((1+5+7)*7 == dumm().probe() );
CHECK ((1+5+7) == checksum );
VERIFY_ERROR (LIFECYCLE, DependInject<Dum>::useSingleton<Dummy<9>>() );
SHOW_EXPR( Depend<Dum>{}().probe() );
SHOW_EXPR( checksum );
CHECK ((1+5+7)*7 == Depend<Dum>{}().probe() );
CHECK ((1+5+7)*7 == dumm().probe() );
CHECK ((1+5+7) == checksum );
}
/** @test expose a dedicated service instance, which can be shut down */
void
verify_expose_Service_with_Lifecycle()
{
CHECK ((1+5+7) == checksum );
struct SubDummy
: Dummy<3>
@ -134,112 +178,138 @@ using error::LUMIERA_ERROR_FATAL;
virtual int
probe() override
{
return -checksum + offset;
return offset - checksum;
}
int offset = 0;
};
Depend<Dummy<3>> dep3;
SHOW_EXPR( checksum );
CHECK ((1+5+7) == checksum );
{
DependInject<Dummy<3>>::ServiceInstance<SubDummy> service{};
CHECK (service);
SHOW_EXPR( checksum );
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( checksum );
service->offset = 5;
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( checksum );
CHECK ((1+5+7+3) == checksum );
CHECK (-(1+5+7+3) == dep3().probe() );
CHECK ((1+5+7+3) == checksum );
service->offset = (1+5+7);
CHECK ( -3 == dep3().probe() );
CHECK ((1+5+7+3) == checksum );
}
SHOW_EXPR( checksum );
CHECK ((1+5+7) == checksum );
VERIFY_ERROR (LIFECYCLE, dep3().probe() );
VERIFY_ERROR (LIFECYCLE, DependInject<Dum>::ServiceInstance<SubDummy>{} );
SHOW_EXPR( checksum );
{
DependInject<Dum>::Local<SubDummy> mockDum;
DependInject<Dummy<3>>::Local<SubDummy> mockDummy3;
CHECK (!mockDum);
CHECK (!mockDummy3);
SHOW_EXPR( dumm().probe() );
CHECK ( mockDum);
CHECK (!mockDummy3);
SHOW_EXPR( checksum );
SHOW_EXPR( mockDum->probe() );
SHOW_EXPR( checksum );
mockDum->offset = -4;
SHOW_EXPR( dumm().probe() );
CHECK (!mockDummy3);
SHOW_EXPR( checksum );
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( checksum );
CHECK ( mockDummy3);
SHOW_EXPR( mockDummy3->probe() );
SHOW_EXPR( checksum );
mockDummy3->offset = 19;
SHOW_EXPR( dep3().probe() );
mockDum->offset = -6;
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( dumm().probe() );
SHOW_EXPR( checksum );
}
SHOW_EXPR( checksum );
SHOW_EXPR( dumm().probe() );
VERIFY_ERROR (LIFECYCLE, dep3().probe() );
SHOW_EXPR( checksum );
{
DependInject<Dummy<3>>::ServiceInstance<SubDummy> service{};
SHOW_EXPR( checksum );
SHOW_EXPR( dep3().probe() );
service->offset = 5;
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( checksum );
{
DependInject<Dummy<3>>::Local<SubDummy> mockDummy31;
CHECK (!mockDummy31);
SHOW_EXPR( checksum );
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( checksum );
mockDummy31->offset = 10;
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( mockDummy31->probe() );
SHOW_EXPR( service->probe() );
CHECK (mockDummy31->offset != service->offset);
service->offset = 20;
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( mockDummy31->probe() );
SHOW_EXPR( service->probe() );
SHOW_EXPR( checksum );
}
SHOW_EXPR( checksum );
SHOW_EXPR( dep3().probe() );
SHOW_EXPR( checksum );
}
SHOW_EXPR( checksum );
VERIFY_ERROR (LIFECYCLE, dep3().probe() );
SHOW_EXPR( dumm().probe() );
SHOW_EXPR( checksum );
}
void
verify_SubclassCreation()
{
CHECK ((1+5+7) == checksum );
}
/** @test instance creation can be preconfigured with a closure */
void
verify_customFactory()
{
TODO ("implement arbitrary ctor closure");
}
/** @test injecting test mocks temporarily */
void
verify_automaticReplacement()
{
Depend<Dum> dumm;
Depend<Dummy<3>> depp;
CHECK ((1+5+7) == checksum );
CHECK ((1+5+7)*7 == dumm().probe() );
VERIFY_ERROR (LIFECYCLE, depp().probe() );
struct Mock
: Dummy<3>
{
virtual int
probe() override
{
return response;
}
int response = -1;
};
{////////////////////////////////////////////////////TEST-Scope
DependInject<Dum>::Local<Mock> mockDumm;
DependInject<Dummy<3>>::Local<Mock> mockDummy3;
CHECK ((1+5+7) == checksum );
CHECK (!mockDumm);
CHECK (!mockDummy3);
CHECK (-1 == dumm().probe() ); // NOTE: now returning the response from the mock instance
CHECK ( mockDumm);
CHECK (!mockDummy3);
CHECK ((1+5+7+3) == checksum );
CHECK (-1 == mockDumm->probe() );
CHECK ((1+5+7+3) == checksum );
mockDumm->response = 11;
CHECK (11 == dumm().probe() ); // NOTE: now returning the response changed on the mock instance
CHECK (!mockDummy3); // the second mock is still in not yet created state...
CHECK ((1+5+7+3) == checksum );
CHECK (-1 == depp().probe() );
CHECK ((1+5+7+3+3) == checksum ); // ...and now we got a second mock instance!
CHECK ( mockDummy3);
CHECK (-1 == mockDummy3->probe() );
CHECK ((1+5+7+3+3) == checksum );
mockDummy3->response = 22;
CHECK (22 == depp().probe() );
mockDumm->response = 12;
CHECK (22 == depp().probe() ); // these are really two distinct instances
CHECK (12 == dumm().probe() );
CHECK ((1+5+7+3+3) == checksum );
}////////////////////////////////////////////////////(End)TEST-Scope
// Back to normal: the Mocks are gone, original behaviour uncovered
CHECK ((1+5+7) == checksum );
CHECK ((1+5+7)*7 == dumm().probe() );
VERIFY_ERROR (LIFECYCLE, depp().probe() );
CHECK ((1+5+7) == checksum );
{/////////////////////////////////////////////////////////Service-Scope
DependInject<Dummy<3>>::ServiceInstance<Mock> service{};
CHECK ((1+5+7+3) == checksum ); // NOTE: we got a new Dummy<3> service instance
CHECK (-1 == depp().probe() ); // ..... which returns the pristine mock response
service->response = 33;
CHECK (33 == depp().probe() );
CHECK ((1+5+7+3) == checksum );
{/////////////////////////////////////////////////////////NESTED-TEST-Scope
DependInject<Dummy<3>>::Local<Mock> mockDummy31;
CHECK (!mockDummy31);
CHECK ((1+5+7+3) == checksum ); // ...while SerivceInstance is created eagerly
CHECK (-1 == depp().probe() ); // the Local mock instance is only created on-demand
CHECK ((1+5+7+3+3) == checksum );
mockDummy31->response = 44;
CHECK (44 == depp().probe() );
CHECK (44 == mockDummy31->probe() );
CHECK (33 == service->probe() );
CHECK (mockDummy31->response != service->response);
service->response = 34;
CHECK (44 == depp().probe() ); // NOTE: remains shadowed by the mockDummy
CHECK (44 == mockDummy31->probe() );
CHECK (34 == service->probe() );
CHECK ((1+5+7+3+3) == checksum );
}/////////////////////////////////////////////////////////(End)NESTED-TEST-Scope
// Now the mock is gone and the service instance becomes uncovered
CHECK ((1+5+7+3) == checksum );
CHECK (34 == depp().probe() ); // now reveals the response changed from within the nested test scope
CHECK ((1+5+7+3) == checksum );
}/////////////////////////////////////////////////////////(End)Service-Scope
// Back to normal: Mocks is gone, Service is shutdown, original behaviour uncovered
CHECK ((1+5+7) == checksum );
VERIFY_ERROR (LIFECYCLE, depp().probe() );
CHECK ((1+5+7)*7 == dumm().probe() );
CHECK ((1+5+7) == checksum );
}
};

View file

@ -22,6 +22,9 @@
/** @file dependency-factory-test.cpp
** unit test \ref DependencyFactory_test
** @remark this is an old test from 2013 and thus verifies that the functionality
** for dependency-injection was not broken by the rewrite in 2018
** @see dependency-configuration-test.cpp
*/
@ -95,6 +98,7 @@ namespace test{
*
* @see lib::Dependency
* @see Singleton_test
* @see DependencyConfiguration_test newer test from 2018 to cover the same ground
*/
class DependencyFactory_test : public Test
{

View file

@ -26488,7 +26488,8 @@
<linktarget COLOR="#5c71a3" DESTINATION="ID_451964727" ENDARROW="Default" ENDINCLINATION="767;-2073;" ID="Arrow_ID_1454095581" SOURCE="ID_1714114896" STARTARROW="None" STARTINCLINATION="1892;380;"/>
<linktarget COLOR="#8697be" DESTINATION="ID_451964727" ENDARROW="Default" ENDINCLINATION="200;-562;" ID="Arrow_ID_1211717131" SOURCE="ID_1032947061" STARTARROW="None" STARTINCLINATION="1387;-152;"/>
<icon BUILTIN="hourglass"/>
<node CREATED="1520722130803" ID="ID_1194364308" MODIFIED="1520722135383" TEXT="Anforderungen">
<node CREATED="1520722130803" ID="ID_1194364308" MODIFIED="1521433923249" TEXT="Anforderungen">
<icon BUILTIN="yes"/>
<node CREATED="1520722192387" ID="ID_1178804552" MODIFIED="1520722204325" TEXT="leicht zu verwenden">
<node CREATED="1520722210416" ID="ID_18853847" MODIFIED="1520722214920" TEXT="access by-name"/>
<node CREATED="1520722205225" ID="ID_187805501" MODIFIED="1520722209444" TEXT="klare einfache Syntax"/>
@ -26512,7 +26513,8 @@
<node CREATED="1520723536370" ID="ID_98564224" MODIFIED="1520723551009" TEXT="Mockbarkeit"/>
<node CREATED="1520722720322" ID="ID_1574090447" MODIFIED="1520722762564" TEXT="Implementierung"/>
</node>
<node CREATED="1521081597008" HGAP="23" ID="ID_198915567" MODIFIED="1521081653981" TEXT="Design" VSHIFT="11">
<node CREATED="1521081597008" FOLDED="true" HGAP="23" ID="ID_198915567" MODIFIED="1521433908152" TEXT="Design-Schlu&#xdf;folgerungen" VSHIFT="11">
<icon BUILTIN="info"/>
<node CREATED="1521081661071" ID="ID_500707872" MODIFIED="1521082831734" TEXT="L&#xf6;sung-1">
<richcontent TYPE="NOTE"><html>
<head>
@ -27206,6 +27208,15 @@
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521433938944" ID="ID_546086969" MODIFIED="1521433946432" TEXT="double-checked-locking">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521433948534" ID="ID_176845865" MODIFIED="1521433954006" TEXT="umstellen auf atomics">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521434009142" ID="ID_117782655" MODIFIED="1521434015486" TEXT="Performance-Messung">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160700669" ID="ID_978221585" MODIFIED="1521160718035" TEXT="Dokumentation">
<icon BUILTIN="flag-yellow"/>
@ -27230,8 +27241,8 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419563657" ID="ID_733291707" MODIFIED="1521419571641" TEXT="DependencyConfiguration_test">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419563657" ID="ID_733291707" MODIFIED="1521433847496" TEXT="DependencyConfiguration_test">
<icon BUILTIN="pencil"/>
<node CREATED="1521419590709" ID="ID_914313570" MODIFIED="1521419778465" TEXT="ziemlich redundant">
<arrowlink COLOR="#b9274f" DESTINATION="ID_417098537" ENDARROW="Default" ENDINCLINATION="159;18;" ID="Arrow_ID_606259386" STARTARROW="Default" STARTINCLINATION="18;-57;"/>
<node CREATED="1521419624065" HGAP="29" ID="ID_477987875" MODIFIED="1521419637660" TEXT="weiterhin sinnvoll?" VSHIFT="14">
@ -27242,6 +27253,18 @@
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1521433819705" ID="ID_797347707" MODIFIED="1521433827896" TEXT="&#xfc;bernehmen und aufteilen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1521433807714" ID="ID_655174082" MODIFIED="1521433832327" TEXT="Assertions explizit machen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521433833942" ID="ID_841958834" MODIFIED="1521433843486" TEXT="Factory-Closure abdecken">
<icon BUILTIN="flag-yellow"/>
</node>
<node COLOR="#338800" CREATED="1521433852660" ID="ID_1581010755" MODIFIED="1521433857543" TEXT="l&#xe4;uft">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node CREATED="1520722155112" ID="ID_1512641426" MODIFIED="1520722159028" TEXT="Integration">
@ -27251,6 +27274,26 @@
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160765588" ID="ID_1857896991" MODIFIED="1521160801792" TEXT="Konfig-Aufrufe anpassen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521433694233" ID="ID_1951071885" MODIFIED="1521433716263" TEXT="Nebenbei: Schwartz-Counter in ClassLock abl&#xf6;sen">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1521433723421" ID="ID_1813539288" MODIFIED="1521433782583" TEXT="Meyers Singleton ist seit C++11 wasserdicht">
<icon BUILTIN="ksmiletris"/>
</node>
<node CREATED="1521433748650" ID="ID_1831581678" MODIFIED="1521433788476">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
und heute w&#252;rde ich den Code <i>so</i>&#160;nicht mehr schreiben
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="smiley-oh"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160792856" ID="ID_367925099" MODIFIED="1521160800344" TEXT="System l&#xe4;uft wie zuvor">
<icon BUILTIN="flag-yellow"/>
</node>