* most usages are drop-in replacements * occasionally the other convenience functions can be used * verify call-paths from core code to identify usages * ensure reseeding for all tests involving some kind of randomness... __Note__: some tests were not yet converted, since their usage of randomness is actually not thread-safe. This problem existed previously, since also `rand()` is not thread safe, albeit in most cases it is possible to ignore this problem, as ''garbled internal state'' is also somehow „random“
224 lines
7.1 KiB
C++
224 lines
7.1 KiB
C++
/*
|
||
CommandMutation(Test) - checking the functor and undo-functor used within Steam-commands
|
||
|
||
Copyright (C) Lumiera.org
|
||
2009, 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
|
||
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 command-mutation-test.cpp
|
||
** unit test \ref CommandMutation_test
|
||
*/
|
||
|
||
|
||
#include "lib/test/run.hpp"
|
||
#include "lib/test/test-helper.hpp"
|
||
#include "steam/control/command-mutation.hpp"
|
||
#include "steam/control/command-simple-closure.hpp"
|
||
#include "steam/control/memento-tie.hpp"
|
||
#include "lib/meta/tuple-helper.hpp"
|
||
#include "lib/meta/typelist.hpp"
|
||
#include "lib/format-cout.hpp"
|
||
|
||
#include <functional>
|
||
#include <cstdlib>
|
||
#include <string>
|
||
|
||
using std::function;
|
||
using std::string;
|
||
using std::rand;
|
||
|
||
|
||
namespace steam {
|
||
namespace control {
|
||
namespace test {
|
||
|
||
using namespace lib::meta;
|
||
using control::CmdClosure;
|
||
using LERR_(MISSING_MEMENTO);
|
||
using LERR_(UNBOUND_ARGUMENTS);
|
||
|
||
|
||
namespace { /* ======= test functions to bind ========= */
|
||
|
||
int testVal=0; ///< used to verify the effect of testFunc
|
||
|
||
void
|
||
testFunc (int val)
|
||
{
|
||
testVal += val;
|
||
}
|
||
|
||
int
|
||
capture ()
|
||
{
|
||
return testVal;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
/***********************************************************************//**
|
||
* Verify the behaviour of the type erased closure, which is used
|
||
* by Steam-Layer commands to implement the capturing and later
|
||
* re-invocation of a function.
|
||
*
|
||
* @see control::Command
|
||
* @see control::CommandDef
|
||
* @see control::Mutation
|
||
* @see control::UndoMutation
|
||
* @see command-basic-test.hpp
|
||
*/
|
||
class CommandMutation_test : public Test
|
||
{
|
||
|
||
virtual void
|
||
run (Arg)
|
||
{
|
||
seedRand();
|
||
|
||
checkMutation();
|
||
checkUndoMutation();
|
||
checkStateCapturingMechanism();
|
||
}
|
||
|
||
|
||
/** @test check the Mutation functor which is bound to our `testFunc(int)`.
|
||
* Then create a argument closure and use this to invoke the Mutation
|
||
* and verify actually \c testFunc(param) is executed.
|
||
*/
|
||
void
|
||
checkMutation ()
|
||
{
|
||
typedef void SIG_fun(int);
|
||
function<SIG_fun> funky = testFunc;
|
||
|
||
Mutation functor (funky);
|
||
|
||
SimpleClosure<SIG_fun> nullClosure;
|
||
CHECK (not nullClosure.isValid());
|
||
cout << "empty placeholder closure: " << nullClosure << endl;
|
||
VERIFY_ERROR (UNBOUND_ARGUMENTS, functor(nullClosure) );
|
||
|
||
// now create a real closure....
|
||
Tuple<Types<int>> param = std::make_tuple (23);
|
||
SimpleClosure<void(int)> closed_over{param};
|
||
|
||
CmdClosure& closure (closed_over);
|
||
CHECK (closure);
|
||
|
||
cout << "param values: " << closure << endl;
|
||
|
||
testVal = 0;
|
||
functor(closure);
|
||
CHECK (testVal == 23);
|
||
functor(closure);
|
||
CHECK (testVal == 2*23);
|
||
}
|
||
|
||
|
||
/** @test check the special Mutation which is used to \em undo a command.
|
||
* This time, we use our \c testFunc(int) as implementation of the
|
||
* "undo" function; thus its parameter has now the meaning of an
|
||
* captured state value. Consequently this time the \em operation
|
||
* which is to be undone would have the signature \c void(void) .
|
||
* Obviously this is a rather silly "undo" function, but it is
|
||
* easy to check for unit testing. To carry out this test, we
|
||
* first have to trigger the state capturing mechanism; after that,
|
||
* invoking the UndoMutation will call the testFunc with the
|
||
* previously captured state.
|
||
* @note Mutation and UndoMutation are value objects, but they refer
|
||
* to a common command state, which for this test is modelled
|
||
* by local variables and which for the real commands is
|
||
* contained in a Command-StorageHolder
|
||
*/
|
||
void
|
||
checkUndoMutation ()
|
||
{
|
||
function<void(int)> undo_func = testFunc;
|
||
function<int(void)> cap_func = capture;
|
||
|
||
typedef MementoTie<void(),int> MemHolder;
|
||
|
||
MemHolder mementoHolder (undo_func,cap_func);
|
||
UndoMutation undoFunctor (mementoHolder);
|
||
CHECK (!mementoHolder);
|
||
|
||
SimpleClosure<void(void)> nullClosure;
|
||
VERIFY_ERROR (UNBOUND_ARGUMENTS, undoFunctor(nullClosure) );
|
||
VERIFY_ERROR (UNBOUND_ARGUMENTS, undoFunctor.captureState(nullClosure) );
|
||
|
||
Tuple<Types<> > param;
|
||
SimpleClosure<void()> clo{param};
|
||
|
||
CHECK (!mementoHolder);
|
||
VERIFY_ERROR (MISSING_MEMENTO, undoFunctor (clo) );
|
||
VERIFY_ERROR (MISSING_MEMENTO, mementoHolder.getState() );
|
||
|
||
testVal = 11;
|
||
undoFunctor.captureState(clo);
|
||
CHECK (mementoHolder);
|
||
CHECK (testVal == 11);
|
||
|
||
int mem = mementoHolder.getState();
|
||
cout << "saved state: " << mem << endl;
|
||
|
||
undoFunctor(clo);
|
||
CHECK (testVal == 11 + 11);
|
||
undoFunctor(clo);
|
||
CHECK (testVal == 11 + 11 + 11);
|
||
undoFunctor.captureState(clo);
|
||
CHECK (33 == mementoHolder.getState());
|
||
undoFunctor(clo);
|
||
CHECK (testVal == 33 + 33);
|
||
testVal = 9;
|
||
undoFunctor(clo);
|
||
CHECK (testVal == 42);
|
||
}
|
||
|
||
|
||
/** @test check the undo memento capturing mechanism in isolation
|
||
* @see memento-tie-test.cpp more in-depth coverage */
|
||
void
|
||
checkStateCapturingMechanism ()
|
||
{
|
||
typedef MementoTie<void(),int> MemHolder;
|
||
|
||
MemHolder mementoHolder (testFunc, capture);
|
||
|
||
function<void()> bound_undo_func = mementoHolder.tieUndoFunc();
|
||
function<void()> bound_cap_func = mementoHolder.tieCaptureFunc();
|
||
|
||
|
||
int rr{rani (100)};
|
||
testVal = rr;
|
||
bound_cap_func(); // invoke state capturing
|
||
CHECK (rr == mementoHolder.getState());
|
||
|
||
testVal = 10; // meanwhile "somehow" mutate the state
|
||
bound_undo_func(); // invoking the undo() feeds back the memento
|
||
CHECK (testVal == 10+rr);
|
||
}
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (CommandMutation_test, "unit controller");
|
||
|
||
|
||
}}} // namespace steam::control::test
|