2015-11-26 22:23:43 +01:00
|
|
|
|
/*
|
|
|
|
|
|
BusTerm(Test) - cover the building block of the UI-Bus
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
|
2015, 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.
|
|
|
|
|
|
|
|
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
2017-02-22 01:54:20 +01:00
|
|
|
|
/** @file bus-term-test.cpp
|
2017-02-22 03:17:18 +01:00
|
|
|
|
** unit test \ref BusTerm_test
|
2016-11-03 18:20:10 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
2016-01-15 02:29:33 +01:00
|
|
|
|
#include "lib/test/test-helper.hpp"
|
2023-10-05 01:50:54 +02:00
|
|
|
|
#include "lib/thread.hpp"
|
2017-08-12 19:32:57 +02:00
|
|
|
|
#include "lib/sync.hpp"
|
2017-08-13 05:17:16 +02:00
|
|
|
|
#include "lib/sync-classlock.hpp"
|
2018-10-08 05:00:06 +02:00
|
|
|
|
#include "include/ui-protocol.hpp"
|
2018-11-15 23:42:43 +01:00
|
|
|
|
#include "stage/ctrl/bus-term.hpp"
|
|
|
|
|
|
#include "stage/ctrl/state-manager.hpp"
|
|
|
|
|
|
#include "steam/control/command.hpp"
|
2016-01-02 01:23:09 +01:00
|
|
|
|
#include "test/test-nexus.hpp"
|
|
|
|
|
|
#include "test/mock-elm.hpp"
|
|
|
|
|
|
#include "lib/diff/gen-node.hpp"
|
2017-08-13 07:09:06 +02:00
|
|
|
|
#include "lib/diff/mutation-message.hpp"
|
2017-08-12 23:02:00 +02:00
|
|
|
|
#include "lib/idi/entry-id.hpp"
|
2017-08-12 19:32:57 +02:00
|
|
|
|
#include "lib/iter-adapter-stl.hpp"
|
|
|
|
|
|
#include "lib/iter-stack.hpp"
|
2017-08-12 23:02:00 +02:00
|
|
|
|
#include "lib/call-queue.hpp"
|
|
|
|
|
|
#include "lib/format-string.hpp"
|
2016-01-07 03:58:29 +01:00
|
|
|
|
#include "lib/format-cout.hpp"
|
2016-01-15 04:30:43 +01:00
|
|
|
|
#include "lib/time/timevalue.hpp"
|
|
|
|
|
|
#include "lib/luid.h"
|
2016-02-14 02:42:14 +01:00
|
|
|
|
#include "lib/util.hpp"
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
using boost::lexical_cast;
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
2017-08-12 19:32:57 +02:00
|
|
|
|
using lib::Sync;
|
2017-08-13 05:17:16 +02:00
|
|
|
|
using lib::ClassLock;
|
2023-10-05 01:50:54 +02:00
|
|
|
|
using lib::ThreadJoinable;
|
2017-08-13 02:26:04 +02:00
|
|
|
|
using lib::iter_stl::dischargeToSnapshot;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
using lib::IterQueue;
|
2017-08-12 19:32:57 +02:00
|
|
|
|
using lib::IterStack;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
using std::function;
|
2017-08-13 05:17:16 +02:00
|
|
|
|
using util::contains;
|
2016-02-14 02:42:14 +01:00
|
|
|
|
using util::isnil;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
using util::_Fmt;
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
2018-11-15 23:55:13 +01:00
|
|
|
|
namespace stage {
|
2015-11-26 22:23:43 +01:00
|
|
|
|
namespace model{
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
using lib::idi::EntryID;
|
|
|
|
|
|
using lib::idi::BareEntryID;
|
2018-11-15 23:59:23 +01:00
|
|
|
|
using steam::control::Command;
|
|
|
|
|
|
using stage::ctrl::StateManager;
|
|
|
|
|
|
using stage::ctrl::BusTerm;
|
|
|
|
|
|
using stage::test::MockElm;
|
2017-08-13 07:09:06 +02:00
|
|
|
|
using lib::diff::MutationMessage;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
using lib::diff::TreeDiffLanguage;
|
|
|
|
|
|
using lib::diff::DiffSource;
|
|
|
|
|
|
using lib::diff::DiffStep;
|
|
|
|
|
|
using lib::diff::GenNode;
|
|
|
|
|
|
using lib::diff::MakeRec;
|
|
|
|
|
|
using lib::diff::Rec;
|
|
|
|
|
|
using lib::diff::Ref;
|
|
|
|
|
|
using lib::time::Time;
|
|
|
|
|
|
using lib::time::TimeSpan;
|
|
|
|
|
|
using lib::hash::LuidH;
|
|
|
|
|
|
using lib::test::EventLog;
|
|
|
|
|
|
using lib::CallQueue;
|
|
|
|
|
|
|
2024-03-16 02:04:47 +01:00
|
|
|
|
using LERR_(UNBOUND_ARGUMENTS);
|
|
|
|
|
|
using LERR_(WRONG_TYPE);
|
2016-01-29 00:59:34 +01:00
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
using ID = lib::idi::BareEntryID const&;
|
|
|
|
|
|
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
2017-08-12 19:32:57 +02:00
|
|
|
|
namespace {// test data...
|
|
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
// --------random-diff-test------
|
|
|
|
|
|
uint const MAX_RAND_BORGS = 100; // stay below 400, verification export grows quadratic
|
|
|
|
|
|
uint const MAX_RAND_NUMBS = 500;
|
|
|
|
|
|
uint const MAX_RAND_DELAY = 5000; // throttle generation, since diff application is slower
|
|
|
|
|
|
// --------random-diff-test------
|
|
|
|
|
|
|
|
|
|
|
|
int generator_instances = 0;
|
2017-08-12 19:32:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************************//**
|
|
|
|
|
|
* @test cover the standard node element (terminal element) within the UI-Bus,
|
2016-01-02 01:23:09 +01:00
|
|
|
|
* with the help of an attached mock UI element. Contrary to the related
|
2018-09-21 13:46:42 +02:00
|
|
|
|
* [ui-element test](\ref AbstractTangible_test), here we focus on the bus side
|
2016-01-02 01:23:09 +01:00
|
|
|
|
* of the standard interactions.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This test enacts the fundamental generic communication patterns
|
|
|
|
|
|
* to verify the messaging behaviour
|
|
|
|
|
|
* - attaching a \ref BusTerm
|
2017-08-13 05:17:16 +02:00
|
|
|
|
* - detaching on element destruction
|
|
|
|
|
|
* - generate a command invocation
|
2016-01-02 01:23:09 +01:00
|
|
|
|
* - argument passing
|
|
|
|
|
|
* - capture a _state mark_
|
|
|
|
|
|
* - replay a _state mark_
|
|
|
|
|
|
* - cast messages and error states downstream
|
|
|
|
|
|
* - generic operating of interface states
|
2017-08-13 05:17:16 +02:00
|
|
|
|
* - multithreaded integration test of diff mutation
|
2016-01-03 05:32:05 +01:00
|
|
|
|
*
|
2015-11-26 22:23:43 +01:00
|
|
|
|
* @see AbstractTangible_test
|
2018-11-15 23:59:23 +01:00
|
|
|
|
* @see stage::model::Tangible
|
|
|
|
|
|
* @see stage::ctrl::BusTerm
|
2015-11-26 22:23:43 +01:00
|
|
|
|
*/
|
|
|
|
|
|
class BusTerm_test : public Test
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
|
run (Arg)
|
|
|
|
|
|
{
|
2024-11-17 19:45:41 +01:00
|
|
|
|
seedRand();
|
|
|
|
|
|
|
2015-11-26 22:23:43 +01:00
|
|
|
|
attachNewBusTerm();
|
|
|
|
|
|
commandInvocation();
|
|
|
|
|
|
captureStateMark();
|
|
|
|
|
|
replayStateMark();
|
|
|
|
|
|
verifyNotifications();
|
|
|
|
|
|
clearStates();
|
2017-08-13 05:17:16 +02:00
|
|
|
|
pushDiff();
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-01-02 01:23:09 +01:00
|
|
|
|
/** @test build a new BusTerm and verify connectivity.
|
|
|
|
|
|
* Every [tangible UI-element](\ref Tangible) bears an embedded BusTerm
|
|
|
|
|
|
* member. Since the latter _requires another, up-link BusTerm_ on construction,
|
2018-09-21 13:46:42 +02:00
|
|
|
|
* connection to the [UI-Bus](\ref ui-bus.hpp) is structurally ensured. Moreover,
|
2016-01-02 01:23:09 +01:00
|
|
|
|
* when hooking up a new UI-element, the initialisation of the embedded BusTerm
|
|
|
|
|
|
* will cause a down-link connection to be installed into the central routing
|
|
|
|
|
|
* table within the \ref Nexus, the hub of the UI-Bus. Routing and addressing
|
|
|
|
|
|
* is based on the UI-element's unique EntryID, destruction of the element,
|
|
|
|
|
|
* through invocation of BusTerm's dtor, will ensure deregistration
|
|
|
|
|
|
* from the Hub.
|
|
|
|
|
|
*/
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
attachNewBusTerm()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2016-01-15 02:29:33 +01:00
|
|
|
|
MARK_TEST_FUN;
|
2016-01-02 01:23:09 +01:00
|
|
|
|
// our dummy will be linked with this identity
|
2016-01-03 03:23:39 +01:00
|
|
|
|
BareEntryID elmID = EntryID<MockElm>{"zeitgeist"};
|
2016-01-02 01:23:09 +01:00
|
|
|
|
|
|
|
|
|
|
// Access the log on the Test-Nexus hub
|
2018-11-15 23:59:23 +01:00
|
|
|
|
EventLog nexusLog = stage::test::Nexus::startNewLog();
|
2016-01-02 01:23:09 +01:00
|
|
|
|
CHECK (nexusLog.ensureNot("zeitgeist"));
|
|
|
|
|
|
|
|
|
|
|
|
MockElm mock(elmID);
|
2016-01-03 03:23:39 +01:00
|
|
|
|
CHECK (nexusLog.verifyCall("routeAdd").on("TestNexus").arg(elmID,"Tangible") // Note: invoked from ctor, so it is just a tangible at the moment
|
2016-01-02 19:16:37 +01:00
|
|
|
|
.beforeEvent("TestNexus", "added route to bID-zeitgeist"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
|
|
|
|
|
|
EventLog elmLog = mock.getLog();
|
|
|
|
|
|
CHECK (elmLog.verifyCall("ctor").on(&mock)
|
|
|
|
|
|
.beforeEvent("create", "zeitgeist"));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// now verify there is indeed bidirectional connectivity...
|
2016-02-13 22:36:42 +01:00
|
|
|
|
CHECK (elmLog.ensureNot("expanded"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
CHECK (elmLog.ensureNot("doFlash"));
|
2016-02-13 22:36:42 +01:00
|
|
|
|
CHECK (nexusLog.ensureNot("zeitgeist").arg("expand"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
CHECK (nexusLog.ensureNot("zeitgeist").arg("Flash"));
|
|
|
|
|
|
|
|
|
|
|
|
// invoke action on element to cause upstream message (with a "state mark")
|
2016-02-13 22:36:42 +01:00
|
|
|
|
mock.slotExpand();
|
|
|
|
|
|
CHECK (elmLog.verifyEvent("expanded"));
|
|
|
|
|
|
CHECK (nexusLog.verifyCall("note").on("TestNexus").arg(elmID, "GenNode-ID(\"expand\")-DataCap|«bool»|true"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
|
2016-01-02 19:16:37 +01:00
|
|
|
|
// send a state mark down to the mock element
|
2018-11-15 23:59:23 +01:00
|
|
|
|
stage::test::Nexus::testUI().mark (elmID, GenNode(string{MARK_Flash}, 23));
|
2018-10-08 05:00:06 +02:00
|
|
|
|
CHECK (nexusLog.verifyCall("mark").on("TestNexus").arg(elmID, MARK_Flash)
|
2016-01-02 19:16:37 +01:00
|
|
|
|
.beforeEvent("TestNexus", "mark to bID-zeitgeist"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
CHECK (elmLog.verifyCall("doFlash").on("zeitgeist"));
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-05 15:55:22 +01:00
|
|
|
|
// kill the zeitgeist and verify disconnection
|
2016-01-02 01:23:09 +01:00
|
|
|
|
mock.kill();
|
|
|
|
|
|
CHECK (elmLog.verifyEvent("destroy","zeitgeist"));
|
|
|
|
|
|
CHECK (nexusLog.verifyCall("routeDetach").on("TestNexus").arg(elmID)
|
2016-01-02 19:16:37 +01:00
|
|
|
|
.beforeEvent("TestNexus", "removed route to bID-zeitgeist"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
stage::test::Nexus::testUI().mark (elmID, GenNode({MARK_Flash}, 88));
|
2016-01-03 05:32:05 +01:00
|
|
|
|
CHECK (nexusLog.verify("removed route to bID-zeitgeist")
|
2018-10-08 05:00:06 +02:00
|
|
|
|
.beforeCall("mark").on("TestNexus").arg(elmID, MARK_Flash)
|
2016-01-03 03:37:52 +01:00
|
|
|
|
.beforeEvent("warn","discarding mark to unknown bID-zeitgeist"));
|
2016-01-02 01:23:09 +01:00
|
|
|
|
CHECK (elmLog.ensureNot("Flash")
|
|
|
|
|
|
.afterEvent("destroy","zeitgeist"));
|
2016-01-02 02:00:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cout << "____Probe-Log_________________\n"
|
2016-01-03 03:23:39 +01:00
|
|
|
|
<< util::join(elmLog, "\n")
|
2016-01-02 02:00:07 +01:00
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
|
|
|
|
|
|
|
|
|
|
|
cout << "____Nexus-Log_________________\n"
|
2018-11-15 23:59:23 +01:00
|
|
|
|
<< util::join(stage::test::Nexus::getLog(), "\n")
|
2016-01-02 02:00:07 +01:00
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-06 01:28:39 +01:00
|
|
|
|
/** @test perform the full command binding and invocation protocol */
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
commandInvocation()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2016-01-15 04:30:43 +01:00
|
|
|
|
MARK_TEST_FUN
|
2018-11-15 23:59:23 +01:00
|
|
|
|
stage::test::Nexus::startNewLog();
|
|
|
|
|
|
Symbol cmd = stage::test::Nexus::prepareMockCmd<string, TimeSpan, LuidH>();
|
2016-01-15 04:30:43 +01:00
|
|
|
|
|
|
|
|
|
|
MockElm mock("uiElm");
|
|
|
|
|
|
|
|
|
|
|
|
// random command arguments...
|
|
|
|
|
|
string text {lib::test::randStr(12)};
|
|
|
|
|
|
TimeSpan clip (Time(1,2,3), lib::test::randTime());
|
|
|
|
|
|
LuidH luid;
|
|
|
|
|
|
|
2017-04-17 16:57:09 +02:00
|
|
|
|
// we cannot invoke commands without binding required arguments
|
|
|
|
|
|
VERIFY_ERROR (WRONG_TYPE, mock.invoke(cmd) );
|
2016-01-29 00:59:34 +01:00
|
|
|
|
|
2024-03-16 02:04:47 +01:00
|
|
|
|
// proper argument typing is ensured while dispatching the bind message.
|
2017-04-14 23:45:35 +02:00
|
|
|
|
VERIFY_ERROR (WRONG_TYPE, mock.invoke(cmd, Rec({"lalala"})) );
|
2016-01-15 04:30:43 +01:00
|
|
|
|
|
|
|
|
|
|
// command can't be issued, since it's still unbound
|
2017-04-17 16:57:09 +02:00
|
|
|
|
CHECK (not Command::canExec(cmd));
|
2016-01-15 04:30:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
2017-04-14 23:45:35 +02:00
|
|
|
|
mock.invoke (cmd, text, clip, luid);
|
2016-01-15 04:30:43 +01:00
|
|
|
|
|
2017-04-17 16:57:09 +02:00
|
|
|
|
CHECK (Command::canExec(cmd));
|
2018-11-15 23:59:23 +01:00
|
|
|
|
CHECK (stage::test::Nexus::wasBound(cmd, text, clip, luid));
|
|
|
|
|
|
CHECK (not stage::test::Nexus::wasBound(cmd, "lololo"));
|
|
|
|
|
|
CHECK (stage::test::Nexus::wasInvoked(cmd));
|
|
|
|
|
|
CHECK (stage::test::Nexus::wasInvoked(cmd, text, clip, luid));
|
|
|
|
|
|
CHECK (not stage::test::Nexus::wasInvoked(cmd, " huh ", clip, luid));
|
|
|
|
|
|
CHECK (not stage::test::Nexus::wasInvoked(cmd, text, clip));
|
2016-01-15 04:30:43 +01:00
|
|
|
|
|
|
|
|
|
|
// Mock commands are automatically unique
|
2018-11-15 23:59:23 +01:00
|
|
|
|
auto cmdX = stage::test::Nexus::prepareMockCmd<>();
|
|
|
|
|
|
auto cmdY = stage::test::Nexus::prepareMockCmd<>();
|
2017-04-17 16:57:09 +02:00
|
|
|
|
CHECK (cmd != cmdX);
|
|
|
|
|
|
CHECK (cmd != cmdY);
|
2016-01-15 04:30:43 +01:00
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
CHECK (not stage::test::Nexus::wasInvoked(cmdX));
|
|
|
|
|
|
CHECK (not stage::test::Nexus::wasInvoked(cmdY));
|
2016-02-06 01:28:39 +01:00
|
|
|
|
|
|
|
|
|
|
cout << "____Nexus-Log_________________\n"
|
2018-11-15 23:59:23 +01:00
|
|
|
|
<< util::join(stage::test::Nexus::getLog(), "\n")
|
2016-02-06 01:28:39 +01:00
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
2016-02-13 23:53:09 +01:00
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
stage::test::Nexus::setCommandHandler(); // deinstall custom command handler
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-12 23:46:10 +01:00
|
|
|
|
/** @test collect state mark notifications from bus
|
|
|
|
|
|
* We use a test version of the PresentationStateManager,
|
|
|
|
|
|
* based on the same building blocks as _the real thing_ */
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
captureStateMark()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2016-02-12 23:46:10 +01:00
|
|
|
|
MARK_TEST_FUN
|
2018-11-15 23:59:23 +01:00
|
|
|
|
stage::test::Nexus::startNewLog();
|
|
|
|
|
|
StateManager& stateManager = stage::test::Nexus::useMockStateManager();
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
2016-02-13 14:46:21 +01:00
|
|
|
|
MockElm mockA("alpha"); BareEntryID alpha = mockA.getID();
|
|
|
|
|
|
MockElm mockB("bravo"); BareEntryID bravo = mockB.getID();
|
|
|
|
|
|
MockElm mockC("charly"); BareEntryID charly = mockC.getID();
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
|
|
|
|
|
mockA.slotExpand();
|
|
|
|
|
|
|
|
|
|
|
|
mockB.slotExpand();
|
|
|
|
|
|
mockB.slotCollapse();
|
|
|
|
|
|
|
2016-02-13 14:46:21 +01:00
|
|
|
|
CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", true ));
|
|
|
|
|
|
CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", false ));
|
|
|
|
|
|
|
|
|
|
|
|
// handling of missing information
|
|
|
|
|
|
CHECK (stateManager.currentState(charly, "expand") == Ref::NO); // no data recorded yet
|
|
|
|
|
|
CHECK (stateManager.currentState(bravo, "extinct") == Ref::NO); // unknown property
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
2016-02-13 14:46:21 +01:00
|
|
|
|
EntryID<MockElm> bruno("bruno");
|
|
|
|
|
|
CHECK (stateManager.currentState(bruno, "expand") == Ref::NO); // who knows bruno?
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
|
|
|
|
|
mockC.slotExpand();
|
2016-02-13 14:46:21 +01:00
|
|
|
|
CHECK (stateManager.currentState(charly, "expand") == GenNode("expand", true ));
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
2016-02-14 05:03:08 +01:00
|
|
|
|
// error states can be sticky
|
|
|
|
|
|
mockC.markErr("overinflated");
|
|
|
|
|
|
CHECK (stateManager.currentState(charly, "Error") == GenNode("Error", "overinflated"));
|
|
|
|
|
|
|
2016-02-12 23:46:10 +01:00
|
|
|
|
mockC.reset();
|
2016-02-13 14:46:21 +01:00
|
|
|
|
CHECK (stateManager.currentState(charly, "expand") == Ref::NO); // back to void
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
2016-02-14 05:03:08 +01:00
|
|
|
|
|
2016-02-06 01:28:39 +01:00
|
|
|
|
cout << "____Nexus-Log_________________\n"
|
2018-11-15 23:59:23 +01:00
|
|
|
|
<< util::join(stage::test::Nexus::getLog(), "\n")
|
2016-02-06 01:28:39 +01:00
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-12 23:46:10 +01:00
|
|
|
|
/** @test replay previously captured state information" */
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
replayStateMark()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2016-02-12 23:46:10 +01:00
|
|
|
|
MARK_TEST_FUN
|
2018-11-15 23:59:23 +01:00
|
|
|
|
StateManager& stateManager = stage::test::Nexus::getMockStateManager();
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
|
|
|
|
|
MockElm mockA("alpha");
|
|
|
|
|
|
// no "bravo" this time
|
|
|
|
|
|
MockElm mockC("charly");
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isExpanded());
|
|
|
|
|
|
CHECK (not mockC.isTouched());
|
|
|
|
|
|
|
2016-02-13 14:46:21 +01:00
|
|
|
|
BareEntryID alpha = mockA.getID();
|
|
|
|
|
|
stateManager.replayState (alpha, "expand");
|
2016-02-13 01:01:45 +01:00
|
|
|
|
CHECK (mockA.isExpanded());
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
auto& uiBus = stage::test::Nexus::testUI();
|
2016-02-12 23:46:10 +01:00
|
|
|
|
uiBus.mark (mockA.getID(), GenNode{"expand", false});
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isExpanded());
|
|
|
|
|
|
CHECK (mockA.isTouched());
|
|
|
|
|
|
|
2016-02-13 01:01:45 +01:00
|
|
|
|
stateManager.replayAllState ("expand");
|
2016-02-12 23:46:10 +01:00
|
|
|
|
|
2016-02-13 01:01:45 +01:00
|
|
|
|
CHECK (mockA.isExpanded());
|
|
|
|
|
|
CHECK (not mockC.isExpanded());
|
2016-02-12 23:46:10 +01:00
|
|
|
|
CHECK (not mockC.isTouched());
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-14 01:34:58 +01:00
|
|
|
|
/** @test send notifications to a distinct element, or as broadcast. */
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
verifyNotifications()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2016-02-14 01:34:58 +01:00
|
|
|
|
MARK_TEST_FUN
|
2018-11-15 23:59:23 +01:00
|
|
|
|
EventLog nexusLog = stage::test::Nexus::startNewLog();
|
2016-02-14 01:34:58 +01:00
|
|
|
|
|
|
|
|
|
|
MockElm mockA("alpha"); BareEntryID alpha = mockA.getID(); mockA.joinLog (nexusLog);
|
|
|
|
|
|
MockElm mockB("bravo"); BareEntryID bravo = mockB.getID(); mockB.joinLog (nexusLog);
|
|
|
|
|
|
MockElm mockC("charly"); BareEntryID charly = mockC.getID(); mockC.joinLog (nexusLog);
|
|
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
auto& uiBus = stage::test::Nexus::testUI();
|
2016-02-14 01:34:58 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isTouched());
|
|
|
|
|
|
CHECK (not mockB.isTouched());
|
|
|
|
|
|
CHECK (not mockC.isTouched());
|
|
|
|
|
|
|
2018-10-08 05:00:06 +02:00
|
|
|
|
uiBus.mark (alpha, GenNode{"Message", "Centauri"});
|
|
|
|
|
|
uiBus.mark (bravo, GenNode{"Flash", true});
|
2016-02-14 01:34:58 +01:00
|
|
|
|
uiBus.mark (charly, GenNode{"Message", "Delta"});
|
|
|
|
|
|
uiBus.mark (charly, GenNode{"Error", "Echo"});
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ( mockA.isTouched());
|
|
|
|
|
|
CHECK (not mockB.isTouched());
|
|
|
|
|
|
CHECK ( mockC.isTouched());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isError());
|
|
|
|
|
|
CHECK (not mockB.isError());
|
|
|
|
|
|
CHECK ( mockC.isError());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ("Centauri" == mockA.getMessage());
|
|
|
|
|
|
CHECK ("Delta" == mockC.getMessage());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ("Echo" == mockC.getError());
|
|
|
|
|
|
|
|
|
|
|
|
// verify the message passing in the combined log...
|
|
|
|
|
|
CHECK (nexusLog.verifyEvent("create", "alpha")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("alpha", "Centauri") // bus API invoked
|
|
|
|
|
|
.beforeCall("doMsg").on("alpha").arg("Centauri") // handler on target invoked
|
2024-03-16 02:04:47 +01:00
|
|
|
|
.beforeEvent("mark", "Centauri") // target action activated
|
2016-02-14 01:34:58 +01:00
|
|
|
|
.beforeEvent("TestNexus","delivered mark to bID-alpha")); // dispatch done within UI-Bus
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (nexusLog.verifyEvent("TestNexus","delivered mark to bID-alpha")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("bravo", "GenNode-ID(\"Flash\")-DataCap|«bool»|true")
|
|
|
|
|
|
.beforeCall("doFlash").on("bravo")
|
|
|
|
|
|
.beforeEvent("TestNexus","delivered mark to bID-bravo"));
|
|
|
|
|
|
|
|
|
|
|
|
// NOTE: calls are passed down synchronously, in one hop, and in sequence
|
|
|
|
|
|
CHECK (nexusLog.verifyEvent("TestNexus","delivered mark to bID-bravo")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("charly", "GenNode-ID(\"Message\")-DataCap|«string»|Delta")
|
|
|
|
|
|
.beforeCall("doMsg").on("charly").arg("Delta")
|
|
|
|
|
|
.beforeEvent("mark", "Delta").id("Message")
|
|
|
|
|
|
.beforeEvent("TestNexus","delivered mark to bID-charly")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("charly", "GenNode-ID(\"Error\")-DataCap|«string»|Echo")
|
|
|
|
|
|
.beforeCall("doErr").on("charly").arg("Echo")
|
|
|
|
|
|
.beforeEvent("mark", "Echo").id("Error")
|
|
|
|
|
|
.beforeEvent("TestNexus","delivered mark to bID-charly"));
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-14 01:47:21 +01:00
|
|
|
|
// broadcast message
|
|
|
|
|
|
uiBus.markAll (GenNode{"Message", "Foxtrot"});
|
|
|
|
|
|
CHECK (not mockA.isError());
|
|
|
|
|
|
CHECK (not mockB.isError());
|
|
|
|
|
|
CHECK ( mockC.isError());
|
|
|
|
|
|
CHECK ( mockA.isTouched());
|
|
|
|
|
|
CHECK ( mockB.isTouched());
|
|
|
|
|
|
CHECK ( mockC.isTouched());
|
|
|
|
|
|
CHECK ("Foxtrot" == mockA.getMessage());
|
|
|
|
|
|
CHECK ("Foxtrot" == mockB.getMessage());
|
|
|
|
|
|
CHECK ("Foxtrot" == mockC.getMessage());
|
|
|
|
|
|
CHECK ( "" == mockA.getError());
|
|
|
|
|
|
CHECK ( "" == mockB.getError());
|
|
|
|
|
|
CHECK ( "Echo" == mockC.getError());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (nexusLog.verifyEvent("mark", "Echo").id("Error")
|
|
|
|
|
|
.beforeCall("markAll").on("TestNexus").arg("Foxtrot")
|
2016-02-14 02:20:51 +01:00
|
|
|
|
.beforeEvent("Broadcast", "Foxtrot")
|
2016-02-14 01:47:21 +01:00
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("bravo", "GenNode-ID(\"Message\")-DataCap|«string»|Foxtrot")
|
|
|
|
|
|
.beforeCall("doMsg").on("bravo").arg("Foxtrot")
|
|
|
|
|
|
.beforeEvent("TestNexus", "broadcasted mark to 3 terminals"));
|
|
|
|
|
|
|
2016-02-14 02:20:51 +01:00
|
|
|
|
// the order of dispatch is unspecified,
|
|
|
|
|
|
// but we know a regular mark call sequence happens for each connected terminal
|
|
|
|
|
|
CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("alpha", "Foxtrot")
|
|
|
|
|
|
.beforeCall("doMsg").on("alpha").arg("Foxtrot")
|
|
|
|
|
|
.beforeEvent("TestNexus", "successfully broadcasted"));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("bravo", "Foxtrot")
|
|
|
|
|
|
.beforeCall("doMsg").on("bravo").arg("Foxtrot")
|
|
|
|
|
|
.beforeEvent("TestNexus", "successfully broadcasted"));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
|
|
|
|
|
|
.beforeCall("mark").on("TestNexus").arg("charly", "Foxtrot")
|
|
|
|
|
|
.beforeCall("doMsg").on("charly").arg("Foxtrot")
|
|
|
|
|
|
.beforeEvent("TestNexus", "successfully broadcasted"));
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-13 23:53:09 +01:00
|
|
|
|
cout << "____Nexus-Log_________________\n"
|
2016-02-14 01:34:58 +01:00
|
|
|
|
<< util::join(nexusLog, "\n")
|
2016-02-13 23:53:09 +01:00
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-14 02:42:14 +01:00
|
|
|
|
/** @test broadcast various degrees of state reset */
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
clearStates()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2016-02-14 02:42:14 +01:00
|
|
|
|
MARK_TEST_FUN
|
2018-11-15 23:59:23 +01:00
|
|
|
|
EventLog nexusLog = stage::test::Nexus::startNewLog();
|
2016-02-14 02:42:14 +01:00
|
|
|
|
|
|
|
|
|
|
MockElm mockA("alpha"); BareEntryID alpha = mockA.getID(); mockA.joinLog (nexusLog);
|
|
|
|
|
|
MockElm mockB("bravo"); BareEntryID bravo = mockB.getID(); mockB.joinLog (nexusLog);
|
|
|
|
|
|
MockElm mockC("charly"); BareEntryID charly = mockC.getID(); mockC.joinLog (nexusLog);
|
|
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
auto& uiBus = stage::test::Nexus::testUI();
|
2016-02-14 02:42:14 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isTouched());
|
|
|
|
|
|
CHECK (not mockB.isTouched());
|
|
|
|
|
|
CHECK (not mockC.isTouched());
|
|
|
|
|
|
|
|
|
|
|
|
mockB.slotExpand();
|
|
|
|
|
|
uiBus.mark (alpha, GenNode{"Message", "Centauri"});
|
|
|
|
|
|
uiBus.mark (charly, GenNode{"Message", "Delta"});
|
|
|
|
|
|
uiBus.mark (charly, GenNode{"Error", "Echo"});
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (mockB.isExpanded());
|
|
|
|
|
|
CHECK (mockC.isError());
|
2016-02-14 05:03:08 +01:00
|
|
|
|
CHECK ("Delta" == mockC.getMessage());
|
2016-02-14 02:42:14 +01:00
|
|
|
|
CHECK ("Centauri" == mockA.getMessage());
|
|
|
|
|
|
|
|
|
|
|
|
// reset all notification messages
|
2016-02-14 03:42:10 +01:00
|
|
|
|
uiBus.markAll (GenNode{"clearMsg", true});
|
2016-02-14 02:42:14 +01:00
|
|
|
|
CHECK (mockB.isExpanded());
|
|
|
|
|
|
CHECK (mockC.isError());
|
|
|
|
|
|
CHECK (isnil (mockA.getMessage()));
|
|
|
|
|
|
CHECK (isnil (mockC.getMessage()));
|
2016-02-14 05:03:08 +01:00
|
|
|
|
CHECK ("Echo" == mockC.getError());
|
2016-02-14 02:42:14 +01:00
|
|
|
|
|
|
|
|
|
|
uiBus.mark (bravo, GenNode{"Message", "miss"});
|
|
|
|
|
|
mockA.slotExpand();
|
|
|
|
|
|
mockA.slotCollapse();
|
|
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
auto& stateManager = stage::test::Nexus::getMockStateManager();
|
2016-02-14 05:03:08 +01:00
|
|
|
|
CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", false ));
|
|
|
|
|
|
CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", true ));
|
2018-10-08 05:00:06 +02:00
|
|
|
|
CHECK (stateManager.currentState(charly,"expand") == Ref::NO);
|
|
|
|
|
|
CHECK (stateManager.currentState(charly, "Error") == GenNode("Error", "Echo")); // sticky error state was recorded
|
2016-02-14 05:03:08 +01:00
|
|
|
|
|
2016-02-14 02:42:14 +01:00
|
|
|
|
// reset error state(s)
|
2016-02-14 03:42:10 +01:00
|
|
|
|
uiBus.markAll (GenNode{"clearErr", true});
|
2016-02-14 05:03:08 +01:00
|
|
|
|
CHECK (not mockA.isExpanded());
|
2016-02-14 02:42:14 +01:00
|
|
|
|
CHECK (mockB.isExpanded());
|
|
|
|
|
|
CHECK ("miss" == mockB.getMessage());
|
|
|
|
|
|
CHECK (not mockC.isError());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", false ));
|
|
|
|
|
|
CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", true ));
|
2018-10-08 05:00:06 +02:00
|
|
|
|
CHECK (stateManager.currentState(charly,"expand") == Ref::NO);
|
|
|
|
|
|
CHECK (stateManager.currentState(charly, "Error") == Ref::NO); // sticky error state was cleared,
|
|
|
|
|
|
// because charly sent a clearErr state mark notification back
|
2016-02-14 02:42:14 +01:00
|
|
|
|
|
|
|
|
|
|
// send global sweeping reset
|
|
|
|
|
|
uiBus.markAll (GenNode{"reset", true});
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isTouched());
|
|
|
|
|
|
CHECK (not mockB.isTouched());
|
|
|
|
|
|
CHECK (not mockC.isTouched());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (not mockA.isExpanded());
|
|
|
|
|
|
CHECK (not mockB.isExpanded());
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (isnil (mockA.getMessage()));
|
|
|
|
|
|
CHECK (isnil (mockB.getMessage()));
|
|
|
|
|
|
CHECK (isnil (mockC.getMessage()));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (stateManager.currentState(alpha, "expand") == Ref::NO);
|
|
|
|
|
|
CHECK (stateManager.currentState(bravo, "expand") == Ref::NO);
|
|
|
|
|
|
CHECK (stateManager.currentState(charly, "expand") == Ref::NO);
|
2016-02-14 05:03:08 +01:00
|
|
|
|
CHECK (stateManager.currentState(charly, "Error" ) == Ref::NO);
|
2016-02-14 02:42:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
2016-02-14 02:20:51 +01:00
|
|
|
|
cout << "____Nexus-Log_________________\n"
|
|
|
|
|
|
<< util::join(nexusLog, "\n")
|
|
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
2016-02-14 05:03:08 +01:00
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
stage::test::Nexus::setStateMarkHandler(); // deinstall custom state mark handler
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-10-05 01:50:54 +02:00
|
|
|
|
* @test integration test of mutation by diff message.
|
2017-08-12 19:32:57 +02:00
|
|
|
|
* Since this test focuses on the bus side of standard interactions,
|
2023-10-05 01:50:54 +02:00
|
|
|
|
* it seems indicated to simulate the complete invocation situation,
|
|
|
|
|
|
* which involves passing thread boundaries. The main thread running
|
2017-08-12 19:32:57 +02:00
|
|
|
|
* this test shall enact the role of the UI event thread (since the
|
|
|
|
|
|
* UI-Bus in the real application is confined to this UI thread).
|
|
|
|
|
|
* Thus we'll start another thread to enact the role of the Session,
|
2017-08-13 05:17:16 +02:00
|
|
|
|
* to produce diff messages and "cast" them towards the UI.
|
2017-08-12 19:32:57 +02:00
|
|
|
|
* @note a defining property of this whole interaction is the fact that
|
2017-08-13 05:17:16 +02:00
|
|
|
|
* the diff is _pulled asynchronously,_ which means the actual diff
|
|
|
|
|
|
* generation happens on callback from the UI. Access to any "session"
|
|
|
|
|
|
* data needs to be protected by lock in such a situation.
|
2017-08-12 19:32:57 +02:00
|
|
|
|
*/
|
2015-11-26 22:23:43 +01:00
|
|
|
|
void
|
2016-02-13 17:09:57 +01:00
|
|
|
|
pushDiff()
|
2015-11-26 22:23:43 +01:00
|
|
|
|
{
|
2017-08-12 19:32:57 +02:00
|
|
|
|
MARK_TEST_FUN
|
|
|
|
|
|
|
|
|
|
|
|
struct SessionThread
|
|
|
|
|
|
: Sync<>
|
2023-10-05 01:50:54 +02:00
|
|
|
|
, ThreadJoinable<>
|
2017-08-12 19:32:57 +02:00
|
|
|
|
{
|
|
|
|
|
|
// shared data
|
2017-08-13 05:17:16 +02:00
|
|
|
|
uint64_t borgChecksum_ = 0;
|
2017-08-12 19:32:57 +02:00
|
|
|
|
IterStack<uint> sessionBorgs_;
|
|
|
|
|
|
|
|
|
|
|
|
// access to shared session data
|
|
|
|
|
|
void
|
|
|
|
|
|
scheduleBorg (uint id)
|
|
|
|
|
|
{
|
2023-10-15 20:42:55 +02:00
|
|
|
|
Lock sync{this};
|
2017-08-13 05:17:16 +02:00
|
|
|
|
borgChecksum_ += id;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
sessionBorgs_.push(id);
|
2017-08-12 19:32:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto
|
|
|
|
|
|
dispatchBorgs()
|
|
|
|
|
|
{
|
2023-10-15 20:42:55 +02:00
|
|
|
|
Lock sync{this};
|
2017-08-13 02:26:04 +02:00
|
|
|
|
return dischargeToSnapshot (sessionBorgs_);
|
2017-08-12 19:32:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
2017-08-12 19:32:57 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Independent heap allocated diff generator.
|
|
|
|
|
|
* Implements the IterSource<DiffStep> interface
|
|
|
|
|
|
* and will be pulled from the GUI-Thread for actually
|
|
|
|
|
|
* generating the diff. At this point, it needs to access
|
|
|
|
|
|
* the shared session data with proper locking, and derive
|
|
|
|
|
|
* a representation of the "changes" in diff format
|
|
|
|
|
|
*/
|
2017-08-12 23:02:00 +02:00
|
|
|
|
struct BorgGenerator
|
2018-03-24 05:35:13 +01:00
|
|
|
|
: util::NonCopyable
|
2017-08-12 23:02:00 +02:00
|
|
|
|
, TreeDiffLanguage
|
2017-08-12 19:32:57 +02:00
|
|
|
|
, DiffSource
|
|
|
|
|
|
{
|
2017-08-13 05:17:16 +02:00
|
|
|
|
uint generatorID_;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
SessionThread& theCube_;
|
|
|
|
|
|
IterQueue<DiffStep> steps_;
|
2017-08-12 19:32:57 +02:00
|
|
|
|
|
2017-08-13 02:26:04 +02:00
|
|
|
|
BorgGenerator (SessionThread& motherShip, uint id)
|
2017-08-13 05:17:16 +02:00
|
|
|
|
: generatorID_{id}
|
2017-08-13 02:26:04 +02:00
|
|
|
|
, theCube_{motherShip}
|
2017-08-13 05:17:16 +02:00
|
|
|
|
{
|
|
|
|
|
|
ClassLock<BorgGenerator> sync;
|
|
|
|
|
|
++generator_instances;
|
|
|
|
|
|
}
|
|
|
|
|
|
~BorgGenerator()
|
|
|
|
|
|
{
|
|
|
|
|
|
ClassLock<BorgGenerator> sync;
|
|
|
|
|
|
--generator_instances;
|
|
|
|
|
|
}
|
2024-03-16 02:04:47 +01:00
|
|
|
|
|
2017-08-12 19:32:57 +02:00
|
|
|
|
|
|
|
|
|
|
/* == Interface IterSource<DiffStep> == */
|
2017-08-12 23:02:00 +02:00
|
|
|
|
|
|
|
|
|
|
virtual DiffStep*
|
|
|
|
|
|
firstResult () override
|
2017-08-12 19:32:57 +02:00
|
|
|
|
{
|
2017-08-12 23:02:00 +02:00
|
|
|
|
REQUIRE (not steps_);
|
|
|
|
|
|
auto plannedBorgs = theCube_.dispatchBorgs();
|
|
|
|
|
|
uint max = plannedBorgs.size();
|
|
|
|
|
|
uint cur = 0;
|
|
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
_Fmt borgName{"%d of %d ≺%03d.gen%03d≻"};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
steps_.feed (after(Ref::ATTRIBS)); // important: retain all existing attributes
|
|
|
|
|
|
for (uint id : plannedBorgs) // Generate diff to inject a flock of Borg
|
2017-08-12 23:02:00 +02:00
|
|
|
|
{
|
2017-08-13 05:17:16 +02:00
|
|
|
|
GenNode borg = MakeRec().genNode(borgName % ++cur % max % id % generatorID_);
|
|
|
|
|
|
steps_.feed (ins(borg));
|
|
|
|
|
|
steps_.feed (mut(borg)); // open nested scope for this Borg
|
|
|
|
|
|
steps_.feed ( ins(GenNode{"borgID", int(id)}));
|
|
|
|
|
|
steps_.feed (emu(borg)); // close nested scope
|
2017-08-12 23:02:00 +02:00
|
|
|
|
}
|
2017-08-13 05:17:16 +02:00
|
|
|
|
steps_.feed (after(Ref::END)); // important: fast-forward and accept already existing Borgs
|
2017-08-12 23:02:00 +02:00
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
return & *steps_; // the IterSource protocol requires us to return a ptr to current element
|
2017-08-12 19:32:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
virtual void
|
|
|
|
|
|
nextResult (DiffStep*& pos) override
|
2017-08-12 19:32:57 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (!pos) return;
|
2017-08-12 23:02:00 +02:00
|
|
|
|
if (steps_) ++steps_;
|
|
|
|
|
|
if (steps_)
|
2017-08-13 05:17:16 +02:00
|
|
|
|
pos = & *steps_; // pointer to current element
|
2017-08-12 19:32:57 +02:00
|
|
|
|
else
|
2017-08-13 05:17:16 +02:00
|
|
|
|
pos = NULL; // signal iteration end
|
2017-08-12 19:32:57 +02:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2024-03-16 02:04:47 +01:00
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
/**
|
2023-10-05 01:50:54 +02:00
|
|
|
|
* launch the Session Thread and start injecting Borgs
|
2017-08-13 05:17:16 +02:00
|
|
|
|
*/
|
|
|
|
|
|
SessionThread(function<void(DiffSource*)> notifyGUI)
|
2017-08-12 19:32:57 +02:00
|
|
|
|
: ThreadJoinable{"BusTerm_test: asynchronous diff mutation"
|
2023-10-05 01:50:54 +02:00
|
|
|
|
, [=]
|
|
|
|
|
|
{
|
2024-11-17 19:45:41 +01:00
|
|
|
|
uint cnt = randGen_.i(MAX_RAND_BORGS);
|
2023-10-05 01:50:54 +02:00
|
|
|
|
for (uint i=0; i<cnt; ++i)
|
|
|
|
|
|
{
|
2024-11-17 19:45:41 +01:00
|
|
|
|
uint delay = randGen_.i(MAX_RAND_DELAY);
|
|
|
|
|
|
uint id = randGen_.i(MAX_RAND_NUMBS);
|
2023-10-05 01:50:54 +02:00
|
|
|
|
usleep (delay);
|
|
|
|
|
|
scheduleBorg (id);
|
|
|
|
|
|
notifyGUI (new BorgGenerator{*this, i});
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
2017-08-12 19:32:57 +02:00
|
|
|
|
{ }
|
2024-11-17 19:45:41 +01:00
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
lib::Random randGen_{lib::seedFromDefaultGen()};
|
2017-08-12 19:32:57 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
|
2018-11-15 23:59:23 +01:00
|
|
|
|
EventLog nexusLog = stage::test::Nexus::startNewLog();
|
2017-08-12 19:32:57 +02:00
|
|
|
|
|
2023-10-05 01:50:54 +02:00
|
|
|
|
// the simulated »GUI model«
|
|
|
|
|
|
// — to be infested by hosts of Borg sent via Diff-Message...
|
2017-08-12 19:32:57 +02:00
|
|
|
|
MockElm rootMock("alpha zero");
|
|
|
|
|
|
ID rootID = rootMock.getID();
|
|
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
rootMock.attrib["α"] = "Quadrant";
|
2017-08-13 02:26:04 +02:00
|
|
|
|
CHECK ("Quadrant" == rootMock.attrib["α"]);
|
2017-08-12 19:32:57 +02:00
|
|
|
|
CHECK (isnil (rootMock.scope));
|
2017-08-12 23:02:00 +02:00
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
CHECK (0 == generator_instances);
|
|
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
|
|
|
|
|
|
// The final part in the puzzle is to dispatch the diff messages into the UI
|
|
|
|
|
|
// In the real application, this operation is provided by the NotificationService.
|
|
|
|
|
|
// It has access to the UI-Bus, but has to ensure all bus operations are actually
|
|
|
|
|
|
// performed on the UI event thread.
|
2018-11-15 23:59:23 +01:00
|
|
|
|
auto& uiBus = stage::test::Nexus::testUI();
|
2017-08-12 23:02:00 +02:00
|
|
|
|
CallQueue uiDispatcher;
|
2017-08-13 05:17:16 +02:00
|
|
|
|
auto notifyGUI = [&](DiffSource* diffGenerator)
|
2017-08-12 23:02:00 +02:00
|
|
|
|
{
|
2017-08-13 05:17:16 +02:00
|
|
|
|
uiDispatcher.feed ([&, diffGenerator]()
|
2017-08-12 23:02:00 +02:00
|
|
|
|
{
|
|
|
|
|
|
// apply and consume diff message stored within closure
|
2017-08-13 07:09:06 +02:00
|
|
|
|
uiBus.change (rootID, MutationMessage{diffGenerator});
|
2017-08-12 23:02:00 +02:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//----start-multithreaded-mutation---
|
|
|
|
|
|
SessionThread session{notifyGUI};
|
2017-08-13 02:26:04 +02:00
|
|
|
|
usleep (2 * MAX_RAND_DELAY);
|
|
|
|
|
|
while (not isnil(uiDispatcher))
|
|
|
|
|
|
{
|
2017-08-13 05:17:16 +02:00
|
|
|
|
usleep (100);
|
2017-08-12 23:02:00 +02:00
|
|
|
|
uiDispatcher.invoke();
|
|
|
|
|
|
}
|
|
|
|
|
|
session.join();
|
|
|
|
|
|
//------end-multithreaded-mutation---
|
|
|
|
|
|
|
2017-08-22 20:07:33 +02:00
|
|
|
|
// on rare occasions we (consumer thread)
|
|
|
|
|
|
// prematurely empty the queue...
|
|
|
|
|
|
while (not isnil(uiDispatcher))
|
|
|
|
|
|
{
|
|
|
|
|
|
uiDispatcher.invoke();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
|
2017-08-13 05:17:16 +02:00
|
|
|
|
// now verify rootMock has been properly assimilated...
|
|
|
|
|
|
uint generatedBorgs = rootMock.scope.size();
|
|
|
|
|
|
|
|
|
|
|
|
// root and all Borg child nodes are connected to the UI-Bus
|
2018-11-15 23:59:23 +01:00
|
|
|
|
CHECK (1 + generatedBorgs == stage::test::Nexus::size());
|
2017-08-13 05:17:16 +02:00
|
|
|
|
|
|
|
|
|
|
uint64_t borgChecksum = 0;
|
|
|
|
|
|
for (uint i=0; i<generatedBorgs; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
MockElm& borg = *rootMock.scope[i];
|
|
|
|
|
|
CHECK (contains (borg.attrib, "borgID"));
|
|
|
|
|
|
string borgID = borg.attrib["borgID"];
|
|
|
|
|
|
borgChecksum += lexical_cast<int> (borgID);
|
|
|
|
|
|
string childID = borg.getID().getSym();
|
|
|
|
|
|
CHECK (contains (childID, borgID));
|
|
|
|
|
|
CHECK (contains (childID, " of ")); // e.g. "3 of 5"
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (nexusLog.verifyCall("routeAdd").arg(rootMock.getID(), memLocation(rootMock)) // rootMock was attached to Nexus
|
|
|
|
|
|
.beforeCall("change") .argMatch(rootMock.getID(), // diff message sent via UI-Bus
|
2018-08-16 21:02:29 +02:00
|
|
|
|
"after.+?_ATTRIBS_.+?" // verify diff pattern generated for each Borg
|
|
|
|
|
|
"ins.+?"+childID+".+?"
|
|
|
|
|
|
"mut.+?"+childID+".+?"
|
|
|
|
|
|
"ins.+?borgID.+?"+borgID+".+?"
|
|
|
|
|
|
"emu.+?"+childID)
|
2017-08-13 05:17:16 +02:00
|
|
|
|
.beforeCall("routeAdd").arg(borg.getID(), memLocation(borg)) // Borg was inserted as child and attached to Nexus
|
|
|
|
|
|
.beforeEvent("applied diff to "+string(rootMock.getID()))
|
2018-08-16 21:40:10 +02:00
|
|
|
|
); ////////////////////////////////////////////////////////TICKET #1158
|
2017-08-13 05:17:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (rootMock.attrib["α"] == "Quadrant"); // attribute alpha was preserved while injecting all those Borg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// sanity checks
|
|
|
|
|
|
CHECK (borgChecksum == session.borgChecksum_); // no Borgs got lost
|
|
|
|
|
|
CHECK (0 == generator_instances); // no generator instance leaks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-12 23:02:00 +02:00
|
|
|
|
cout << "____Event-Log_________________\n"
|
|
|
|
|
|
<< util::join(rootMock.getLog(), "\n")
|
|
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
|
|
|
|
|
|
|
|
|
|
|
cout << "____Nexus-Log_________________\n"
|
|
|
|
|
|
<< util::join(nexusLog, "\n")
|
|
|
|
|
|
<< "\n───╼━━━━━━━━━╾────────────────"<<endl;
|
2017-08-13 05:17:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static string
|
|
|
|
|
|
memLocation (Tangible& uiElm)
|
|
|
|
|
|
{
|
|
|
|
|
|
return lib::idi::instanceTypeID (&uiElm);
|
2015-11-26 22:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
2018-11-16 22:38:29 +01:00
|
|
|
|
LAUNCHER (BusTerm_test, "unit stage");
|
2015-11-26 22:23:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
2018-11-15 23:55:13 +01:00
|
|
|
|
}}} // namespace stage::model::test
|