WIP devised various aspects of command execution, drafted as unit test
This commit is contained in:
parent
a8a0e07726
commit
f40282b2ff
7 changed files with 546 additions and 34 deletions
|
|
@ -54,3 +54,18 @@ END
|
|||
TEST "Memento wiring and storage" MementoTie_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
PLANNED "Command usage aspects I" CommandUse1_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
PLANNED "Command usage aspects II" CommandUse2_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
||||
PLANNED "Command usage aspects III" CommandUse3_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ test_components_SOURCES = \
|
|||
$(testcomponents_srcdir)/proc/control/command-basic-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/command-argument-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/command-mutation-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/command-use1-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/command-use2-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/command-use3-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/argument-tuple-accept-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/control/memento-tie-test.cpp \
|
||||
$(testcomponents_srcdir)/proc/engine/bufftabletest.cpp \
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
CommandUse1(Test) - usage scenario 1
|
||||
CommandUse1(Test) - usage aspects I
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2009, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
//#include "lib/test/test-helper.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
//#include "proc/asset/media.hpp"
|
||||
//#include "proc/mobject/session.hpp"
|
||||
//#include "proc/mobject/session/edl.hpp"
|
||||
|
|
@ -70,15 +70,18 @@ namespace test {
|
|||
|
||||
|
||||
/***************************************************************************
|
||||
* @test command usage scenario 1: defining commands in various ways.
|
||||
* @test command usage aspects I: defining commands in various ways,
|
||||
* then re-accessing those definitions, create instances,
|
||||
* invoke them and undo the effect. Clean up finally.
|
||||
*
|
||||
* @todo WIP
|
||||
* @see Command
|
||||
* @see command-basic-test.cpp (simple usage example)
|
||||
*/
|
||||
class CommandUse1_test : public Test
|
||||
{
|
||||
|
||||
int randVal;
|
||||
int shuffle() { return randVal = 10 + (rand() % 40); }
|
||||
int random() { return randVal = 10 + (rand() % 40); }
|
||||
|
||||
|
||||
|
||||
|
|
@ -86,11 +89,18 @@ namespace test {
|
|||
run (Arg)
|
||||
{
|
||||
command1::checksum_ = 0;
|
||||
uint cnt_defs = Command::definition_count();
|
||||
uint cnt_inst = Command::instance_count();
|
||||
|
||||
allInOneStep();
|
||||
standardUse();
|
||||
definePrototype();
|
||||
usePrototype();
|
||||
undef();
|
||||
|
||||
ASSERT (0 == command1::checksum_);
|
||||
ASSERT (0 == command1::check_);
|
||||
ASSERT (cnt_defs == Command::definition_count());
|
||||
ASSERT (cnt_inst == Command::instance_count());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -102,41 +112,80 @@ namespace test {
|
|||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
.bind (shuffle())
|
||||
.exec()
|
||||
.bind (random())
|
||||
.execSync()
|
||||
;
|
||||
|
||||
ASSERT (randVal == checksum_);
|
||||
|
||||
Command::get("test.command1.1").undo();
|
||||
ASSERT ( 0 == command1::checksum_);
|
||||
ASSERT ( 0 == command1::check_);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
standardUse()
|
||||
{
|
||||
{
|
||||
CommandDef ("test.command1.2")
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
;
|
||||
}
|
||||
ASSERT ( CommandDef("test.command1.2"));
|
||||
ASSERT (!Command::get("test.command1.2"));
|
||||
|
||||
Command com = Command::get("test.command1.2");
|
||||
ASSERT (!com);
|
||||
ASSERT (!com.canUndo());
|
||||
VERIFY_ERROR (UNBOUND_ARGUMENTS, com() );
|
||||
|
||||
ASSERT ( 0 == command1::check_);
|
||||
VERIFY_ERROR (INVALID_ARGUMENTS, com.bind ("foo") );
|
||||
com.bind (random()); // note: run-time type check only
|
||||
com();
|
||||
ASSERT (randVal == command1::check_);
|
||||
com.undo();
|
||||
ASSERT ( 0 == command1::check_);
|
||||
|
||||
// the following shortcut does the same:
|
||||
Command::invoke ("test.command1.2", 1234);
|
||||
ASSERT ( 1234 == command1::check_);
|
||||
|
||||
// another shortcut, with static type check:
|
||||
invoke (command1::operate, 5678);
|
||||
ASSERT ( 1234+5678 == command1::check_);
|
||||
|
||||
com.undo();
|
||||
ASSERT ( 0 == command1::check_);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
definePrototype()
|
||||
{
|
||||
CommandDef ("test.command1.2")
|
||||
CommandDef ("test.command1.3")
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt)
|
||||
.bind (shuffle())
|
||||
.bind (random())
|
||||
;
|
||||
|
||||
|
||||
ASSERT (Command::get("test.command1.2").canExec());
|
||||
ASSERT (Command::get("test.command1.3").canExec());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
usePrototype()
|
||||
{
|
||||
Command c1 = Command::get("test.command1.2");
|
||||
Command c1 = Command::get("test.command1.3");
|
||||
ASSERT (c1);
|
||||
ASSERT (c1.canExec());
|
||||
ASSERT (!c1.canUndo());
|
||||
|
||||
Command c2 = Command::get("test.command1.2");
|
||||
Command c2 = Command::get("test.command1.3");
|
||||
ASSERT (c1);
|
||||
ASSERT (c1.canExec());
|
||||
ASSERT (!c1.canUndo());
|
||||
|
|
@ -144,41 +193,111 @@ namespace test {
|
|||
ASSERT (c1 == c2);
|
||||
ASSERT (!isSameObject(c1, c2));
|
||||
|
||||
ASSERT (0 == command1::checksum_);
|
||||
ASSERT (0 == command1::check_);
|
||||
|
||||
c1();
|
||||
|
||||
ASSERT (randVal == command1::checksum_);
|
||||
ASSERT (randVal == command1::check_);
|
||||
ASSERT (c1.canUndo());
|
||||
ASSERT (c1 != c2);
|
||||
ASSERT (!c2.canUndo());
|
||||
|
||||
c2();
|
||||
ASSERT (randVal + randVal == command1::checksum_);
|
||||
ASSERT (randVal + randVal == command1::check_);
|
||||
ASSERT (c2.canUndo());
|
||||
ASSERT (c1 != c2);
|
||||
|
||||
c1.undo();
|
||||
ASSERT (0 == command1::checksum_);
|
||||
ASSERT (0 == command1::check_);
|
||||
c2.undo();
|
||||
ASSERT (randVal == command1::checksum_);
|
||||
ASSERT (randVal == command1::check_);
|
||||
|
||||
c2.bind(23);
|
||||
c2();
|
||||
ASSERT (randVal + 23 == command1::checksum_);
|
||||
ASSERT (randVal + 23 == command1::check_);
|
||||
|
||||
// you should not use a command more than once (but it works...)
|
||||
c1();
|
||||
ASSERT (randVal + 23 + randVal == command1::checksum_);
|
||||
ASSERT (randVal + 23 + randVal == command1::check_);
|
||||
c1.undo();
|
||||
ASSERT (randVal + 23 == command1::checksum_);
|
||||
ASSERT (randVal + 23 == command1::check_);
|
||||
// note we've overwritten the previous undo state
|
||||
// and get the sate captured on the second invocation
|
||||
|
||||
c2.undo()
|
||||
ASSERT (randVal == command1::checksum_);
|
||||
ASSERT (randVal == command1::check_);
|
||||
c1.undo();
|
||||
ASSERT (randVal + 23 == command1::checksum_);
|
||||
ASSERT (randVal + 23 == command1::check_);
|
||||
|
||||
// use the current sate of c2 as Prototype for new command definition
|
||||
c2.storeDef ("test.command1.4");
|
||||
Command c3 = Command::get("test.command1.4");
|
||||
ASSERT (c3);
|
||||
ASSERT (!c3.canUndo());
|
||||
c3();
|
||||
ASSERT (randVal + 2*23 == command1::check_);
|
||||
|
||||
c3.bind(-command1::check_);
|
||||
c3();
|
||||
ASSERT (0 == command1::check_);
|
||||
c2();
|
||||
ASSERT (23 == command1::check_);
|
||||
c2.undo();
|
||||
ASSERT (0 == command1::check_);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
undef()
|
||||
{
|
||||
ASSERT (CommandDef ("test.command1.1"))
|
||||
ASSERT (CommandDef ("test.command1.2"))
|
||||
ASSERT (CommandDef ("test.command1.3"))
|
||||
ASSERT (CommandDef ("test.command1.4"))
|
||||
|
||||
ASSERT (Command::get("test.command1.1"));
|
||||
ASSERT (Command::get("test.command1.2"));
|
||||
ASSERT (Command::get("test.command1.3"));
|
||||
ASSERT (Command::get("test.command1.4"));
|
||||
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle"));
|
||||
|
||||
CommandDef unbelievable ("miracle");
|
||||
ASSERT (!unbelievable);
|
||||
|
||||
// but because the miracle isn't yet defined, any use throws
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle"));
|
||||
VERIFY_ERROR (UNBOUND_ARGUMENTS, unbelievable.execSync() );
|
||||
VERIFY_ERROR (INVALID_COMMAND, unbelievable.bind("abracadabra"));
|
||||
|
||||
ASSERT (Command::remove("test.command1.1"));
|
||||
ASSERT (Command::remove("test.command1.2"));
|
||||
ASSERT (Command::remove("test.command1.3"));
|
||||
|
||||
ASSERT (!Command::remove("miracle")); // there is no such thing...
|
||||
|
||||
// note, we didn't remove the *definitions*
|
||||
// thus we're free to create new instances...
|
||||
Command xxx = Command::get("test.command1.1");
|
||||
VERIFY_ERROR (UNBOUND_ARGUMENTS, xxx() );
|
||||
|
||||
// but this one is still there (we didn't remove it)
|
||||
ASSERT (Command::get("test.command1.4"));
|
||||
|
||||
// now kill the definitions too...
|
||||
ASSERT (Command::undef ("test.command1.1"));
|
||||
ASSERT (Command::undef ("test.command1.2"));
|
||||
ASSERT (Command::undef ("test.command1.3"));
|
||||
ASSERT (Command::undef ("test.command1.4"));
|
||||
ASSERT (Command::undef ("miracle"));
|
||||
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.1"));
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.2"));
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.3"));
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("miracle"));
|
||||
|
||||
// note: removing the definition automatically killed the remaining instance:
|
||||
VERIFY_ERROR (INVALID_COMMAND, Command::get("test.command1.4"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
208
tests/components/proc/control/command-use2-test.cpp
Normal file
208
tests/components/proc/control/command-use2-test.cpp
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
CommandUse2(Test) - usage aspects II
|
||||
|
||||
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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
//#include "proc/asset/media.hpp"
|
||||
//#include "proc/mobject/session.hpp"
|
||||
//#include "proc/mobject/session/edl.hpp"
|
||||
//#include "proc/mobject/session/testclip.hpp"
|
||||
//#include "proc/mobject/test-dummy-mobject.hpp"
|
||||
//#include "lib/p.hpp"
|
||||
//#include "proc/mobject/placement.hpp"
|
||||
//#include "proc/mobject/placement-index.hpp"
|
||||
//#include "proc/mobject/explicitplacement.hpp"
|
||||
#include "proc/control/command-def.hpp"
|
||||
//#include "lib/lumitime.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "proc/control/test-dummy-commands.hpp"
|
||||
|
||||
#include <tr1/functional>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/format.hpp>
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace control {
|
||||
namespace test {
|
||||
|
||||
|
||||
using boost::format;
|
||||
using boost::str;
|
||||
//using lumiera::Time;
|
||||
//using util::contains;
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
using std::string;
|
||||
//using std::rand;
|
||||
//using std::cout;
|
||||
//using std::endl;
|
||||
// using lib::test::showSizeof;
|
||||
// using util::isSameObject;
|
||||
using util::contains;
|
||||
using boost::ref;
|
||||
|
||||
// using session::test::TestClip;
|
||||
// using lumiera::P;
|
||||
|
||||
|
||||
//using lumiera::typelist::BuildTupleAccessor;
|
||||
using lumiera::error::LUMIERA_ERROR_EXTERNAL;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* @test command usage aspects II: patterns of invoking commands.
|
||||
*
|
||||
* @see Command
|
||||
* @see command-basic-test.cpp (simple usage example)
|
||||
*/
|
||||
class CommandUse2_test : public Test
|
||||
{
|
||||
|
||||
int randVal_;
|
||||
|
||||
string randomTxt()
|
||||
{
|
||||
format fmt ("invoked( %2d )");
|
||||
|
||||
randVal_ = rand() % 100;
|
||||
return str (fmt % randVal_);
|
||||
}
|
||||
|
||||
bool blowUp_ = false;
|
||||
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
command2::check_.seekp(0);
|
||||
uint cnt_defs = Command::definition_count();
|
||||
uint cnt_inst = Command::instance_count();
|
||||
|
||||
function<string()> randFun = bind (&CommandUse2_test::randomTxt, this);
|
||||
|
||||
// prepare a command definition (prototype)
|
||||
CommandDef ("test.command2")
|
||||
.operation (command2::operate)
|
||||
.captureUndo (command2::capture)
|
||||
.undoOperation (command2::undoIt)
|
||||
.bind (randFun, ref(blowUp_));
|
||||
|
||||
// note: blowUp_ is bound via reference_wrapper,
|
||||
// so we can pull the trigger to provoke an exception
|
||||
blowUp_ = false;
|
||||
|
||||
|
||||
check_defaultHandlingPattern();
|
||||
check_ThrowOnError();
|
||||
|
||||
|
||||
Command::undef ("test.command2");
|
||||
Command::undef ("test.command2.1");
|
||||
ASSERT (cnt_defs == Command::definition_count());
|
||||
ASSERT (cnt_inst == Command::instance_count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
check_defaultHandlingPattern()
|
||||
{
|
||||
Command com = Command::get("test.command2");
|
||||
|
||||
ASSERT (!contains (command2::check_, "invoked"));
|
||||
|
||||
bool res = com();
|
||||
|
||||
ASSERT (res);
|
||||
ASSERT (contains (command2::check_, "invoked"));
|
||||
ASSERT (contains (command2::check_, randVal_));
|
||||
|
||||
res = com.undo();
|
||||
ASSERT (res); // UNDO invoked successfully
|
||||
ASSERT (!contains (command2::check_, randVal_));
|
||||
ASSERT (contains (command2::check_, "UNDO"));
|
||||
|
||||
blowUp_ = true;
|
||||
string current = command2::check_.str();
|
||||
|
||||
res = com();
|
||||
ASSERT (!res); // not executed successfully (exception thrown)
|
||||
ASSERT (command2::check_.str() == current);
|
||||
ASSERT (LUMIERA_ERROR_EXTERNAL == lumiera_error());
|
||||
|
||||
res = com.undo();
|
||||
ASSERT (!res); // UNDO failed (exception thrown)
|
||||
ASSERT (command2::check_.str() == current);
|
||||
ASSERT (LUMIERA_ERROR_EXTERNAL == lumiera_error());
|
||||
|
||||
blowUp_ = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
check_ThrowOnError()
|
||||
{
|
||||
Command com = Command::get("test.command2");
|
||||
|
||||
blowUp_ = false;
|
||||
com.exec(HandlingPattern::THROW_SYNC);
|
||||
ASSERT (contains (command2::check_, randVal_));
|
||||
|
||||
blowUp_ = true;
|
||||
string current = command2::check_.str();
|
||||
VERIFY_ERROR( EXTERNAL, com.exec(HandlingPattern::THROW_SYNC) );
|
||||
ASSERT (command2::check_.str() == current);
|
||||
|
||||
// we can achieve the same effect,
|
||||
// after changing the default HandlingPatern for this command instance
|
||||
com.setHandlingPattern(HandlingPattern::THROW_SYNC);
|
||||
com.storeDef ("test.command2.1");
|
||||
|
||||
Command com2 = Command::get("test.command2.1");
|
||||
VERIFY_ERROR( EXTERNAL, com2() );
|
||||
ASSERT (command2::check_.str() == current);
|
||||
|
||||
blowUp_ = false;
|
||||
com2();
|
||||
ASSERT (command2::check_.str() > current);
|
||||
ASSERT (contains (command2::check_, randVal_));
|
||||
|
||||
com2.undo();
|
||||
ASSERT (!contains (command2::check_, randVal_));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (CommandUse2_test, "function controller");
|
||||
|
||||
|
||||
}} // namespace control::test
|
||||
121
tests/components/proc/control/command-use3-test.cpp
Normal file
121
tests/components/proc/control/command-use3-test.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
CommandUse3(Test) - usage aspects III
|
||||
|
||||
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.
|
||||
|
||||
* *****************************************************/
|
||||
|
||||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
//#include "proc/asset/media.hpp"
|
||||
//#include "proc/mobject/session.hpp"
|
||||
//#include "proc/mobject/session/edl.hpp"
|
||||
//#include "proc/mobject/session/testclip.hpp"
|
||||
//#include "proc/mobject/test-dummy-mobject.hpp"
|
||||
//#include "lib/p.hpp"
|
||||
//#include "proc/mobject/placement.hpp"
|
||||
//#include "proc/mobject/placement-index.hpp"
|
||||
//#include "proc/mobject/explicitplacement.hpp"
|
||||
#include "proc/control/command-def.hpp"
|
||||
//#include "lib/lumitime.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include "proc/control/test-dummy-commands.hpp"
|
||||
|
||||
//#include <tr1/functional>
|
||||
//#include <boost/ref.hpp>
|
||||
//#include <boost/format.hpp>
|
||||
//#include <iostream>
|
||||
//#include <cstdlib>
|
||||
//#include <string>
|
||||
|
||||
|
||||
namespace control {
|
||||
namespace test {
|
||||
|
||||
|
||||
// using boost::format;
|
||||
// using boost::str;
|
||||
//using lumiera::Time;
|
||||
//using util::contains;
|
||||
// using std::tr1::function;
|
||||
// using std::tr1::bind;
|
||||
// using std::string;
|
||||
//using std::rand;
|
||||
//using std::cout;
|
||||
//using std::endl;
|
||||
// using lib::test::showSizeof;
|
||||
// using util::isSameObject;
|
||||
// using util::contains;
|
||||
|
||||
// using session::test::TestClip;
|
||||
// using lumiera::P;
|
||||
|
||||
|
||||
//using lumiera::typelist::BuildTupleAccessor;
|
||||
// using lumiera::error::LUMIERA_ERROR_EXTERNAL;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* @test command usage aspects III: elaborate handling patterns, like e.g.
|
||||
* asynchronous or repeated invocation and command sequence bundles.
|
||||
*
|
||||
* @todo planned but not implemented as of 7/09
|
||||
*
|
||||
* @see HandlingPattern
|
||||
*/
|
||||
class CommandUse3_test : public Test
|
||||
{
|
||||
|
||||
|
||||
virtual void
|
||||
run (Arg)
|
||||
{
|
||||
command1::check_ = 0;
|
||||
uint cnt_defs = Command::definition_count();
|
||||
uint cnt_inst = Command::instance_count();
|
||||
|
||||
// prepare a command definition (prototype)
|
||||
CommandDef ("test.command1.1")
|
||||
.operation (command1::operate)
|
||||
.captureUndo (command1::capture)
|
||||
.undoOperation (command1::undoIt);
|
||||
|
||||
UNIMPLEMENTED ("more elaborate command handling patterns")
|
||||
////////////////////////////////////////////////////////////////////////////////TODO: devise tests for async, repeated and compound sequences
|
||||
|
||||
ASSERT (cnt_inst == Command::instance_count());
|
||||
|
||||
Command::undef ("test.command1.1");
|
||||
ASSERT (cnt_defs == Command::definition_count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (CommandUse3_test, "function controller");
|
||||
|
||||
|
||||
}} // namespace control::test
|
||||
|
|
@ -61,42 +61,88 @@
|
|||
//#include <string>
|
||||
|
||||
//using boost::format;
|
||||
|
||||
//using lumiera::Time;
|
||||
//using util::contains;
|
||||
//using std::string;
|
||||
//using std::rand;
|
||||
//using std::cout;
|
||||
//using std::endl;
|
||||
#include <tr1/functional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace control {
|
||||
namespace test {
|
||||
|
||||
// using lib::test::showSizeof;
|
||||
using std::ostringstream;
|
||||
using std::tr1::function;
|
||||
using std::string;
|
||||
|
||||
|
||||
|
||||
|
||||
namespace command1 { ///< test command just adding a given value
|
||||
|
||||
long checksum_ = 0;
|
||||
long check_ = 0;
|
||||
|
||||
void
|
||||
operate (int someVal)
|
||||
{
|
||||
checksum_ += someVal;
|
||||
check_ += someVal;
|
||||
}
|
||||
|
||||
long
|
||||
capture (int)
|
||||
{
|
||||
return checksum_;
|
||||
return check_;
|
||||
}
|
||||
|
||||
void
|
||||
undoIt (int, long oldVal)
|
||||
{
|
||||
checksum_ = oldVal;
|
||||
check_ = oldVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace command2 { ///< test command writing to protocol and possibly throwing
|
||||
|
||||
using lumiera::error::External;
|
||||
|
||||
|
||||
ostringstream check_;
|
||||
|
||||
|
||||
typedef function<string()> FunS;
|
||||
|
||||
void
|
||||
operate (FunS func, bool fail)
|
||||
{
|
||||
if (fail) throw External("simulated exception");
|
||||
|
||||
check_ << func();
|
||||
}
|
||||
|
||||
string
|
||||
capture (FunS, bool)
|
||||
{
|
||||
return check_.str();
|
||||
}
|
||||
|
||||
void
|
||||
undoIt (FunS, bool fail, string previousProtocol)
|
||||
{
|
||||
if (fail) throw External("simulated exception in UNDO");
|
||||
|
||||
check_.seekp(0);
|
||||
check_ << previousProtocol << "|UNDO|";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1012,20 +1012,20 @@ A command may be [[defined|CommandDefinition]] completely from scratch, or it mi
|
|||
By design, commands are single-serving value objects; executing an operation repeatedly requires creating a collection of command objects, one for each invocation. While nothing prevents you from invoking the command operation functor several times, each invocation will overwrite the undo state captrued by the previous invocation. Thus, each command instance should bes seen as the promise (or later the trace) of a single operation execution. In a similar vein, the undo capturing should be defined as to be self sufficient, so that invoking just the undo functor of a single command performes any necessary steps to restore the situation found before invoking the corresponding mutation functor &mdash; of course only //with respect to the topic covered by this command.// So, while commands provide a lot of flexibility and allow to do a multitude of things, certainly there is an intended CommandLifecycle.
|
||||
&rarr; more on possible [[command usage scenarios|CommandUsage]]</pre>
|
||||
</div>
|
||||
<div title="CommandLifecycle" modifier="Ichthyostega" modified="200907210211" created="200907210135" tags="SessionLogic spec draft design img" changecount="5">
|
||||
<div title="CommandLifecycle" modifier="Ichthyostega" modified="200907240010" created="200907210135" tags="SessionLogic spec draft design img" changecount="10">
|
||||
<pre>[<img[Structure of Commands|uml/fig135173.png]]
|
||||
While generally the command framework was designed to be flexible and allow a lot of different use cases, execution paths and to serve various goals, there is an ''intended lifecycle'' &mdash; commands are expected to go through several distinct states.
|
||||
|
||||
The handling of a command starts out with a ''command ID'' provided by the client code. Command IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all these informations are available, the entity is called a ''command definition''. It is comparable to a //class// or //meta object.//
|
||||
The handling of a command starts out with a ''command ID'' provided by the client code. Command IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all these informations are available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.//
|
||||
|
||||
By ''binding'' to specific operation arguments, the definition is //armed// and becomes a real ''command''. This is similar to creating an instance from a class
|
||||
By ''binding'' to specific operation arguments, the definition is //armed up//&nbsp; and becomes a real ''command''. This is similar to creating an instance from a class. Behind the scenes, storage is allocated to hold the argument values and any state captured to create the ability to UNDO the command's effect later on.
|
||||
|
||||
A command is operated or executed by passing it to an ''execution pattern'' &mdash; there is a multitude of possible execution patterns to choose from, depending on the situation.
|
||||
{{red{WIP}}}
|
||||
When a command has been executed (and maybe undone), it's best to leave it alone, because the UndoManager might hold a reference. At any time, c ''clone of the command'' could be created, maybe bound with different arguments and treated separately from the original command.
|
||||
When a command has been executed (and maybe undone), it's best to leave it alone, because the UndoManager might hold a reference. Anyway, a ''clone of the command'' could be created, maybe bound with different arguments and treated separately from the original command.
|
||||
</pre>
|
||||
</div>
|
||||
<div title="CommandUsage" modifier="Ichthyostega" modified="200907230031" created="200907212338" tags="SessionLogic draft dynamic" changecount="4">
|
||||
<div title="CommandUsage" modifier="Ichthyostega" modified="200907232209" created="200907212338" tags="SessionLogic draft dynamic" changecount="5">
|
||||
<pre>//for now (7/09) I'll use this page to collect ideas how commands might be used...//
|
||||
|
||||
* use a command for getting a log entry and an undo possibility automatically
|
||||
|
|
@ -1052,7 +1052,7 @@ When a command has been executed (and maybe undone), it's best to leave it alone
|
|||
|
||||
!!!an execution pattern....
|
||||
* can only be defined in code by a class definition, not at runtime
|
||||
* subclasses the !HandlingPattern interface, which automatically creates an registration.
|
||||
* subclasses the ~HandlingPattern interface, which automatically creates an registration.
|
||||
* a singleton instance is created on demand, triggered by referring the pattern's ID
|
||||
* is conceptually //stateless// &mdash; of course there can be common configuration values
|
||||
* is always invoked providing a concrete command instance to execute
|
||||
|
|
|
|||
Loading…
Reference in a new issue