Lumiera  0.pre.03
»edityourfreedom«
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/sync.hpp"
31 #include "lib/sync-classlock.hpp"
33 #include "include/ui-protocol.hpp"
34 #include "gui/ctrl/bus-term.hpp"
36 #include "proc/control/command.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 gui {
68 namespace model{
69 namespace test {
70 
71  using lib::idi::EntryID;
75  using gui::ctrl::BusTerm;
76  using gui::test::MockElm;
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 proc::control::LUMIERA_ERROR_UNBOUND_ARGUMENTS;
92  using lumiera::error::LUMIERA_ERROR_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
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
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  gui::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 
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"
220  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
221  }
222 
223 
225  void
227  {
230  Symbol cmd = gui::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 (gui::test::Nexus::wasBound(cmd, text, clip, luid));
253  CHECK (not gui::test::Nexus::wasBound(cmd, "lololo"));
254  CHECK (gui::test::Nexus::wasInvoked(cmd));
255  CHECK (gui::test::Nexus::wasInvoked(cmd, text, clip, luid));
256  CHECK (not gui::test::Nexus::wasInvoked(cmd, " huh ", clip, luid));
257  CHECK (not gui::test::Nexus::wasInvoked(cmd, text, clip));
258 
259  // Mock commands are automatically unique
260  auto cmdX = gui::test::Nexus::prepareMockCmd<>();
261  auto cmdY = gui::test::Nexus::prepareMockCmd<>();
262  CHECK (cmd != cmdX);
263  CHECK (cmd != cmdY);
264 
265  CHECK (not gui::test::Nexus::wasInvoked(cmdX));
266  CHECK (not gui::test::Nexus::wasInvoked(cmdY));
267 
268  cout << "____Nexus-Log_________________\n"
270  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
271 
272  gui::test::Nexus::setCommandHandler(); // deinstall custom command handler
273  }
274 
275 
279  void
281  {
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"
318  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
319  }
320 
321 
323  void
325  {
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 = gui::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  {
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 = gui::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  {
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 = gui::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 = gui::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  gui::test::Nexus::setStateMarkHandler(); // deinstall custom state mark handler
542  }
543 
544 
545 
546 
547 
562  void
564  {
566 
567  struct SessionThread
568  : Sync<>
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  uint cnt = rand() % MAX_RAND_BORGS;
672  for (uint i=0; i<cnt; ++i)
673  {
674  uint delay = rand() % MAX_RAND_DELAY;
675  uint id = rand() % MAX_RAND_NUMBS;
676  usleep (delay);
677  scheduleBorg (id);
678  notifyGUI (new BorgGenerator{*this, i});
679  }
680  }}
681  { }
682  };
683 
684 
685 
687 
688  MockElm rootMock("alpha zero");
689  ID rootID = rootMock.getID();
690 
691  rootMock.attrib["α"] = "Quadrant";
692  CHECK ("Quadrant" == rootMock.attrib["α"]);
693  CHECK (isnil (rootMock.scope));
694 
695  CHECK (0 == generator_instances);
696 
697 
698  // The final part in the puzzle is to dispatch the diff messages into the UI
699  // In the real application, this operation is provided by the NotificationService.
700  // It has access to the UI-Bus, but has to ensure all bus operations are actually
701  // performed on the UI event thread.
702  auto& uiBus = gui::test::Nexus::testUI();
703  CallQueue uiDispatcher;
704  auto notifyGUI = [&](DiffSource* diffGenerator)
705  {
706  uiDispatcher.feed ([&, diffGenerator]()
707  {
708  // apply and consume diff message stored within closure
709  uiBus.change (rootID, MutationMessage{diffGenerator});
710  });
711  };
712 
713 
714 
715  //----start-multithreaded-mutation---
716  SessionThread session{notifyGUI};
717  usleep (2 * MAX_RAND_DELAY);
718  while (not isnil(uiDispatcher))
719  {
720  usleep (100);
721  uiDispatcher.invoke();
722  }
723  session.join();
724  //------end-multithreaded-mutation---
725 
726  // on rare occasions we (consumer thread)
727  // prematurely empty the queue...
728  while (not isnil(uiDispatcher))
729  {
730  uiDispatcher.invoke();
731  }
732 
733 
734  // now verify rootMock has been properly assimilated...
735  uint generatedBorgs = rootMock.scope.size();
736 
737  // root and all Borg child nodes are connected to the UI-Bus
738  CHECK (1 + generatedBorgs == gui::test::Nexus::size());
739 
740  uint64_t borgChecksum = 0;
741  for (uint i=0; i<generatedBorgs; ++i)
742  {
743  MockElm& borg = *rootMock.scope[i];
744  CHECK (contains (borg.attrib, "borgID"));
745  string borgID = borg.attrib["borgID"];
746  borgChecksum += lexical_cast<int> (borgID);
747  string childID = borg.getID().getSym();
748  CHECK (contains (childID, borgID));
749  CHECK (contains (childID, " of ")); // e.g. "3 of 5"
750 
751  CHECK (nexusLog.verifyCall("routeAdd").arg(rootMock.getID(), memLocation(rootMock)) // rootMock was attached to Nexus
752  .beforeCall("change") .argMatch(rootMock.getID(), // diff message sent via UI-Bus
753  "after.+?_ATTRIBS_.+?" // verify diff pattern generated for each Borg
754  "ins.+?"+childID+".+?"
755  "mut.+?"+childID+".+?"
756  "ins.+?borgID.+?"+borgID+".+?"
757  "emu.+?"+childID)
758  .beforeCall("routeAdd").arg(borg.getID(), memLocation(borg)) // Borg was inserted as child and attached to Nexus
759  .beforeEvent("applied diff to "+string(rootMock.getID()))
760  );
761  }
762 
763  CHECK (rootMock.attrib["α"] == "Quadrant"); // attribute alpha was preserved while injecting all those Borg
764 
765 
766  // sanity checks
767  CHECK (borgChecksum == session.borgChecksum_); // no Borgs got lost
768  CHECK (0 == generator_instances); // no generator instance leaks
769 
770 
771 
772  cout << "____Event-Log_________________\n"
773  << util::join(rootMock.getLog(), "\n")
774  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
775 
776  cout << "____Nexus-Log_________________\n"
777  << util::join(nexusLog, "\n")
778  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
779  }
780 
781 
782  static string
784  {
785  return lib::idi::instanceTypeID (&uiElm);
786  }
787  };
788 
789 
791  LAUNCHER (BusTerm_test, "unit gui");
792 
793 
794 }}} // namespace gui::model::test
static string memLocation(Tangible &uiElm)
Facility for monitor object based locking.
Definition: sync.hpp:425
type erased baseclass for building a combined hash and symbolic ID.
Definition: entry-id.hpp:135
Rec::Mutator MakeRec
Definition: gen-node.hpp:135
Generic Message with an embedded diff, to describe changes to model elements.
Record< GenNode > Rec
Definition: gen-node.hpp:133
static ctrl::BusTerm & testUI()
get a connection point to a UI backbone faked for test
Definition: test-nexus.cpp:375
CallQueue & invoke()
Definition: call-queue.hpp:86
bool isTouched() const
Definition: mock-elm.hpp:377
Automatically use custom string conversion in C++ stream output.
std::vector< PMockElm > scope
Definition: mock-elm.hpp:366
Constructor for a specially crafted &#39;ref GenNode&#39;.
Definition: gen-node.hpp:733
Hard wired key constants and basic definitions for communication with the GUI.
A Stack which can be popped by iterating.
Definition: iter-stack.hpp:122
string randStr(size_t len)
create garbage string of given length
Definition: test-helper.cpp:61
virtual StateMark currentState(ID uiElm, string propertyKey) const =0
constexpr X const & max(X const &a, X const &b)
A threadsafe queue for bound void(void) functors.
Definition: call-queue.hpp:58
Definition: run.hpp:49
A fake UI backbone for investigations and unit testing.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:47
CallQueue & feed(Operation &&op)
Definition: call-queue.hpp:73
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:436
Interface: a component to maintain persistent interface state.
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:273
Front-end for printf-style string template interpolation.
static ctrl::StateManager & getMockStateManager()
Definition: test-nexus.cpp:578
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
bool canExec() const
Definition: command.cpp:357
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:128
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify a statement indeed raises an exception.
EventLog const & getLog() const
Definition: mock-elm.hpp:470
ID getID() const
Definition: mock-elm.hpp:371
EventMatch & arg(ARGS const &...args)
refine filter to additionally require specific arguments
Definition: event-log.hpp:206
string join(CON &&coll, string const &delim=", ")
enumerate a collection&#39;s contents, separated by delimiter.
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
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.
EventLog & joinLog(MockElm &otherMock)
Definition: mock-elm.hpp:476
A Queue which can be pulled by iterating.
Definition: iter-stack.hpp:197
void slotExpand()
Expand this element and remember the expanded state.
Definition: tangible.cpp:157
Lumiera unique object identifier.
Attachment point to the UI-Bus.
Object Monitor based synchronisation.
IterStack & push(TY const &elm)
Definition: iter-stack.hpp:129
void slotCollapse()
Collapse or minimise this element and remember the collapsed state.
Definition: tangible.cpp:169
static lib::test::EventLog const & startNewLog()
Definition: test-nexus.cpp:387
unsigned int delay[threads_per_pool_count *LUMIERA_THREADCLASS_COUNT]
Token or Atom with distinct identity.
Definition: symbol.hpp:116
EventMatch verifyCall(string match) const
start a query to match especially a function call
Definition: event-log.cpp:776
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition: util.hpp:205
A Queue for function invocations, allowing them to be dispatched on demand.
std::map< string, string > attrib
Definition: mock-elm.hpp:365
std::vector< string > & Arg
Definition: run.hpp:54
EventMatch ensureNot(string match) const
start a query to ensure the given expression does not match.
Definition: event-log.cpp:794
LAUNCHER(AbstractTangible_test, "unit gui")
Register this test class...
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:755
#define MARK_TEST_FUN
Macro to mark the current test function in STDOUT.
return NULL
Definition: llist.h:596
EventMatch & beforeEvent(string match)
find a match for an "event" after the current point of reference
Definition: event-log.cpp:415
Simple test class runner.
const Symbol MARK_Flash
Definition: ui-protocol.cpp:54
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
string getMessage() const
Definition: mock-elm.hpp:395
Convenience front-end for basic thread handling needs.
Generic building block for tree shaped (meta)data structures.
Proc-Layer command frontend.
A special implementation of lib::Sync, where the storage of the object monitor is associated directly...
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:225
static const Ref NO
symbolic ID ref "_NO_"
Definition: gen-node.hpp:754
bool isnil(lib::time::Duration const &dur)
Definition: timevalue.hpp:642
Variant of the standard case, allowing additionally to join on the termination of this thread...
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::StateManager & useMockStateManager()
install a standard handler for state mark messages, which is actually backed by a mock implementation...
Definition: test-nexus.cpp:562
Hash implementation based on a lumiera unique object id (LUID) When invoking the default ctor...
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
Interface common to all UI elements of relevance for the Lumiera application.
Definition: tangible.hpp:165
Namespace of Session and user visible high-level objects.
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:758
virtual bool mark(ID subject, GenNode const &mark)
route a state update or notification to the given subject.
Definition: ui-bus.cpp:177
EventMatch verify(string match) const
start a query to match for some substring.
Definition: event-log.cpp:740
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:125
bool isExpanded() const
Definition: mock-elm.hpp:383
Bare symbolic and hash ID used for accounting of asset like entries.
Interface: handling of persistent interface state.
TreeDiffLanguage::DiffStep DiffStep
IterSource< DiffStep > DiffSource
Mock UI element or controller.
Definition: mock-elm.hpp:110
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:467
EventMatch & on(string targetID)
refine filter to additionally match the &#39;this&#39; attribute
Definition: event-log.cpp:571
connection point at the UI-Bus.
Definition: bus-term.hpp:105
lib::time::Time randTime()
create a random but not insane Time value
Preconfigured adapters for some STL container standard usage situations.
a family of time value like entities and their relationships.
void markErr(string error)
push an error state tag to the element
Definition: tangible.cpp:137
bool isError() const
Definition: mock-elm.hpp:389
A generic interface element instrumented for unit testing.
lib::idi::BareEntryID const & ID
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
void reset()
invoke the generic reset hook
Definition: tangible.cpp:61
generic data element node within a tree
Definition: gen-node.hpp:213
string getError() const
Definition: mock-elm.hpp:401
ContentSnapshot< IT > dischargeToSnapshot(IT &ii)
Take a snapshot of the given LumieraIterator, which is thereby consumed.
static lib::test::EventLog const & getLog()
Definition: test-nexus.cpp:381
static size_t size()
Definition: test-nexus.cpp:393