lumiera_/tests/core/proc/control/command-equality-test.cpp
Ichthyostega ada5cefaaf re-arrange tests according to layer structure
the buildsystem will now pick up and link
all test cases according to the layer, e.g.
backend tests will automatically be linked
against the backend + library solely.
2013-01-07 05:43:01 +01:00

321 lines
10 KiB
C++

/*
CommandEquality(Test) - verify equality comparisons on command's subsystems
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-def.hpp"
#include "proc/control/command-mutation.hpp"
#include "proc/control/argument-erasure.hpp"
#include "proc/control/command-argument-holder.hpp"
#include "proc/control/memento-tie.hpp"
#include "lib/meta/tuple.hpp"
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <tr1/functional>
#include <iostream>
#include <string>
namespace proc {
namespace control {
namespace test {
using namespace lib::meta;
using lib::Symbol;
using util::isnil;
using util::isSameObject;
using std::tr1::function;
using std::tr1::bind;
using std::string;
using std::cout;
using std::endl;
namespace {
string check_;
Symbol COMMAND1 ("test.equalityCommand1");
Symbol COMMAND2 ("test.equalityCommand2");
const string MARK_1 ("|_1_");
const string MARK_2 ("|_2_");
void oper_1 (char par) { check_ += MARK_1 + par; }
void oper_2 (char par) { check_ += MARK_2 + par; }
string capt_1 (char par) { return check_ + MARK_1 + "|"+par+"|"; }
string capt_2 (char par) { return check_ + MARK_2 + "|"+par+"|"; }
void undo_1 (char par, string mem) { check_ = mem + MARK_1 + par + "|";}
void undo_2 (char par, string mem) { check_ = mem + MARK_2 + par + "|";}
typedef void Sig_oper(char);
typedef string Sig_capt(char);
typedef void Sig_undo(char,string);
typedef function<Sig_oper> Fun_o;
typedef function<Sig_capt> Fun_c;
typedef function<Sig_undo> Fun_u;
typedef Tuple<Types<char> > ArgTuple;
typedef Closure<Sig_oper> ArgHolder;
typedef MementoTie<Sig_oper, string> MemHolder;
}
/*****************************************************************************************
* @test cover command equality detection. Two commands are deemed equivalent, if they
* - build on the same Mutation functors
* - are either both incomplete or
* - are bound to equivalent arguments
* - hold equivalent undo state (memento)
* To conduct this test, we set up two sets of functions, and then build both complete
* command objects and command implementation facilities based on them.
*
* @note The hidden problem with those comparisons is the equivalence of function objects.
* While required by TR1, unfortunately lib boost refuses to implement functor equality.
* Which forces us to resort to a low level hack, based on internals of the boost function
* implementation. This workaround reliably pinpoints differing functions, but sometimes
* fails to detect equivalent functions under specific circumstances (e.g. when there is
* binding involved, and / or the binders have been cloned). Bottom line: \c == is
* reliable, \c != might be wrong.
*
* @see control::Command
* @see control::CmdClosure
* @see control::Mutation
* @see control::UndoMutation
* @see control::MementoTie
* @see control::CommandImpl
* @see command-basic-test.hpp
* @see functor-util.hpp functor equality workaround
*/
class CommandEquality_test : public Test
{
virtual void
run (Arg)
{
CHECK (&oper_1 != &oper_2);
CHECK (&capt_1 != &capt_2);
CHECK (&undo_1 != &undo_2);
verifyMutationEquality();
verifyMementoEquality();
verifyClosureEquality();
verifyCommandEquality();
}
void
verifyMutationEquality()
{
Fun_o oFun_1 (oper_1);
Fun_o oFun_2 (oper_2);
Fun_o oFun_empty;
Fun_u uFun_1 (undo_1);
Fun_u uFun_empty;
Mutation mut1 (oFun_1);
Mutation muti (oFun_1);
Mutation mut2 (oFun_2);
CHECK (mut1 == mut1);
CHECK (mut1 == muti);
CHECK (muti == mut1);
CHECK (mut1 != mut2);
CHECK (mut2 != mut1);
CHECK (muti != mut2);
CHECK (mut2 != muti);
Mutation umu (oFun_empty); // empty operation function
CHECK (mut1 != umu);
Mutation mut_u0 (uFun_empty); // empty undo function
CHECK (mut_u0 != umu);
CHECK (mut_u0 != muti);
Mutation mut_u1 (uFun_1);
CHECK (mut_u0 != mut_u1); // function signatures differing
}
void
verifyClosureEquality()
{
ArgHolder a1 (tuple::make ('a'));
ArgHolder a2 (tuple::make ('z'));
CHECK (a1 == a1);
CHECK (a1 != a2);
CHECK (a2 != a1);
TypedArguments<ArgTuple> newArgs (tuple::make ('z'));
a1.bindArguments(newArgs);
CHECK (a1 == a2);
CHECK (a2 == a1);
typedef ArgumentHolder<Sig_oper,string> AHImpl;
AHImpl abuff1;
AHImpl abuff2;
CHECK (abuff1 == abuff2);
abuff1.bindArguments(newArgs);
CHECK (abuff1 != abuff2);
abuff2.bindArguments(newArgs);
CHECK (abuff1 == abuff2);
UndoMutation umu1 (abuff1.tie (undo_1, capt_1));
CHECK (abuff1 != abuff2); // abuff2 isn't tied yet, i.e. has no undo/capture function
UndoMutation umu2 (abuff2.tie (undo_1, capt_1));
CHECK (abuff1 == abuff2); // same capture function, no memento state!
umu1.captureState(a1);
CHECK (abuff1 != abuff2);
umu2.captureState(a1);
CHECK (abuff1 == abuff2); // same functions, same memento state
check_ += "fake"; // manipulate the "state" to be captured
umu2.captureState(a1); // capture again...
CHECK (abuff1 != abuff2); // captured memento differs!
UndoMutation umu3 (abuff2.tie (undo_1, capt_2));
umu3.captureState(a1);
CHECK (abuff1 != abuff2); // differing functions detected
}
void
verifyMementoEquality()
{
Fun_u uFun_1 (undo_1);
Fun_u uFun_2 (undo_2);
Fun_c cFun_1 (capt_1);
Fun_c cFun_2 (capt_2);
Fun_c cFun_empty;
Fun_c empty_c;
MemHolder m11 (uFun_1, cFun_1);
MemHolder m12 (uFun_1, cFun_2);
MemHolder m21 (uFun_2, cFun_empty); // note: unbound capture function
MemHolder m22 (uFun_2, cFun_2);
CHECK (m11 == m11);
CHECK (m12 == m12);
CHECK (m21 == m21);
CHECK (m22 == m22);
CHECK (!(m11 != m11));
CHECK (m11 != m12);
CHECK (m11 != m21);
CHECK (m11 != m22);
CHECK (m12 != m11);
CHECK (m12 != m21);
CHECK (m12 != m22);
CHECK (m21 != m11);
CHECK (m21 != m12);
CHECK (m21 != m22);
CHECK (m22 != m11);
CHECK (m22 != m12);
CHECK (m22 != m21);
MemHolder m22x (m22); // clone copy
CHECK (!m22x);
CHECK (m22 == m22x); // same functions, no state --> equal
m22x.tieCaptureFunc() ('x'); // produce a memento state
CHECK (!isnil (m22x.getState()));
CHECK (m22 != m22x);
m22.tieCaptureFunc() ('x'); // get same value into the memento within m22
CHECK (m22 == m22x);
// document shortcomings on UndoMutation comparisons
UndoMutation umu11 (m11);
UndoMutation umu12 (m11); // note: due to cloning the embedded functor,
CHECK (umu11 != umu12); // our hacked-in comparison operator fails
}
void
verifyCommandEquality()
{
CommandDef (COMMAND1)
.operation (oper_1)
.captureUndo (capt_1)
.undoOperation (undo_1)
;
CommandDef (COMMAND2)
.operation (oper_2)
.captureUndo (capt_2)
.undoOperation (undo_2)
;
Command c1 = Command::get(COMMAND1);
Command c2 = Command::get(COMMAND2);
CHECK (c1 == c1);
CHECK (c1 != c2);
CHECK (c2 != c1);
Command cx = c1;
CHECK (c1 == cx);
CHECK (cx == c1);
CHECK (!isSameObject (c1, c2));
// verify equality matches behaviour
string protocol1 = execCommand(c1);
string protocolX = execCommand(cx);
string protocol2 = execCommand(c2);
CHECK (protocol1 == protocolX);
CHECK (protocol1 != protocol2);
}
/** Helper: invoke and undo a command,
* @return resulting operation protocol
*/
string
execCommand (Command com)
{
check_ = "(start)";
com.bind('o');
com();
cout << com << ":" << check_ << endl;
com.undo();
cout << com << ":" << check_ << endl;
return check_;
}
};
/** Register this test class... */
LAUNCHER (CommandEquality_test, "function controller");
}}} // namespace proc::control::test