Command handling pattern? test-driven brainstorming

This commit is contained in:
Fischlurch 2009-09-29 18:09:34 +02:00
parent 843f44795e
commit e9b95e47cf
6 changed files with 359 additions and 9 deletions

View file

@ -96,6 +96,7 @@ namespace control {
{ SYNC
, SYNC_THROW
, ASYNC
, DUMMY
, NUM_IDS
};

View file

@ -35,13 +35,6 @@ out: RESET...undoIt\(time=00:..:....00\)----memento-:START...doIt\( Time=00:..:.
END
TEST "Command functor and UNDO functor" CommandMutation_test <<END
out: empty placeholder closure: Closure\(0\)
out: param values: Closure\(23\)
out: saved state: 11
END
TEST "build argument accepting function" AcceptArgumentTuple_test <<END
out: sizeof\( .+control.+TestClass.+ \) = 1
out: sizeof\( .+control.+TestClass.+lumiera.Time.+ \) = (12)|(16)
@ -50,6 +43,13 @@ return: 0
END
TEST "Command functor and UNDO functor" CommandMutation_test <<END
out: empty placeholder closure: Closure\(0\)
out: param values: Closure\(23\)
out: saved state: 11
END
TEST "Memento wiring and storage" MementoTie_test <<END
return: 0
END
@ -60,6 +60,16 @@ return: 0
END
PLANNED "handling pattern basics" HandlingPatternBasics_test <<END
return: 0
END
PLANNED "handling patterns" HandlingPatternStandardImpl_test <<END
return: 0
END
PLANNED "Command usage aspects I" CommandUse1_test <<END
return: 0
END

View file

@ -56,6 +56,8 @@ test_components_SOURCES = \
$(testcomponents_srcdir)/proc/control/command-registry-test.cpp \
$(testcomponents_srcdir)/proc/control/argument-tuple-accept-test.cpp \
$(testcomponents_srcdir)/proc/control/memento-tie-test.cpp \
$(testcomponents_srcdir)/proc/control/handling-pattern-basics-test.cpp \
$(testcomponents_srcdir)/proc/control/handling-pattern-standard-impl-test.cpp \
$(testcomponents_srcdir)/proc/engine/buff-table-test.cpp \
$(testcomponents_srcdir)/proc/engine/node-basic-test.cpp \
$(testcomponents_srcdir)/proc/engine/node-fabrication-test.cpp \

View file

@ -0,0 +1,194 @@
/*
HandlingPatternBasics(Test) - verify elementary operation of a command handling pattern
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/control/command.hpp"
#include "proc/control/command-impl.hpp"
#include "proc/control/command-registry.hpp"
#include "proc/control/argument-erasure.hpp"
#include "proc/control/handling-pattern.hpp"
//#include "lib/lumitime.hpp"
#include "lib/symbol.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 lib::Symbol;
//using lumiera::typelist::BuildTupleAccessor;
// using lumiera::error::LUMIERA_ERROR_EXTERNAL;
namespace { // test data and helpers...
Symbol TEST_CMD = "test.command1.handling";
HandlingPattern::ID TEST_PATTERN = HandlingPattern::DUMMY;
}
typedef shared_ptr<CommandImpl> PCommandImpl;
typedef HandlingPattern const& HaPatt;
/**********************************************************************************
* @test operate and verify a simple dummy command handling pattern.
* interface. Add/remove a command instance to the index, allocate an
* CommandImpl frame and verify it is removed properly on ref count zero.
* @note this test covers mainly the behaviour of a handling pattern as a concept,
* not so much the behaviour of the (standard) handling pattern implementations.
*
* @see HandlingPattern
* @see CommandRegistry
* @see command.hpp
* @see command-basic-test.cpp
*/
class HandlingPatternBasics_test : public Test
{
uint cnt_inst;
virtual void
run (Arg)
{
CommandRegistry& registry = CommandRegistry::instance();
ASSERT (&registry);
cnt_inst = registry.instance_count();
{
PCommandImpl pCom = buildTestCommand(registry);
checkExec (pCom);
checkUndo (pCom);
}
ASSERT (cnt_inst == registry.instance_count());
}
/** create a command implementation frame usable for tests.
* This simulates what normally happens within a CommandDef.
* The created CommandImpl isn't registered, and thus will
* just go away when the smart-ptr leaves scope.
*/
PCommandImpl
buildTestCommand (CommandRegistry& registry)
{
typedef void Sig_oper(int);
typedef long Sig_capt(int);
typedef void Sig_undo(int,long);
function<Sig_oper> o_Fun (command1::operate);
function<Sig_capt> c_Fun (command1::capture);
function<Sig_undo> u_Fun (command1::undoIt);
ASSERT (o_Fun && c_Fun && u_Fun);
// when the CommandDef is complete, it issues the
// allocation call to the registry behind the scenes....
PCommandImpl pImpl = registry.newCommandImpl(o_Fun,c_Fun,u_Fun);
ASSERT (pImpl);
ASSERT (*pImpl);
return pImpl;
}
void
checkExec (PCommandImpl com)
{
ASSERT (com);
ASSERT (!com->canExec());
typedef Types<int> ArgType;
const int ARGU (1 + rand() % 1000);
Tuple<ArgType> tuple(ARGU);
TypedArguments<Tuple<ArgType> > arg(tuple);
com->setArguments(arg);
ASSERT (com->canExec());
ASSERT (!com->canUndo());
command1::check_ = 0;
HaPatt patt = HandlingPattern::get(TEST_PATTERN);
ExecResult res = patt.invoke(*com, TEST_CMD);
ASSERT (res);
ASSERT (ARGU == command1::check_);
ASSERT (com->canUndo());
}
void
checkUndo (PCommandImpl com)
{
ASSERT (com);
ASSERT (com->canExec());
ASSERT (com->canUndo());
ASSERT (command1::check_ > 0);
HaPatt ePatt = HandlingPattern::get(TEST_PATTERN);
HaPatt uPatt = ePatt.howtoUNDO();
ExecResult res = uPatt.invoke(*com, TEST_CMD);
ASSERT (res);
ASSERT (command1::check_ == 0);
}
};
/** Register this test class... */
LAUNCHER (HandlingPatternBasics_test, "function controller");
}} // namespace control::test

