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
* @param identity the [endpoint-ID](\ref BusTerm::endpointID_) used
* 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
* 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&
routeAdd (ID identity, Tangible& newNode) override

View file

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

View file

@ -36,6 +36,7 @@
//#include <map>
using lib::idi::EntryID;
using lib::idi::BareEntryID;
using gui::test::MockElm;
using lib::diff::GenNode;
//using boost::lexical_cast;
@ -111,14 +112,14 @@ namespace test {
attachNewBusTerm ()
{
// 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
EventLog nexusLog = gui::test::Nexus::startNewLog();
CHECK (nexusLog.ensureNot("zeitgeist"));
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"));
EventLog elmLog = mock.getLog();
@ -135,7 +136,7 @@ namespace test {
// invoke action on element to cause upstream message (with a "state mark")
mock.slotCollapse();
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
gui::test::Nexus::testUI().mark (elmID, GenNode("Flash", 23));
@ -159,7 +160,7 @@ namespace test {
cout << "____Probe-Log_________________\n"
<< util::join(mock.getLog(), "\n")
<< util::join(elmLog, "\n")
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
cout << "____Nexus-Log_________________\n"

View file

@ -55,8 +55,12 @@ using std::cerr;
using std::endl;
using std::string;
using lib::test::EventLog;
using lib::Variant;
using lib::diff::Rec;
using lib::diff::GenNode;
using lib::diff::DataValues;
using lib::idi::instanceTypeID;
using lib::test::EventLog;
using gui::ctrl::BusTerm;
using util::_Fmt;
//using util::contains;
@ -67,10 +71,6 @@ namespace test{
namespace { // internal details
using lib::Variant;
using lib::diff::Rec;
using lib::diff::DataValues;
using BusHub = gui::ctrl::Nexus;
@ -123,12 +123,19 @@ namespace test{
{
EventLog log_{this};
using CommandHandler = test::Nexus::CommandHandler;
using StateMarkHandler = test::Nexus::StateMarkHandler;
CommandHandler commandHandler_;
StateMarkHandler stateMarkHandler_;
virtual void
act (GenNode const& command)
{
log_.call(this, "act", command);
BusHub::act (command);
commandHandler_(command);
log_.event("TestNexus", _Fmt("%s command \"%s\"%s")
% invocationStage(command)
% command.idi.getSym()
@ -139,7 +146,7 @@ namespace test{
note (ID subject, GenNode const& mark) override
{
log_.call(this, "note", subject, mark);
BusHub::note (subject, mark);
stateMarkHandler_(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);
}
virtual operator string() const
{
return getID().getSym()+"."+lib::idi::instanceTypeID(this);
}
virtual BusTerm&
routeAdd (ID identity, Tangible& newNode) override
{
log_.call(this, "routeAdd", identity, newNode);
log_.call(this, "routeAdd", identity, instanceTypeID(&newNode));
BusHub::routeAdd (identity, newNode);
log_.event("TestNexus", _Fmt("added route to %s |%s| table-size=%2d")
% identity
% lib::idi::instanceTypeID(&newNode)
% instanceTypeID(&newNode)
% BusHub::size());
return *this;
}
@ -176,11 +178,19 @@ namespace test{
log_.event("TestNexus", _Fmt("removed route to %s | table-size=%2d") % node % BusHub::size());
}
virtual operator string() const
{
return getID().getSym()+"."+instanceTypeID(this);
}
public:
TestNexus()
: BusHub(*this, lib::idi::EntryID<TestNexus>("mock-UI"))
{ }
{
installCommandHandler();
installStateMarkHandler();
}
// standard copy operations
@ -190,6 +200,33 @@ namespace test{
{
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]
@ -198,6 +235,7 @@ namespace test{
/**
* @internal a defunct interface backbone.
* All UI-Bus operations are implemented NOP, but warning on STDRR
@ -244,11 +282,6 @@ namespace test{
cerr << "mark message -> ZombieNexus" <<endl;
}
virtual operator string() const
{
return getID().getSym()+"."+lib::idi::instanceTypeID(this);
}
virtual BusTerm&
routeAdd (ID identity, Tangible& newNode) override
{
@ -266,6 +299,11 @@ namespace test{
cerr << "disconnect("<<string(node)<<" -> ZombieNexus" <<endl;
}
virtual operator string() const
{
return getID().getSym()+"."+instanceTypeID(this);
}
public:
/** 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]

View file

@ -47,9 +47,12 @@
#include "lib/error.hpp"
#include "gui/ctrl/bus-term.hpp"
#include "gui/model/tangible.hpp"
#include "lib/test/event-log.hpp"
#include "lib/diff/gen-node.hpp"
#include <boost/noncopyable.hpp>
#include <functional>
#include <string>
@ -81,6 +84,15 @@ namespace test{
static lib::test::EventLog const& getLog();
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());
};