Lumiera  0.pre.03
»edit your freedom«
bus-term-test.cpp
Go to the documentation of this file.
1 /*
2  BusTerm(Test) - cover the building block of the UI-Bus
3 
4  Copyright (C) Lumiera.org
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
29 #include "lib/test/test-helper.hpp"
30 #include "lib/thread.hpp"
31 #include "lib/sync.hpp"
32 #include "lib/sync-classlock.hpp"
33 #include "include/ui-protocol.hpp"
34 #include "stage/ctrl/bus-term.hpp"
37 #include "test/test-nexus.hpp"
38 #include "test/mock-elm.hpp"
39 #include "lib/diff/gen-node.hpp"
41 #include "lib/idi/entry-id.hpp"
42 #include "lib/iter-adapter-stl.hpp"
43 #include "lib/iter-stack.hpp"
44 #include "lib/call-queue.hpp"
45 #include "lib/format-string.hpp"
46 #include "lib/format-cout.hpp"
47 #include "lib/time/timevalue.hpp"
48 #include "lib/luid.h"
49 #include "lib/util.hpp"
50 
51 #include <boost/lexical_cast.hpp>
52 
53 using boost::lexical_cast;
54 
55 using lib::Sync;
56 using lib::ClassLock;
59 using lib::IterQueue;
60 using lib::IterStack;
61 using std::function;
62 using util::contains;
63 using util::isnil;
64 using util::_Fmt;
65 
66 
67 namespace stage {
68 namespace model{
69 namespace test {
70 
71  using lib::idi::EntryID;
80  using lib::diff::DiffStep;
81  using lib::diff::GenNode;
82  using lib::diff::MakeRec;
83  using lib::diff::Rec;
84  using lib::diff::Ref;
85  using lib::time::Time;
86  using lib::time::TimeSpan;
87  using lib::hash::LuidH;
88  using lib::test::EventLog;
89  using lib::CallQueue;
90 
91  using LERR_(UNBOUND_ARGUMENTS);
92  using LERR_(WRONG_TYPE);
93 
94  using ID = lib::idi::BareEntryID const&;
95 
96 
97  namespace {// test data...
98 
99  // --------random-diff-test------
100  uint const MAX_RAND_BORGS = 100; // stay below 400, verification export grows quadratic
101  uint const MAX_RAND_NUMBS = 500;
102  uint const MAX_RAND_DELAY = 5000; // throttle generation, since diff application is slower
103  // --------random-diff-test------
104 
105  int generator_instances = 0;
106  }
107 
108 
109 
110 
111 
112 
113  /**************************************************************************/
135  class BusTerm_test : public Test
136  {
137 
138  virtual void
139  run (Arg)
140  {
144  replayStateMark();
146  clearStates();
147  pushDiff();
148  }
149 
150 
162  void
164  {
166  // our dummy will be linked with this identity
167  BareEntryID elmID = EntryID<MockElm>{"zeitgeist"};
168 
169  // Access the log on the Test-Nexus hub
170  EventLog nexusLog = stage::test::Nexus::startNewLog();
171  CHECK (nexusLog.ensureNot("zeitgeist"));
172 
173  MockElm mock(elmID);
174  CHECK (nexusLog.verifyCall("routeAdd").on("TestNexus").arg(elmID,"Tangible") // Note: invoked from ctor, so it is just a tangible at the moment
175  .beforeEvent("TestNexus", "added route to bID-zeitgeist"));
176 
177  EventLog elmLog = mock.getLog();
178  CHECK (elmLog.verifyCall("ctor").on(&mock)
179  .beforeEvent("create", "zeitgeist"));
180 
181 
182  // now verify there is indeed bidirectional connectivity...
183  CHECK (elmLog.ensureNot("expanded"));
184  CHECK (elmLog.ensureNot("doFlash"));
185  CHECK (nexusLog.ensureNot("zeitgeist").arg("expand"));
186  CHECK (nexusLog.ensureNot("zeitgeist").arg("Flash"));
187 
188  // invoke action on element to cause upstream message (with a "state mark")
189  mock.slotExpand();
190  CHECK (elmLog.verifyEvent("expanded"));
191  CHECK (nexusLog.verifyCall("note").on("TestNexus").arg(elmID, "GenNode-ID(\"expand\")-DataCap|«bool»|true"));
192 
193  // send a state mark down to the mock element
194  stage::test::Nexus::testUI().mark (elmID, GenNode(string{MARK_Flash}, 23));
195  CHECK (nexusLog.verifyCall("mark").on("TestNexus").arg(elmID, MARK_Flash)
196  .beforeEvent("TestNexus", "mark to bID-zeitgeist"));
197  CHECK (elmLog.verifyCall("doFlash").on("zeitgeist"));
198 
199 
200  // kill the zeitgeist and verify disconnection
201  mock.kill();
202  CHECK (elmLog.verifyEvent("destroy","zeitgeist"));
203  CHECK (nexusLog.verifyCall("routeDetach").on("TestNexus").arg(elmID)
204  .beforeEvent("TestNexus", "removed route to bID-zeitgeist"));
205 
206  stage::test::Nexus::testUI().mark (elmID, GenNode({MARK_Flash}, 88));
207  CHECK (nexusLog.verify("removed route to bID-zeitgeist")
208  .beforeCall("mark").on("TestNexus").arg(elmID, MARK_Flash)
209  .beforeEvent("warn","discarding mark to unknown bID-zeitgeist"));
210  CHECK (elmLog.ensureNot("Flash")
211  .afterEvent("destroy","zeitgeist"));
212 
213 
214  cout << "____Probe-Log_________________\n"
215  << util::join(elmLog, "\n")
216  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
217 
218  cout << "____Nexus-Log_________________\n"
219  << util::join(stage::test::Nexus::getLog(), "\n")
220  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
221  }
222 
223 
225  void
227  {
229  stage::test::Nexus::startNewLog();
230  Symbol cmd = stage::test::Nexus::prepareMockCmd<string, TimeSpan, LuidH>();
231 
232  MockElm mock("uiElm");
233 
234  // random command arguments...
235  string text {lib::test::randStr(12)};
236  TimeSpan clip (Time(1,2,3), lib::test::randTime());
237  LuidH luid;
238 
239  // we cannot invoke commands without binding required arguments
240  VERIFY_ERROR (WRONG_TYPE, mock.invoke(cmd) );
241 
242  // proper argument typing is ensured while dispatching the bind message.
243  VERIFY_ERROR (WRONG_TYPE, mock.invoke(cmd, Rec({"lalala"})) );
244 
245  // command can't be issued, since it's still unbound
246  CHECK (not Command::canExec(cmd));
247 
248 
249  mock.invoke (cmd, text, clip, luid);
250 
251  CHECK (Command::canExec(cmd));
252  CHECK (stage::test::Nexus::wasBound(cmd, text, clip, luid));
253  CHECK (not stage::test::Nexus::wasBound(cmd, "lololo"));
254  CHECK (stage::test::Nexus::wasInvoked(cmd));
255  CHECK (stage::test::Nexus::wasInvoked(cmd, text, clip, luid));
256  CHECK (not stage::test::Nexus::wasInvoked(cmd, " huh ", clip, luid));
257  CHECK (not stage::test::Nexus::wasInvoked(cmd, text, clip));
258 
259  // Mock commands are automatically unique
260  auto cmdX = stage::test::Nexus::prepareMockCmd<>();
261  auto cmdY = stage::test::Nexus::prepareMockCmd<>();
262  CHECK (cmd != cmdX);
263  CHECK (cmd != cmdY);
264 
265  CHECK (not stage::test::Nexus::wasInvoked(cmdX));
266  CHECK (not stage::test::Nexus::wasInvoked(cmdY));
267 
268  cout << "____Nexus-Log_________________\n"
269  << util::join(stage::test::Nexus::getLog(), "\n")
270  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
271 
272  stage::test::Nexus::setCommandHandler(); // deinstall custom command handler
273  }
274 
275 
279  void
281  {
283  stage::test::Nexus::startNewLog();
285 
286  MockElm mockA("alpha"); BareEntryID alpha = mockA.getID();
287  MockElm mockB("bravo"); BareEntryID bravo = mockB.getID();
288  MockElm mockC("charly"); BareEntryID charly = mockC.getID();
289 
290  mockA.slotExpand();
291 
292  mockB.slotExpand();
293  mockB.slotCollapse();
294 
295  CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", true ));
296  CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", false ));
297 
298  // handling of missing information
299  CHECK (stateManager.currentState(charly, "expand") == Ref::NO); // no data recorded yet
300  CHECK (stateManager.currentState(bravo, "extinct") == Ref::NO); // unknown property
301 
302  EntryID<MockElm> bruno("bruno");
303  CHECK (stateManager.currentState(bruno, "expand") == Ref::NO); // who knows bruno?
304 
305  mockC.slotExpand();
306  CHECK (stateManager.currentState(charly, "expand") == GenNode("expand", true ));
307 
308  // error states can be sticky
309  mockC.markErr("overinflated");
310  CHECK (stateManager.currentState(charly, "Error") == GenNode("Error", "overinflated"));
311 
312  mockC.reset();
313  CHECK (stateManager.currentState(charly, "expand") == Ref::NO); // back to void
314 
315 
316  cout << "____Nexus-Log_________________\n"
317  << util::join(stage::test::Nexus::getLog(), "\n")
318  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
319  }
320 
321 
323  void
325  {
327  StateManager& stateManager = stage::test::Nexus::getMockStateManager();
328 
329  MockElm mockA("alpha");
330  // no "bravo" this time
331  MockElm mockC("charly");
332 
333  CHECK (not mockA.isExpanded());
334  CHECK (not mockC.isTouched());
335 
336  BareEntryID alpha = mockA.getID();
337  stateManager.replayState (alpha, "expand");
338  CHECK (mockA.isExpanded());
339 
340  auto& uiBus = stage::test::Nexus::testUI();
341  uiBus.mark (mockA.getID(), GenNode{"expand", false});
342 
343  CHECK (not mockA.isExpanded());
344  CHECK (mockA.isTouched());
345 
346  stateManager.replayAllState ("expand");
347 
348  CHECK (mockA.isExpanded());
349  CHECK (not mockC.isExpanded());
350  CHECK (not mockC.isTouched());
351  }
352 
353 
355  void
357  {
359  EventLog nexusLog = stage::test::Nexus::startNewLog();
360 
361  MockElm mockA("alpha"); BareEntryID alpha = mockA.getID(); mockA.joinLog (nexusLog);
362  MockElm mockB("bravo"); BareEntryID bravo = mockB.getID(); mockB.joinLog (nexusLog);
363  MockElm mockC("charly"); BareEntryID charly = mockC.getID(); mockC.joinLog (nexusLog);
364 
365  auto& uiBus = stage::test::Nexus::testUI();
366 
367  CHECK (not mockA.isTouched());
368  CHECK (not mockB.isTouched());
369  CHECK (not mockC.isTouched());
370 
371  uiBus.mark (alpha, GenNode{"Message", "Centauri"});
372  uiBus.mark (bravo, GenNode{"Flash", true});
373  uiBus.mark (charly, GenNode{"Message", "Delta"});
374  uiBus.mark (charly, GenNode{"Error", "Echo"});
375 
376  CHECK ( mockA.isTouched());
377  CHECK (not mockB.isTouched());
378  CHECK ( mockC.isTouched());
379 
380  CHECK (not mockA.isError());
381  CHECK (not mockB.isError());
382  CHECK ( mockC.isError());
383 
384  CHECK ("Centauri" == mockA.getMessage());
385  CHECK ("Delta" == mockC.getMessage());
386 
387  CHECK ("Echo" == mockC.getError());
388 
389  // verify the message passing in the combined log...
390  CHECK (nexusLog.verifyEvent("create", "alpha")
391  .beforeCall("mark").on("TestNexus").arg("alpha", "Centauri") // bus API invoked
392  .beforeCall("doMsg").on("alpha").arg("Centauri") // handler on target invoked
393  .beforeEvent("mark", "Centauri") // target action activated
394  .beforeEvent("TestNexus","delivered mark to bID-alpha")); // dispatch done within UI-Bus
395 
396  CHECK (nexusLog.verifyEvent("TestNexus","delivered mark to bID-alpha")
397  .beforeCall("mark").on("TestNexus").arg("bravo", "GenNode-ID(\"Flash\")-DataCap|«bool»|true")
398  .beforeCall("doFlash").on("bravo")
399  .beforeEvent("TestNexus","delivered mark to bID-bravo"));
400 
401  // NOTE: calls are passed down synchronously, in one hop, and in sequence
402  CHECK (nexusLog.verifyEvent("TestNexus","delivered mark to bID-bravo")
403  .beforeCall("mark").on("TestNexus").arg("charly", "GenNode-ID(\"Message\")-DataCap|«string»|Delta")
404  .beforeCall("doMsg").on("charly").arg("Delta")
405  .beforeEvent("mark", "Delta").id("Message")
406  .beforeEvent("TestNexus","delivered mark to bID-charly")
407  .beforeCall("mark").on("TestNexus").arg("charly", "GenNode-ID(\"Error\")-DataCap|«string»|Echo")
408  .beforeCall("doErr").on("charly").arg("Echo")
409  .beforeEvent("mark", "Echo").id("Error")
410  .beforeEvent("TestNexus","delivered mark to bID-charly"));
411 
412 
413  // broadcast message
414  uiBus.markAll (GenNode{"Message", "Foxtrot"});
415  CHECK (not mockA.isError());
416  CHECK (not mockB.isError());
417  CHECK ( mockC.isError());
418  CHECK ( mockA.isTouched());
419  CHECK ( mockB.isTouched());
420  CHECK ( mockC.isTouched());
421  CHECK ("Foxtrot" == mockA.getMessage());
422  CHECK ("Foxtrot" == mockB.getMessage());
423  CHECK ("Foxtrot" == mockC.getMessage());
424  CHECK ( "" == mockA.getError());
425  CHECK ( "" == mockB.getError());
426  CHECK ( "Echo" == mockC.getError());
427 
428  CHECK (nexusLog.verifyEvent("mark", "Echo").id("Error")
429  .beforeCall("markAll").on("TestNexus").arg("Foxtrot")
430  .beforeEvent("Broadcast", "Foxtrot")
431  .beforeCall("mark").on("TestNexus").arg("bravo", "GenNode-ID(\"Message\")-DataCap|«string»|Foxtrot")
432  .beforeCall("doMsg").on("bravo").arg("Foxtrot")
433  .beforeEvent("TestNexus", "broadcasted mark to 3 terminals"));
434 
435  // the order of dispatch is unspecified,
436  // but we know a regular mark call sequence happens for each connected terminal
437  CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
438  .beforeCall("mark").on("TestNexus").arg("alpha", "Foxtrot")
439  .beforeCall("doMsg").on("alpha").arg("Foxtrot")
440  .beforeEvent("TestNexus", "successfully broadcasted"));
441 
442  CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
443  .beforeCall("mark").on("TestNexus").arg("bravo", "Foxtrot")
444  .beforeCall("doMsg").on("bravo").arg("Foxtrot")
445  .beforeEvent("TestNexus", "successfully broadcasted"));
446 
447  CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
448  .beforeCall("mark").on("TestNexus").arg("charly", "Foxtrot")
449  .beforeCall("doMsg").on("charly").arg("Foxtrot")
450  .beforeEvent("TestNexus", "successfully broadcasted"));
451 
452 
453  cout << "____Nexus-Log_________________\n"
454  << util::join(nexusLog, "\n")
455  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
456  }
457 
458 
460  void
462  {
464  EventLog nexusLog = stage::test::Nexus::startNewLog();
465 
466  MockElm mockA("alpha"); BareEntryID alpha = mockA.getID(); mockA.joinLog (nexusLog);
467  MockElm mockB("bravo"); BareEntryID bravo = mockB.getID(); mockB.joinLog (nexusLog);
468  MockElm mockC("charly"); BareEntryID charly = mockC.getID(); mockC.joinLog (nexusLog);
469 
470  auto& uiBus = stage::test::Nexus::testUI();
471 
472  CHECK (not mockA.isTouched());
473  CHECK (not mockB.isTouched());
474  CHECK (not mockC.isTouched());
475 
476  mockB.slotExpand();
477  uiBus.mark (alpha, GenNode{"Message", "Centauri"});
478  uiBus.mark (charly, GenNode{"Message", "Delta"});
479  uiBus.mark (charly, GenNode{"Error", "Echo"});
480 
481  CHECK (mockB.isExpanded());
482  CHECK (mockC.isError());
483  CHECK ("Delta" == mockC.getMessage());
484  CHECK ("Centauri" == mockA.getMessage());
485 
486  // reset all notification messages
487  uiBus.markAll (GenNode{"clearMsg", true});
488  CHECK (mockB.isExpanded());
489  CHECK (mockC.isError());
490  CHECK (isnil (mockA.getMessage()));
491  CHECK (isnil (mockC.getMessage()));
492  CHECK ("Echo" == mockC.getError());
493 
494  uiBus.mark (bravo, GenNode{"Message", "miss"});
495  mockA.slotExpand();
496  mockA.slotCollapse();
497 
498  auto& stateManager = stage::test::Nexus::getMockStateManager();
499  CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", false ));
500  CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", true ));
501  CHECK (stateManager.currentState(charly,"expand") == Ref::NO);
502  CHECK (stateManager.currentState(charly, "Error") == GenNode("Error", "Echo")); // sticky error state was recorded
503 
504  // reset error state(s)
505  uiBus.markAll (GenNode{"clearErr", true});
506  CHECK (not mockA.isExpanded());
507  CHECK (mockB.isExpanded());
508  CHECK ("miss" == mockB.getMessage());
509  CHECK (not mockC.isError());
510 
511  CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", false ));
512  CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", true ));
513  CHECK (stateManager.currentState(charly,"expand") == Ref::NO);
514  CHECK (stateManager.currentState(charly, "Error") == Ref::NO); // sticky error state was cleared,
515  // because charly sent a clearErr state mark notification back
516 
517  // send global sweeping reset
518  uiBus.markAll (GenNode{"reset", true});
519 
520  CHECK (not mockA.isTouched());
521  CHECK (not mockB.isTouched());
522  CHECK (not mockC.isTouched());
523 
524  CHECK (not mockA.isExpanded());
525  CHECK (not mockB.isExpanded());
526 
527  CHECK (isnil (mockA.getMessage()));
528  CHECK (isnil (mockB.getMessage()));
529  CHECK (isnil (mockC.getMessage()));
530 
531  CHECK (stateManager.currentState(alpha, "expand") == Ref::NO);
532  CHECK (stateManager.currentState(bravo, "expand") == Ref::NO);
533  CHECK (stateManager.currentState(charly, "expand") == Ref::NO);
534  CHECK (stateManager.currentState(charly, "Error" ) == Ref::NO);
535 
536 
537  cout << "____Nexus-Log_________________\n"
538  << util::join(nexusLog, "\n")
539  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
540 
541  stage::test::Nexus::setStateMarkHandler(); // deinstall custom state mark handler
542  }
543 
544 
545 
546 
547 
562  void
564  {
566 
567  struct SessionThread
568  : Sync<>
569  , ThreadJoinable<>
570  {
571  // shared data
572  uint64_t borgChecksum_ = 0;
573  IterStack<uint> sessionBorgs_;
574 
575  // access to shared session data
576  void
577  scheduleBorg (uint id)
578  {
579  Lock sync{this};
580  borgChecksum_ += id;
581  sessionBorgs_.push(id);
582  }
583 
584  auto
585  dispatchBorgs()
586  {
587  Lock sync{this};
588  return dischargeToSnapshot (sessionBorgs_);
589  }
590 
591 
592 
601  struct BorgGenerator
604  , DiffSource
605  {
606  uint generatorID_;
607  SessionThread& theCube_;
608  IterQueue<DiffStep> steps_;
609 
610  BorgGenerator (SessionThread& motherShip, uint id)
611  : generatorID_{id}
612  , theCube_{motherShip}
613  {
615  ++generator_instances;
616  }
617  ~BorgGenerator()
618  {
620  --generator_instances;
621  }
622 
623 
624  /* == Interface IterSource<DiffStep> == */
625 
626  virtual DiffStep*
627  firstResult () override
628  {
629  REQUIRE (not steps_);
630  auto plannedBorgs = theCube_.dispatchBorgs();
631  uint max = plannedBorgs.size();
632  uint cur = 0;
633 
634  _Fmt borgName{"%d of %d ≺%03d.gen%03d≻"};
635 
636 
637  steps_.feed (after(Ref::ATTRIBS)); // important: retain all existing attributes
638  for (uint id : plannedBorgs) // Generate diff to inject a flock of Borg
639  {
640  GenNode borg = MakeRec().genNode(borgName % ++cur % max % id % generatorID_);
641  steps_.feed (ins(borg));
642  steps_.feed (mut(borg)); // open nested scope for this Borg
643  steps_.feed ( ins(GenNode{"borgID", int(id)}));
644  steps_.feed (emu(borg)); // close nested scope
645  }
646  steps_.feed (after(Ref::END)); // important: fast-forward and accept already existing Borgs
647 
648  return & *steps_; // the IterSource protocol requires us to return a ptr to current element
649  }
650 
651 
652  virtual void
653  nextResult (DiffStep*& pos) override
654  {
655  if (!pos) return;
656  if (steps_) ++steps_;
657  if (steps_)
658  pos = & *steps_; // pointer to current element
659  else
660  pos = NULL; // signal iteration end
661  }
662  };
663 
664 
668  SessionThread(function<void(DiffSource*)> notifyGUI)
669  : ThreadJoinable{"BusTerm_test: asynchronous diff mutation"
670  , [=]
671  {
672  uint cnt = rand() % MAX_RAND_BORGS;
673  for (uint i=0; i<cnt; ++i)
674  {
675  uint delay = rand() % MAX_RAND_DELAY;
676  uint id = rand() % MAX_RAND_NUMBS;
677  usleep (delay);
678  scheduleBorg (id);
679  notifyGUI (new BorgGenerator{*this, i});
680  }
681  }}
682  { }
683  };
684 
685 
686 
687  EventLog nexusLog = stage::test::Nexus::startNewLog();
688 
689  // the simulated »GUI model«
690  // — to be infested by hosts of Borg sent via Diff-Message...
691  MockElm rootMock("alpha zero");
692  ID rootID = rootMock.getID();
693 
694  rootMock.attrib["α"] = "Quadrant";
695  CHECK ("Quadrant" == rootMock.attrib["α"]);
696  CHECK (isnil (rootMock.scope));
697 
698  CHECK (0 == generator_instances);
699 
700 
701  // The final part in the puzzle is to dispatch the diff messages into the UI
702  // In the real application, this operation is provided by the NotificationService.
703  // It has access to the UI-Bus, but has to ensure all bus operations are actually
704  // performed on the UI event thread.
705  auto& uiBus = stage::test::Nexus::testUI();
706  CallQueue uiDispatcher;
707  auto notifyGUI = [&](DiffSource* diffGenerator)
708  {
709  uiDispatcher.feed ([&, diffGenerator]()
710  {
711  // apply and consume diff message stored within closure
712  uiBus.change (rootID, MutationMessage{diffGenerator});
713  });
714  };
715 
716 
717 
718  //----start-multithreaded-mutation---
719  SessionThread session{notifyGUI};
720  usleep (2 * MAX_RAND_DELAY);
721  while (not isnil(uiDispatcher))
722  {
723  usleep (100);
724  uiDispatcher.invoke();
725  }
726  session.join();
727  //------end-multithreaded-mutation---
728 
729  // on rare occasions we (consumer thread)
730  // prematurely empty the queue...
731  while (not isnil(uiDispatcher))
732  {
733  uiDispatcher.invoke();
734  }
735 
736 
737  // now verify rootMock has been properly assimilated...
738  uint generatedBorgs = rootMock.scope.size();
739 
740  // root and all Borg child nodes are connected to the UI-Bus
741  CHECK (1 + generatedBorgs == stage::test::Nexus::size());
742 
743  uint64_t borgChecksum = 0;
744  for (uint i=0; i<generatedBorgs; ++i)
745  {
746  MockElm& borg = *rootMock.scope[i];
747  CHECK (contains (borg.attrib, "borgID"));
748  string borgID = borg.attrib["borgID"];
749  borgChecksum += lexical_cast<int> (borgID);
750  string childID = borg.getID().getSym();
751  CHECK (contains (childID, borgID));
752  CHECK (contains (childID, " of ")); // e.g. "3 of 5"
753 
754  CHECK (nexusLog.verifyCall("routeAdd").arg(rootMock.getID(), memLocation(rootMock)) // rootMock was attached to Nexus
755  .beforeCall("change") .argMatch(rootMock.getID(), // diff message sent via UI-Bus
756  "after.+?_ATTRIBS_.+?" // verify diff pattern generated for each Borg
757  "ins.+?"+childID+".+?"
758  "mut.+?"+childID+".+?"
759  "ins.+?borgID.+?"+borgID+".+?"
760  "emu.+?"+childID)
761  .beforeCall("routeAdd").arg(borg.getID(), memLocation(borg)) // Borg was inserted as child and attached to Nexus
762  .beforeEvent("applied diff to "+string(rootMock.getID()))
763  );
764  }
765 
766  CHECK (rootMock.attrib["α"] == "Quadrant"); // attribute alpha was preserved while injecting all those Borg
767 
768 
769  // sanity checks
770  CHECK (borgChecksum == session.borgChecksum_); // no Borgs got lost
771  CHECK (0 == generator_instances); // no generator instance leaks
772 
773 
774 
775  cout << "____Event-Log_________________\n"
776  << util::join(rootMock.getLog(), "\n")
777  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
778 
779  cout << "____Nexus-Log_________________\n"
780  << util::join(nexusLog, "\n")
781  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
782  }
783 
784 
785  static string
786  memLocation (Tangible& uiElm)
787  {
788  return lib::idi::instanceTypeID (&uiElm);
789  }
790  };
791 
792 
794  LAUNCHER (BusTerm_test, "unit stage");
795 
796 
797 }}} // namespace stage::model::test
Facility for monitor object based locking.
Definition: sync.hpp:217
type erased baseclass for building a combined hash and symbolic ID.
Definition: entry-id.hpp:142
Variant of the standard case, requiring to wait and join() on the termination of this thread...
Definition: thread.hpp:676
Generic Message with an embedded diff, to describe changes to model elements.
void slotExpand()
Expand this element and remember the expanded state.
Definition: tangible.cpp:170
Automatically use custom string conversion in C++ stream output.
Constructor for a specially crafted &#39;ref GenNode&#39;.
Definition: gen-node.hpp:849
Hard wired key constants and basic definitions for communication with the GUI.
connection point at the UI-Bus.
Definition: bus-term.hpp:105
A Stack which can be popped by iterating.
Definition: iter-stack.hpp:122
A threadsafe queue for bound void(void) functors.
Definition: call-queue.hpp:58
Definition: run.hpp:49
static void setCommandHandler(CommandHandler=CommandHandler())
install a closure (custom handler function) to deal with any command invocations encountered in the t...
Definition: test-nexus.cpp:417
A fake UI backbone for investigations and unit testing.
void reset()
invoke the generic reset hook
Definition: tangible.cpp:74
Conveniently iterable stack and queue containers.
Any copy and copy construction prohibited.
Definition: nocopy.hpp:46
EventMatch & beforeCall(string match)
find a match for some function invocation after the current point of reference
Definition: event-log.cpp:447
Interface: a component to maintain persistent interface state.
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:284
Front-end for printf-style string template interpolation.
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:135
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
EventMatch & arg(ARGS const &...args)
refine filter to additionally require specific arguments
Definition: event-log.hpp:207
Opaque message to effect a structural change on a target, which is likewise only known in an abstract...
A front-end for using printf-style formatting.
Namespace of Session and user visible high-level objects.
Definition: sequence.hpp:74
A Queue which can be pulled by iterating.
Definition: iter-stack.hpp:197
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:308
Lumiera unique object identifier.
Attachment point to the UI-Bus.
Object Monitor based synchronisation.
ThreadJoinable(string const &, FUN &&, ARGS &&...) -> ThreadJoinable< std::invoke_result_t< FUN, ARGS... >>
deduction guide: find out about result value to capture from a generic callable.
Token or Atom with distinct identity.
Definition: symbol.hpp:126
Iteration source interface to abstract a data source, which then can be accessed through IterAdapter ...
Definition: iter-source.hpp:88
static ctrl::StateManager & useMockStateManager()
install a standard handler for state mark messages, which is actually backed by a mock implementation...
Definition: test-nexus.cpp:562
EventMatch verifyCall(string match) const
start a query to match especially a function call
Definition: event-log.cpp:797
A Queue for function invocations, allowing them to be dispatched on demand.
EventMatch ensureNot(string match) const
start a query to ensure the given expression does not match.
Definition: event-log.cpp:814
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:871
#define MARK_TEST_FUN
Macro to mark the current test function in STDOUT.
EventMatch & beforeEvent(string match)
find a match for an "event" after the current point of reference
Definition: event-log.cpp:426
Simple test class runner.
virtual bool mark(ID subject, GenNode const &mark)
route a state update or notification to the given subject.
Definition: ui-bus.cpp:177
void markErr(string error)
push an error state tag to the element
Definition: tangible.cpp:150
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:46
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
static bool wasInvoked(Symbol)
Test predicate: verify at least one actual invocation happened for the given commend, without matching any concrete arguments.
Definition: test-nexus.hpp:197
static bool wasBound(Symbol, ARGS const &...args)
Test predicate: verify by string match that the denoted command was actually bound against the given ...
Definition: test-nexus.hpp:159
ContentSnapshot< IT > dischargeToSnapshot(IT &ii)
Take a snapshot of the given LumieraIterator, which is thereby consumed.
Generic building block for tree shaped (meta)data structures.
Steam-Layer command frontend.
Convenience front-end to simplify and codify basic thread handling.
A special implementation of lib::Sync, where the storage of the object monitor is associated directly...
static void setStateMarkHandler(StateMarkHandler=StateMarkHandler())
similar to the custom command handler this hook allows to install a closure to intercept any "state m...
Definition: test-nexus.cpp:428
A collection of frequently used helper functions to support unit testing.
EventMatch & argMatch(ARGS const &...regExps)
refine filter to additionally cover all arguments with a series of regular expressions.
Definition: event-log.hpp:226
static const Ref NO
symbolic ID ref "_NO_"
Definition: gen-node.hpp:870
Interface: handling of persistent interface state.
void slotCollapse()
Collapse or minimise this element and remember the collapsed state.
Definition: tangible.cpp:182
string instanceTypeID(const TY *const obj)
designation of an distinct object instance
Definition: genfunc.hpp:125
A synchronisation protection guard employing a lock scoped to the parameter type as a whole...
static ctrl::BusTerm & testUI()
get a connection point to a UI backbone faked for test
Definition: test-nexus.cpp:375
Hash implementation based on a lumiera unique object id (LUID) When invoking the default ctor...
Mock UI element or controller.
Definition: mock-elm.hpp:108
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:874
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:124
EventMatch verify(string match) const
start a query to match for some substring.
Definition: event-log.cpp:761
Bare symbolic and hash ID used for accounting of asset like entries.
Interface common to all UI elements of relevance for the Lumiera application.
Definition: tangible.hpp:165
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:582
EventMatch & on(string targetID)
refine filter to additionally match the &#39;this&#39; attribute
Definition: event-log.cpp:592
Preconfigured adapters for some STL container standard usage situations.
a family of time value like entities and their relationships.
object-like record of data.
Definition: record.hpp:150
A generic interface element instrumented for unit testing.
generic data element node within a tree
Definition: gen-node.hpp:231
bool contains(SEQ const &cont, typename SEQ::const_reference val)
shortcut for brute-force containment test in any sequential container
Definition: util.hpp:255