/* CommandArgument(Test) - checking storage of specifically typed command arguments Copyright (C) 2009, Hermann Vosseler   **Lumiera** 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. See the file COPYING for further details. * *****************************************************************/ /** @file command-argument-test.cpp ** unit test \ref CommandArgument_test */ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "steam/control/command-storage-holder.hpp" #include "lib/scoped-ptrvect.hpp" #include "lib/format-string.hpp" #include "lib/format-cout.hpp" #include "lib/util-foreach.hpp" #include "lib/util.hpp" #include #include #include #include #include using util::_Fmt; using util::isnil; using util::for_each; using lib::time::Time; using lib::time::TimeVar; using lib::time::TimeValue; using std::string; using std::ostringstream; using std::make_tuple; using std::rand; namespace steam { namespace control { namespace test { using lib::test::showSizeof; using lib::test::randTime; using namespace lib::meta; using LERR_(MISSING_MEMENTO); namespace { // test helpers ostringstream protocol; ///< used to verify the test function calls /** * watching the instance creation * of some parameter values */ template struct Tracker { TY element_; static int instanceCnt; Tracker (TY init = TY()) : element_(init) { ++instanceCnt; } Tracker (Tracker const& otr) : element_(otr.element_) { ++instanceCnt; } ~Tracker() { --instanceCnt; } Tracker& operator= (Tracker const&) = default; TY& operator* () { return element_; } operator string() const { return element_; } friend bool operator== (Tracker const& tra1, Tracker const& tra2) { return tra1.element_ == tra2.element_; } }; template int Tracker::instanceCnt (0); /** prepare a (singleton) _empty value_ for the memento. * @remarks This is done prior to observing the Tracker instanceCnt, * because this empty value obviously remains allocated forever. * The memento is stored within a [special holder](\ref lib::ReplaceableItem) * to allow capturing memento state even from immutable values, which only can * be copy constructed. This mechanism uses lib::NullValue to retrieve an * empty placeholder value when the memento has not yet been captured. */ void prepareEmptyMemento() { lib::NullValue>::get(); } /** Dummy custom memento datatype * @note memento needs to be equality comparable */ struct Sint5 { int i[5]; friend bool operator== (Sint5 const& i1, Sint5 const& i2) { return i1.i == i2.i; } }; /* === functions to implement test-"operation" & UNDO === */ void doIt (Tracker time, Tracker str, int rand) { static _Fmt fmt ("doIt( Time=%s \"%s\" rand=%2d )"); cout << "invoke operation..." << endl; protocol << fmt % *time % *str % rand; } Tracker captureState (Tracker, Tracker xstr, int) { cout << "capture state..." << endl; return protocol.str() + *xstr; } void undoIt (Tracker time, Tracker, int, Tracker memento) { cout << "undo... memento=" << memento << endl; protocol << "undoIt(time="< ArgTuples; /***********************************************************************//** * @test Check storage handling of the command parameters and state memento. * * @see control::CommandStorageHolder * @see command-basic-test.hpp */ class CommandArgument_test : public Test { virtual void run (Arg) { seedRand(); ArgTuples testTuples; prepareEmptyMemento(); Tracker::instanceCnt = 0; Tracker::instanceCnt = 0; createTuples (testTuples); serialiseArgTuples (testTuples); testTuples.clear(); simulateCmdLifecycle(); // verify all dtors properly called... CHECK (0 == Tracker::instanceCnt); CHECK (0 == Tracker::instanceCnt); } typedef Tracker TTime; typedef Tracker Tstr; /** @test create various argument tuples and re-access their contents */ void createTuples (ArgTuples& tup) { typedef StorageHolder A1; typedef StorageHolder A2; typedef StorageHolder A3; typedef StorageHolder A4; typedef StorageHolder A5; A1* arg1 = new A1(); tup.manage (arg1); A2* arg2 = new A2(); tup.manage (arg2); A3* arg3 = new A3(); tup.manage (arg3); A4* arg4 = new A4(); tup.manage (arg4); A5* arg5 = new A5(); tup.manage (arg5); CHECK (isnil (*arg1)); CHECK (isnil (*arg2)); CHECK (isnil (*arg3)); CHECK (isnil (*arg4)); CHECK (isnil (*arg5)); for_each (tup, showIt); arg1->storeTuple (std::tuple<>()); arg2->storeTuple (make_tuple (rani(10))); arg3->storeTuple (make_tuple (rani(10), TimeVar(randTime()))); arg4->storeTuple (make_tuple (rani(10), TimeVar(randTime()))); arg5->storeTuple (make_tuple (TTime (randTime()), Tstr("glorious"), twoRandomDigits() )); CHECK (!arg5->canUndo()); arg5->tie(undoIt, captureState) .tieCaptureFunc() // bind capturing function to memento storage, (TTime(), Tstr("destruction"), 11); // then invoke the bound capturing mechanism CHECK (arg5->canUndo()); CHECK (*arg5->memento() == "destruction"); VERIFY_ERROR(MISSING_MEMENTO, arg4->memento().i[3] = 513 ); for_each (tup, showIt); } /** @test serialise and de-serialise each tuple and check validity * @todo unimplemented, waiting on Serialiser */ void serialiseArgTuples (ArgTuples& tup) { for_each (tup, checkSerialisation); } /** @test simulate a complete command lifecycle with regards to the * storage handling of the command parameters and state memento. */ void simulateCmdLifecycle() { typedef void SIG_do(Tracker, Tracker, int); using Args = StorageHolder>; using MemHolder = MementoTie>; Args args; CHECK (isnil (args)); cout << showSizeof(args) << endl; // store a set of parameter values, later to be used on invocation args.storeTuple ( make_tuple (TTime(randTime()), Tstr("Lumiera rocks"), twoRandomDigits() )); CHECK (!isnil (args)); cout << args << endl; CHECK (!args.canUndo()); VERIFY_ERROR(MISSING_MEMENTO, args.memento() ); MemHolder& memHolder = args.tie(undoIt,captureState); CHECK (!memHolder); // no stored memento.... CHECK (!args.canUndo()); function doItFun = doIt; function undoFun = memHolder.tieUndoFunc(); function captFun = memHolder.tieCaptureFunc(); typedef function OpFun; // now close all the functions with the stored parameter values... OpFun bound_doItFun = std::bind (&CmdClosure::invoke, args, CmdFunctor(doItFun)); OpFun bound_undoFun = std::bind (&CmdClosure::invoke, args, CmdFunctor(undoFun)); OpFun bound_captFun = std::bind (&CmdClosure::invoke, args, CmdFunctor(captFun)); protocol.seekp(0); protocol << "START..."; bound_captFun(); cout << "captured state: " << args.memento() << endl; CHECK (memHolder); CHECK (!isnil (*args.memento())); CHECK (args.canUndo()); cout << args << endl; bound_doItFun(); cout << protocol.str() << endl; bound_undoFun(); cout << protocol.str() << endl; // Commands can serve as prototype to be copied.... Args argsCopy (args); bound_captFun(); protocol.seekp(0); protocol << "RESET..."; args.storeTuple ( make_tuple (TTime(TimeValue(123456)), Tstr("unbelievable"), twoRandomDigits() )); cout << "modified: " << args << endl; cout << "copied : " << argsCopy << endl; // holds still the old params & memento bound_undoFun(); cout << protocol.str() << endl; } }; /** Register this test class... */ LAUNCHER (CommandArgument_test, "unit controller"); }}} // namespace steam::control::test