View file

@ -0,0 +1,126 @@
/*
HandlingPatternStandardImpl(Test) - cover the provided standard command handling patterns
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.hpp"
#include "proc/control/command-impl.hpp"
#include "proc/control/command-registry.hpp"
//#include "proc/control/command-def.hpp"
//#include "lib/lumitime.hpp"
//#include "lib/symbol.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 lib::Symbol;
// using lumiera::P;
//using lumiera::typelist::BuildTupleAccessor;
// using lumiera::error::LUMIERA_ERROR_EXTERNAL;
namespace { // test data and helpers...
// Symbol TEST_CMD = "test.command1.1";
// Symbol TEST_CMD2 = "test.command1.2";
}
/***********************************************************************
* @test verify correct behaviour of all the command handling patterns
* provided by the default configuration of a lumiera session.
* - executing quasi synchronous
* - the throw-on-error variant
* - background execution
* - ...?
* @todo define and code those handling patterns; Ticket #210
*
* @see HandlingPattern
* @see CommandRegistry
* @see command.hpp
* @see command-basic-test.cpp
*/
class HandlingPatternStandardImpl_test : public Test
{
uint cnt_inst;
virtual void
run (Arg)
{
CommandRegistry& registry = CommandRegistry::instance();
ASSERT (&registry);
cnt_inst = registry.instance_count();
UNIMPLEMENTED ("unit test to cover the standard command handling patterns");
ASSERT (cnt_inst == registry.instance_count());
}
};
/** Register this test class... */
LAUNCHER (HandlingPatternStandardImpl_test, "function controller");
}} // namespace control::test

View file

@ -995,7 +995,7 @@ While generally there is //no limitation// on the number and type of parameters,
Usually, parameters should be passed //by value// &amp;mdash; with the exception of target object(s), which are typically bound as MObjectRef, causing them to be resolved at commad execution time (late binding).
</pre>
</div>
<div title="CommandHandling" modifier="Ichthyostega" modified="200907212323" created="200906072048" tags="SessionLogic spec draft decision design img" changecount="26">
<div title="CommandHandling" modifier="Ichthyostega" modified="200909291418" created="200906072048" tags="SessionLogic spec draft decision design img" changecount="27">
<pre>Organising any ''mutating'' operations executable by the user (via GUI) by means of the [[command pattern|http://en.wikipedia.org/wiki/Command_pattern]] can be considered //state of the art//&amp;nbsp; today. First of all, it allows to discern the specific implementation operations to be called on one or several objects within the HighLevelModel from the operation requested by the user, the latter being rather a concept. A command can be labeled clearly, executed under controlled circumstances, allowing transactional behaviour.
@ -1014,7 +1014,24 @@ While the usual »Memento« implementation might automatically capture the whole
A command may be [[defined|CommandDefinition]] completely from scratch, or it might just serve as a CommandPrototype with specific targets and parameters. The command could then be serialised and later be recovered and re-bound with the parameters, but usually it will be handed over to the ProcDispatcher, pending execution. When ''invoking'', the handling sequence is to [[log the command|SessionStorage]], then call the ''undo capture function'', followed from calling the actual ''operation function''. After success, the logging and [[undo registration|UndoManager]] is completed. In any case, finally the ''result signal'' (a functor previously stored within the command) is emitted.
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 &amp;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.
&amp;rarr; more on possible [[command usage scenarios|CommandUsage]]</pre>
&amp;rarr; more on possible [[command usage scenarios|CommandUsage]]
&amp;rarr; more details regarding [[command implementation|CommandImpl]]
</pre>
</div>
<div title="CommandImpl" modifier="Ichthyostega" modified="200909291434" created="200909291424" tags="spec impl" changecount="9">
<pre>Commands are separated in a handle (the {{{control::Command}}}-object), to be used by the client code, and an implementation level, which is managed transparently behind the stages. Client code is assumed to build a CommandDefinition at some point, and from then on to access the command ''by ID'', yielding the command handle.
Binding of arguments, invocation and UNDO all are accessible through this frontend.
!Infrastructure
To support this handling scheme, some infrastructure is in place:
* a command registry maintains the ID &amp;harr; Command relation.
* indirectly, through a custom alloctaor, the registry is also involved into allocation of the command implementation frame
* this implementation frame combines
** an operation mutation and an undo mutation
** a closure, implemented through an argument holder
** an undo state capturing mechanism, based on a capturing function provided on definition
* performing the actual execution is delegated to a handling pattern object, accessed by name.
</pre>
</div>
<div title="CommandLifecycle" modifier="Ichthyostega" modified="200907240010" created="200907210135" tags="SessionLogic spec draft design img" changecount="10">
<pre>[&lt;img[Structure of Commands|uml/fig135173.png]]