From 7a22e7f987c3d846fbae36304906860c13df354f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 8 Nov 2023 19:27:08 +0100 Subject: [PATCH] Test: helper for transitory manipulations Use a simple destructor-trick to set up a concise notation for temporarily manipulating a value for testing. The manipulation will automatically be undone when leaving scope --- src/lib/test/test-helper.hpp | 1 + src/lib/test/transiently.hpp | 134 ++++++++++++++++++++++++ tests/library/test/test-helper-test.cpp | 50 ++++++++- 3 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 src/lib/test/transiently.hpp diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp index 20f2b0891..7ad2e97d9 100644 --- a/src/lib/test/test-helper.hpp +++ b/src/lib/test/test-helper.hpp @@ -42,6 +42,7 @@ #include "lib/symbol.hpp" #include "lib/time/timevalue.hpp" +#include "lib/test/transiently.hpp" #include "lib/format-obj.hpp" #include diff --git a/src/lib/test/transiently.hpp b/src/lib/test/transiently.hpp new file mode 100644 index 000000000..49f28ee40 --- /dev/null +++ b/src/lib/test/transiently.hpp @@ -0,0 +1,134 @@ +/* + TRANSIENTLY.hpp - temporary manipulations undone when leaving scope + + Copyright (C) Lumiera.org + 2023, 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 + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file transiently.hpp + ** Test helper to perform temporary manipulations within a test scope. + ** Such _safe manipulations_ can be achieved by tying the clean-up to + ** the destructor of a token object in local scope. In the simple form, + ** a reference to the original and the original value are captured; + ** alternatively, both manipulation and clean-up can be given as Lambdas. + ** @see TestHelper_test::checkLocalManipulation + ** @see scheduler-service-test.cpp + ** + */ + + +#ifndef LIB_TEST_TRANSIENTLY_H +#define LIB_TEST_TRANSIENTLY_H + + +#include "lib/nocopy.hpp" +#include "lib/meta/function.hpp" +#include "lib/ppmpl.h" + +#include + +namespace lib { +namespace test{ + + /** + * Token to capture a value + * and restore original when leaving scope + */ + template + class Transiently + : util::NonCopyable + { + TAR originalVal_; + TAR& manipulated_; + + public: + Transiently(TAR& target) + : originalVal_{target} + , manipulated_{target} + { } + + ~Transiently() + { + manipulated_ = originalVal_; + } + + template + void + operator= (X&& x) + { + manipulated_ = std::forward (x); + } + }; + + + /** Variation where manipulation is done by λ */ + template<> + class Transiently + : util::NonCopyable + { + using Manipulator = std::function; + + Manipulator doIt_; + Manipulator undoIt_; + + public: + Transiently (Manipulator manipulation) + : doIt_{std::move (manipulation)} + , undoIt_{} + { } + + ~Transiently() + { + CHECK (undoIt_, "REJECT Manipulation -- " + "Failed to provide a way " + "to undo the manipulation."); + undoIt_(); + } + + void + cleanUp (Manipulator cleanUp) + { + undoIt_ = std::move (cleanUp); + doIt_(); // actually perform the manipulation + } + }; + + /** deduction guide: + * use λ for manipulation and clean-up + * @remark activated when given a function with signature + */ + template>> + Transiently (FUN&&) -> Transiently; + +}} // namespace lib::test + + + + +/* === test helper macros === */ + +/** + * Macro to simplify capturing assignments. + * @remark use as `TRANSIENTLY(blah) = moo;` + */ +#define TRANSIENTLY(_OO_) \ + lib::test::Transiently PPMPL_CAT(transientlyManipulated_,__LINE__)(_OO_); PPMPL_CAT(transientlyManipulated_,__LINE__) + + +#endif /*LIB_TEST_TRANSIENTLY_H*/ diff --git a/tests/library/test/test-helper-test.cpp b/tests/library/test/test-helper-test.cpp index a9335a02a..85d485a70 100644 --- a/tests/library/test/test-helper-test.cpp +++ b/tests/library/test/test-helper-test.cpp @@ -27,6 +27,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "lib/time/timevalue.hpp" #include "lib/error.hpp" #include "lib/util-foreach.hpp" #include "lib/format-cout.hpp" @@ -39,6 +40,8 @@ using util::for_each; using lumiera::Error; using lumiera::LUMIERA_ERROR_EXCEPTION; using lumiera::error::LUMIERA_ERROR_ASSERTION; +using lib::time::TimeVar; +using lib::time::Time; using boost::algorithm::is_lower; using boost::algorithm::is_digit; @@ -51,7 +54,7 @@ namespace test{ namespace test{ template - class Wrmrmpft + class Wrmrmpft { T tt_; }; @@ -76,6 +79,7 @@ namespace test{ checkGarbageStr(); checkTypeDisplay(); checkThrowChecker(); + checkLocalManipulation(); } @@ -140,7 +144,7 @@ namespace test{ } - /** @test check the VERIFY_ERROR macro, + /** @test check the VERIFY_ERROR macro, * which ensures a given error is raised. */ void @@ -155,6 +159,48 @@ namespace test{ #endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT } + + /** @test check a local manipulations, + * which are undone when leaving the scope. + */ + void + checkLocalManipulation() + { + int equilibrium = 42; + { + // manipulate the value temporarily... + TRANSIENTLY(equilibrium) = 49; + + CHECK (49 == equilibrium); + } + CHECK (42 == equilibrium); + + + TimeVar day_of_reckoning{Time{555,5}}; + try + { + TRANSIENTLY(equilibrium) = 55; + TRANSIENTLY(day_of_reckoning) = Time::ANYTIME; + + CHECK (55 == equilibrium); + CHECK (Time::ANYTIME == day_of_reckoning); + throw "RRRrrevenge!!!!!!!!!!!!!!!!1!!11!!"; + } + catch(...) { } + CHECK (42 == equilibrium); + CHECK (Time(555,5) == day_of_reckoning); + + + { // can also use λ for manipulation and clean-up + TRANSIENTLY ([&]{ day_of_reckoning *= 2; }) + .cleanUp ([&]{ equilibrium /= 2; }); + + CHECK (42 == equilibrium); // not yet touched... + CHECK (Time(110,11) == day_of_reckoning); + } + CHECK (Time(110,11) == day_of_reckoning); + CHECK (21 == equilibrium); + } }; LAUNCHER (TestHelper_test, "unit common");