provide a mock handler for commands and state marks

in the real system, this will be the task of the CoreService,
while here, in test mode, we allow to install handling closures
from the unit-test-code
This commit is contained in:
Fischlurch 2016-01-03 03:23:39 +01:00
parent 16a70ce9f8
commit 540151b56b
5 changed files with 121 additions and 28 deletions

View file

@ -103,8 +103,12 @@ namespace ctrl{
/** add a new down-link connection to the routing table /** add a new down-link connection to the routing table
* @param identity the [endpoint-ID](\ref BusTerm::endpointID_) used * @param identity the [endpoint-ID](\ref BusTerm::endpointID_) used
* to address the new element to be connected to the bus. * to address the new element to be connected to the bus.
* @param newNode to add the address (!) into the routing table
* @return backlink for the new Tangible's BusTerm to * @return backlink for the new Tangible's BusTerm to
* attach itself to the Nexus. * attach itself to the Nexus.
* @note at call time, the second param, the newNode will typically
* be just a Tangible (and not a subclass yet),
* since this function is invoked from ctor.
*/ */
virtual BusTerm& virtual BusTerm&
routeAdd (ID identity, Tangible& newNode) override routeAdd (ID identity, Tangible& newNode) override

View file

@ -193,6 +193,7 @@ namespace test{
or entry.getType() == "destroy" or entry.getType() == "destroy"
or entry.getType() == "logJoin" or entry.getType() == "logJoin"
) )
and !isnil(entry.scope())
and contains (*entry.scope(), match); and contains (*entry.scope(), match);
}; };
} }
@ -210,6 +211,7 @@ namespace test{
or entry.getType() == classifier or entry.getType() == classifier
or (entry.hasAttribute("ID") and contains (entry.get("ID"), classifier)) or (entry.hasAttribute("ID") and contains (entry.get("ID"), classifier))
) )
and !isnil(entry.scope())
and contains (*entry.scope(), match); and contains (*entry.scope(), match);
}; };
} }
@ -682,9 +684,9 @@ namespace test{
EventLog& EventLog&
event (string text) event (string text)
{ {
log({"type=event", text}); log ("event", ArgSeq{}, ArgSeq{text}); // we use this ctor variant to ensure
return *this; return *this; // that text is not misinterpreted as attribute,
} } // which might happen when text contains a '='
/** log some event, with additional ID or classifier /** log some event, with additional ID or classifier
* @param classifier info to be saved into the `ID` attribute * @param classifier info to be saved into the `ID` attribute
@ -693,7 +695,7 @@ namespace test{
EventLog& EventLog&
event (string classifier, string text) event (string classifier, string text)
{ {
log({"type=event", "ID="+classifier, text}); log ("event", ArgSeq{"ID="+classifier}, ArgSeq{text});
return *this; return *this;
} }

View file

@ -36,6 +36,7 @@
//#include <map> //#include <map>
using lib::idi::EntryID; using lib::idi::EntryID;
using lib::idi::BareEntryID;
using gui::test::MockElm; using gui::test::MockElm;
using lib::diff::GenNode; using lib::diff::GenNode;
//using boost::lexical_cast; //using boost::lexical_cast;
@ -111,14 +112,14 @@ namespace test {
attachNewBusTerm () attachNewBusTerm ()
{ {
// our dummy will be linked with this identity // our dummy will be linked with this identity
EntryID<MockElm> elmID{"zeitgeist"}; BareEntryID elmID = EntryID<MockElm>{"zeitgeist"};
// Access the log on the Test-Nexus hub // Access the log on the Test-Nexus hub
EventLog nexusLog = gui::test::Nexus::startNewLog(); EventLog nexusLog = gui::test::Nexus::startNewLog();
CHECK (nexusLog.ensureNot("zeitgeist")); CHECK (nexusLog.ensureNot("zeitgeist"));
MockElm mock(elmID); MockElm mock(elmID);
CHECK (nexusLog.verifyCall("routeAdd").on("TestNexus").arg(elmID,"MockElm") CHECK (nexusLog.verifyCall("routeAdd").on("TestNexus").arg(elmID,"Tangible") // Note: invoked from ctor, so it is just a tangible at the moment
.beforeEvent("TestNexus", "added route to bID-zeitgeist")); .beforeEvent("TestNexus", "added route to bID-zeitgeist"));
EventLog elmLog = mock.getLog(); EventLog elmLog = mock.getLog();
@ -135,7 +136,7 @@ namespace test {
// invoke action on element to cause upstream message (with a "state mark") // invoke action on element to cause upstream message (with a "state mark")
mock.slotCollapse(); mock.slotCollapse();
CHECK (elmLog.verifyEvent("collapsed")); CHECK (elmLog.verifyEvent("collapsed"));
CHECK (nexusLog.verifyCall("note").on("TestNexus").arg("zeitgeist", "collapse")); CHECK (nexusLog.verifyCall("note").on("TestNexus").arg(elmID, "collapse"));
// send a state mark down to the mock element // send a state mark down to the mock element
gui::test::Nexus::testUI().mark (elmID, GenNode("Flash", 23)); gui::test::Nexus::testUI().mark (elmID, GenNode("Flash", 23));
@ -159,7 +160,7 @@ namespace test {
cout << "____Probe-Log_________________\n" cout << "____Probe-Log_________________\n"
<< util::join(mock.getLog(), "\n") << util::join(elmLog, "\n")
<< "\n───╼━━━━━━━━━╾────────────────"<<endl; << "\n───╼━━━━━━━━━╾────────────────"<<endl;
cout << "____Nexus-Log_________________\n" cout << "____Nexus-Log_________________\n"

View file

@ -55,8 +55,12 @@ using std::cerr;
using std::endl; using std::endl;
using std::string; using std::string;
using lib::test::EventLog; using lib::Variant;
using lib::diff::Rec;
using lib::diff::GenNode; using lib::diff::GenNode;
using lib::diff::DataValues;
using lib::idi::instanceTypeID;
using lib::test::EventLog;
using gui::ctrl::BusTerm; using gui::ctrl::BusTerm;
using util::_Fmt; using util::_Fmt;
//using util::contains; //using util::contains;
@ -67,10 +71,6 @@ namespace test{
namespace { // internal details namespace { // internal details
using lib::Variant;
using lib::diff::Rec;
using lib::diff::DataValues;
using BusHub = gui::ctrl::Nexus; using BusHub = gui::ctrl::Nexus;
@ -123,12 +123,19 @@ namespace test{
{ {
EventLog log_{this}; EventLog log_{this};
using CommandHandler = test::Nexus::CommandHandler;
using StateMarkHandler = test::Nexus::StateMarkHandler;
CommandHandler commandHandler_;
StateMarkHandler stateMarkHandler_;
virtual void virtual void
act (GenNode const& command) act (GenNode const& command)
{ {
log_.call(this, "act", command); log_.call(this, "act", command);
BusHub::act (command); commandHandler_(command);
log_.event("TestNexus", _Fmt("%s command \"%s\"%s") log_.event("TestNexus", _Fmt("%s command \"%s\"%s")
% invocationStage(command) % invocationStage(command)
% command.idi.getSym() % command.idi.getSym()
@ -139,7 +146,7 @@ namespace test{
note (ID subject, GenNode const& mark) override note (ID subject, GenNode const& mark) override
{ {
log_.call(this, "note", subject, mark); log_.call(this, "note", subject, mark);
BusHub::note (subject, mark); stateMarkHandler_(subject, mark);
log_.event("TestNexus", _Fmt("processed note from %s |%s") % subject % mark); log_.event("TestNexus", _Fmt("processed note from %s |%s") % subject % mark);
} }
@ -151,19 +158,14 @@ namespace test{
log_.event("TestNexus", _Fmt("delivered mark to %s |%s") % subject % mark); log_.event("TestNexus", _Fmt("delivered mark to %s |%s") % subject % mark);
} }
virtual operator string() const
{
return getID().getSym()+"."+lib::idi::instanceTypeID(this);
}
virtual BusTerm& virtual BusTerm&
routeAdd (ID identity, Tangible& newNode) override routeAdd (ID identity, Tangible& newNode) override
{ {
log_.call(this, "routeAdd", identity, newNode); log_.call(this, "routeAdd", identity, instanceTypeID(&newNode));
BusHub::routeAdd (identity, newNode); BusHub::routeAdd (identity, newNode);
log_.event("TestNexus", _Fmt("added route to %s |%s| table-size=%2d") log_.event("TestNexus", _Fmt("added route to %s |%s| table-size=%2d")
% identity % identity
% lib::idi::instanceTypeID(&newNode) % instanceTypeID(&newNode)
% BusHub::size()); % BusHub::size());
return *this; return *this;
} }
@ -176,11 +178,19 @@ namespace test{
log_.event("TestNexus", _Fmt("removed route to %s | table-size=%2d") % node % BusHub::size()); log_.event("TestNexus", _Fmt("removed route to %s | table-size=%2d") % node % BusHub::size());
} }
virtual operator string() const
{
return getID().getSym()+"."+instanceTypeID(this);
}
public: public:
TestNexus() TestNexus()
: BusHub(*this, lib::idi::EntryID<TestNexus>("mock-UI")) : BusHub(*this, lib::idi::EntryID<TestNexus>("mock-UI"))
{ } {
installCommandHandler();
installStateMarkHandler();
}
// standard copy operations // standard copy operations
@ -190,6 +200,33 @@ namespace test{
{ {
return log_; return log_;
} }
void
installCommandHandler (CommandHandler newHandler =CommandHandler())
{
if (newHandler)
commandHandler_ = newHandler;
else
commandHandler_ =
[=](GenNode const& cmd)
{
log_.warn(_Fmt("NOT handling command-message %s in test-mode") % cmd);
};
}
void
installStateMarkHandler (StateMarkHandler newHandler =StateMarkHandler())
{
if (newHandler)
stateMarkHandler_ = newHandler;
else
stateMarkHandler_ =
[=](ID subject, GenNode const& mark)
{
log_.warn(_Fmt("NOT handling state-mark %s passed from %s in test-mode")
% mark % subject);
};
}
}; };
/** singleton instance of the [TestNexus] /** singleton instance of the [TestNexus]
@ -198,6 +235,7 @@ namespace test{
/** /**
* @internal a defunct interface backbone. * @internal a defunct interface backbone.
* All UI-Bus operations are implemented NOP, but warning on STDRR * All UI-Bus operations are implemented NOP, but warning on STDRR
@ -244,11 +282,6 @@ namespace test{
cerr << "mark message -> ZombieNexus" <<endl; cerr << "mark message -> ZombieNexus" <<endl;
} }
virtual operator string() const
{
return getID().getSym()+"."+lib::idi::instanceTypeID(this);
}
virtual BusTerm& virtual BusTerm&
routeAdd (ID identity, Tangible& newNode) override routeAdd (ID identity, Tangible& newNode) override
{ {
@ -266,6 +299,11 @@ namespace test{
cerr << "disconnect("<<string(node)<<" -> ZombieNexus" <<endl; cerr << "disconnect("<<string(node)<<" -> ZombieNexus" <<endl;
} }
virtual operator string() const
{
return getID().getSym()+"."+instanceTypeID(this);
}
public: public:
/** fabricate a "dead terminal", marked as deceased, viciously connected to itself. /** fabricate a "dead terminal", marked as deceased, viciously connected to itself.
@ -319,6 +357,42 @@ namespace test{
} }
/**
* install a closure (custom handler function)
* to deal with any command invocations encountered
* in the test-UI-Bus. In the real Lumiera-UI, the UI-Bus
* is wired with a [core service handler](\ref core-service.hpp),
* which processes command messages by actually triggering
* command invocation on the Session within Proc-Layer
* @note when called without arguments, a default handler
* will be installed, which just logs and discards
* any command invocation message.
* @warning when you install a closure from within unit test code,
* be sure to re-install the default handler prior to leaving
* the definition scope; since the "test nexus" is actually
* implemented as singleton, an installed custom handler
* will outlive the immediate usage scope, possibly
* leading to segfault
*/
void
Nexus::setCommandHandler (CommandHandler newHandler)
{
testNexus().installCommandHandler (newHandler);
}
/**
* similar to the [custom command handler](::setCommandHandler)
* this hook allows to install a closure to intercept any
* "state mark" messages passed over the test-UI-Bus
*/
void
Nexus::setStateMarkHandler(StateMarkHandler newHandler)
{
testNexus().installStateMarkHandler (newHandler);
}
/** /**
* @return a defunct BusTerm with up-link to [ZombieNexus] * @return a defunct BusTerm with up-link to [ZombieNexus]

View file

@ -47,9 +47,12 @@
#include "lib/error.hpp" #include "lib/error.hpp"
#include "gui/ctrl/bus-term.hpp" #include "gui/ctrl/bus-term.hpp"
#include "gui/model/tangible.hpp"
#include "lib/test/event-log.hpp" #include "lib/test/event-log.hpp"
#include "lib/diff/gen-node.hpp"
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <functional>
#include <string> #include <string>
@ -81,6 +84,15 @@ namespace test{
static lib::test::EventLog const& getLog(); static lib::test::EventLog const& getLog();
static lib::test::EventLog const& startNewLog(); static lib::test::EventLog const& startNewLog();
/* == allow to set custom handlers for commands and state changes == */
using CommandHandler = std::function<void(lib::diff::GenNode const&)>;
using StateMarkHandler = std::function<void(lib::idi::BareEntryID, lib::diff::GenNode const&)>;
static void setCommandHandler (CommandHandler =CommandHandler());
static void setStateMarkHandler (StateMarkHandler =StateMarkHandler());
}; };