From 5c394989294e8be96c3ef074a4b164d4460316ca Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 19 Mar 2018 05:27:37 +0100 Subject: [PATCH] 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... --- src/lib/depend-inject.hpp | 9 +- src/lib/depend2.hpp | 15 +- tests/15library.tests | 2 +- .../library/dependency-configuration-test.cpp | 324 +++++++++++------- tests/library/dependency-factory-test.cpp | 4 + wiki/thinkPad.ichthyo.mm | 51 ++- 6 files changed, 256 insertions(+), 149 deletions(-) diff --git a/src/lib/depend-inject.hpp b/src/lib/depend-inject.hpp index 5ba834529..f93956fcf 100644 --- a/src/lib/depend-inject.hpp +++ b/src/lib/depend-inject.hpp @@ -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 + 2018, Hermann Vosseler 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 */ diff --git a/src/lib/depend2.hpp b/src/lib/depend2.hpp index e8adc1ecd..7ffd69dfb 100644 --- a/src/lib/depend2.hpp +++ b/src/lib/depend2.hpp @@ -3,6 +3,7 @@ Copyright (C) Lumiera.org 2013, Hermann Vosseler + 2018, Hermann Vosseler 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 Singleton Pattern 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 */ diff --git a/tests/15library.tests b/tests/15library.tests index 2b201b80b..b00d48868 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -148,7 +148,7 @@ return: 0 END -PLANNED "Dependency injection configuration" DependencyConfiguration_test <"< + 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> dep11; Depend> dep5; Depend> dep12; - cout << "Siz-DUM : " << lib::test::showSizeof(dep11) << " " << lib::test::showSizeof(dep5) << endl; - cout << "check-vor="<{}() ); + + CHECK ((1+5) == checksum ); Depend dumm; DependInject::useSingleton>(); - 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::useSingleton>() ); - SHOW_EXPR( Depend{}().probe() ); - SHOW_EXPR( checksum ); + CHECK ((1+5+7)*7 == Depend{}().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> dep3; - SHOW_EXPR( checksum ); + CHECK ((1+5+7) == checksum ); { DependInject>::ServiceInstance 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::ServiceInstance{} ); - SHOW_EXPR( checksum ); - - { - DependInject::Local mockDum; - DependInject>::Local 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>::ServiceInstance service{}; - SHOW_EXPR( checksum ); - SHOW_EXPR( dep3().probe() ); - service->offset = 5; - SHOW_EXPR( dep3().probe() ); - SHOW_EXPR( checksum ); - { - DependInject>::Local 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 dumm; + Depend> 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::Local mockDumm; + DependInject>::Local 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>::ServiceInstance 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>::Local 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 ); } }; diff --git a/tests/library/dependency-factory-test.cpp b/tests/library/dependency-factory-test.cpp index 7dca01d61..b325caf2b 100644 --- a/tests/library/dependency-factory-test.cpp +++ b/tests/library/dependency-factory-test.cpp @@ -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 { diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 51bfcaa2b..7d952389c 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -26488,7 +26488,8 @@ - + + @@ -26512,7 +26513,8 @@ - + + @@ -27206,6 +27208,15 @@ + + + + + + + + + @@ -27230,8 +27241,8 @@ - - + + @@ -27242,6 +27253,18 @@ + + + + + + + + + + + + @@ -27251,6 +27274,26 @@ + + + + + + + + + + + +

+ und heute würde ich den Code so nicht mehr schreiben +

+ + +
+ +
+