the intention is to cover more of the full invocation path, without running all of the application infrastructure. So this second test cases simulates how messages are handled in CoreService, where the CommandHandler (visitor) actually invokes the SessionCommand facade
262 lines
8.3 KiB
C++
262 lines
8.3 KiB
C++
/*
|
|
SessionCommandFunction(Test) - function test of command dispatch via SessionCommand facade
|
|
|
|
Copyright (C) Lumiera.org
|
|
2017, 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"
|
|
extern "C" {
|
|
#include "common/interfaceregistry.h"
|
|
}
|
|
|
|
#include "proc/control/proc-dispatcher.hpp"
|
|
#include "proc/control/command-def.hpp"
|
|
#include "gui/ctrl/command-handler.hpp"
|
|
#include "gui/interact/invocation-trail.hpp"
|
|
#include "lib/format-obj.hpp"
|
|
#include "lib/format-cout.hpp" //////////TODO
|
|
#include "lib/symbol.hpp"
|
|
#include "lib/util.hpp"
|
|
|
|
//#include <cstdlib>
|
|
#include <string>
|
|
|
|
|
|
namespace proc {
|
|
namespace control {
|
|
namespace test {
|
|
|
|
|
|
// using std::function;
|
|
// using std::rand;
|
|
using lib::test::randTime;
|
|
using gui::interact::InvocationTrail;
|
|
using gui::ctrl::CommandHandler;
|
|
using lib::diff::GenNode;
|
|
using lib::diff::Rec;
|
|
using lib::time::Time;
|
|
using lib::time::TimeVar;
|
|
using lib::time::Duration;
|
|
using lib::time::Offset;
|
|
using lib::time::FSecs;
|
|
using lib::Symbol;
|
|
using util::isnil;
|
|
using util::toString;
|
|
using std::string;
|
|
|
|
|
|
namespace { // test fixture...
|
|
|
|
/* === mock operation to be dispatched as command === */
|
|
|
|
const Symbol COMMAND_ID{"test.dispatch.function.command"};
|
|
|
|
TimeVar testCommandState = randTime();
|
|
|
|
void
|
|
operate (Duration dur, Offset offset, int factor)
|
|
{
|
|
testCommandState += Offset(dur) + offset*factor;
|
|
}
|
|
|
|
Time
|
|
capture (Duration, Offset, int)
|
|
{
|
|
return testCommandState;
|
|
}
|
|
|
|
void
|
|
undoIt (Duration, Offset, int, Time oldState)
|
|
{
|
|
testCommandState = oldState;
|
|
}
|
|
|
|
|
|
}//(End) test fixture
|
|
|
|
// typedef shared_ptr<CommandImpl> PCommandImpl;
|
|
// typedef HandlingPattern const& HaPatt;
|
|
#define __DELAY__ usleep(10000);
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************************//**
|
|
* @test verify integrated functionality of command dispatch through the SessionCommand facade.
|
|
* - operate lifecycle of the supporting components,
|
|
* similar to activating the »session subsystem«
|
|
* - generate command messages similar to what is received from the UI-Bus
|
|
* - us the handler mechanism from gui::ctrl::CoreService to talk to the facade
|
|
* - have a specially rigged command function to observe invocation
|
|
* - wait for the session loop thread to dispatch this command
|
|
* - verify that commands are really executed single-threaded
|
|
*
|
|
* @see proc::SessionSubsystem
|
|
* @see ProcDispatcher
|
|
* @see CommandQueue_test
|
|
* @see AbstractTangible_test::invokeCommand()
|
|
*/
|
|
class SessionCommandFunction_test : public Test
|
|
{
|
|
|
|
//------------------FIXTURE
|
|
public:
|
|
SessionCommandFunction_test()
|
|
{
|
|
CommandDef (COMMAND_ID)
|
|
.operation (operate)
|
|
.captureUndo (capture)
|
|
.undoOperation (undoIt)
|
|
;
|
|
}
|
|
~SessionCommandFunction_test()
|
|
{
|
|
Command::remove (COMMAND_ID);
|
|
}
|
|
//-------------(End)FIXTURE
|
|
|
|
|
|
virtual void
|
|
run (Arg)
|
|
{
|
|
lumiera_interfaceregistry_init();
|
|
lumiera::throwOnError();
|
|
|
|
startDispatcher();
|
|
perform_simpleInvocation();
|
|
// perform_messageInvocation();
|
|
// perform_massivelyParallel();
|
|
stopDispatcher();
|
|
|
|
lumiera_interfaceregistry_destroy();
|
|
}
|
|
|
|
|
|
/** @test start the session loop thread, similar
|
|
* to what the »session subsystem« does
|
|
* @note we are _not_ actually starting the subsystem
|
|
* @see facade.cpp
|
|
*/
|
|
void
|
|
startDispatcher()
|
|
{
|
|
CHECK (not ProcDispatcher::instance().isRunning());
|
|
|
|
ProcDispatcher::instance().start ([&] (string* problemMessage)
|
|
{
|
|
CHECK (isnil (*problemMessage));
|
|
thread_has_ended = true;
|
|
});
|
|
|
|
CHECK (ProcDispatcher::instance().isRunning());
|
|
CHECK (not thread_has_ended);
|
|
}
|
|
bool thread_has_ended{false};
|
|
|
|
|
|
void
|
|
stopDispatcher()
|
|
{
|
|
CHECK (ProcDispatcher::instance().isRunning());
|
|
ProcDispatcher::instance().requestStop();
|
|
|
|
__DELAY__
|
|
CHECK (not ProcDispatcher::instance().isRunning());
|
|
CHECK (thread_has_ended);
|
|
}
|
|
|
|
|
|
void
|
|
perform_simpleInvocation()
|
|
{
|
|
string cmdID {COMMAND_ID};
|
|
Rec arguments {Duration(15,10), Time(500,0), -1};
|
|
|
|
CHECK (not Command(COMMAND_ID).canExec());
|
|
SessionCommand::facade().bindArg (cmdID, arguments);
|
|
CHECK (Command(COMMAND_ID).canExec());
|
|
|
|
|
|
Time prevState = testCommandState;
|
|
SessionCommand::facade().invoke(cmdID);
|
|
|
|
__DELAY__
|
|
CHECK (testCommandState - prevState == Time(0, 1)); // execution added 1500ms -1*500ms == 1sec
|
|
}
|
|
|
|
|
|
/** @test invoke a command in the same way as CoreService does
|
|
* when handling command messages from the UI-Bus
|
|
* - use the help of an InvocationTrail, similar to what the
|
|
* [generic UI element](\ref gui::model::Tangible) does
|
|
* - generate a argument binding message
|
|
* - generate a "bang!" message
|
|
*/
|
|
void
|
|
perform_messageInvocation()
|
|
{
|
|
// this happens "somewhere" in the UI interaction control framework
|
|
InvocationTrail invoTrail{Command(COMMAND_ID)};
|
|
|
|
// this happens within some tangible UI element (widget / controller)
|
|
GenNode argumentBindingMessage = invoTrail.bindMsg (Rec{Duration(25,10), Time(500,0), -2});
|
|
GenNode commandTriggerMessage = invoTrail.bangMsg ();
|
|
CHECK (argumentBindingMessage.idi == commandTriggerMessage.idi);
|
|
|
|
// this happens, when CoreService receives command messages from UI-Bus
|
|
CommandHandler handler1{argumentBindingMessage};
|
|
argumentBindingMessage.data.accept(handler1); // handler is a visitor for the message payload
|
|
|
|
CHECK (Command::canExec(COMMAND_ID));
|
|
CHECK (not Command::canUndo(COMMAND_ID));
|
|
Time prevState = testCommandState;
|
|
|
|
// now handling the message to trigger execution
|
|
CommandHandler handler2{commandTriggerMessage};
|
|
commandTriggerMessage.data.accept(handler2);
|
|
|
|
__DELAY__
|
|
CHECK (Command::canUndo(COMMAND_ID));
|
|
CHECK (testCommandState - prevState == Time(500, 1)); // execution added 2500ms -2*500ms == 1.5sec
|
|
}
|
|
|
|
|
|
/** @test verify that commands are properly enqueued
|
|
* and executed one by one
|
|
* - create several threads to send random command messages
|
|
* - verify that, after executing all commands, the internal
|
|
* state variable reflects the result of a proper
|
|
* sequential calculation and summation
|
|
*/
|
|
void
|
|
perform_massivelyParallel()
|
|
{
|
|
UNIMPLEMENTED ("verify sequentialisation by queue");
|
|
}
|
|
};
|
|
|
|
|
|
/** Register this test class... */
|
|
LAUNCHER (SessionCommandFunction_test, "function controller");
|
|
|
|
|
|
}}} // namespace proc::control::test
|