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 "gui/ctrl/bus-term.hpp"
35 #include "proc/control/command.hpp"
36 #include "test/test-nexus.hpp"
37 #include "test/mock-elm.hpp"
38 #include "lib/diff/gen-node.hpp"
40 #include "lib/idi/entry-id.hpp"
41 #include "lib/iter-adapter-stl.hpp"
42 #include "lib/iter-stack.hpp"
43 #include "lib/call-queue.hpp"
44 #include "lib/format-string.hpp"
45 #include "lib/format-cout.hpp"
46 #include "lib/time/timevalue.hpp"
47 #include "lib/luid.h"
48 #include "lib/util.hpp"
49 
50 #include <boost/lexical_cast.hpp>
51 
52 using boost::lexical_cast;
53 
54 using lib::Sync;
55 using lib::ClassLock;
58 using lib::IterQueue;
59 using lib::IterStack;
60 using std::function;
61 using util::contains;
62 using util::isnil;
63 using util::_Fmt;
64 
65 
66 namespace gui {
67 namespace model{
68 namespace test {
69 
70  using lib::idi::EntryID;
74  using gui::ctrl::BusTerm;
75  using gui::test::MockElm;
79  using lib::diff::DiffStep;
80  using lib::diff::GenNode;
81  using lib::diff::MakeRec;
82  using lib::diff::Rec;
83  using lib::diff::Ref;
84  using lib::time::Time;
85  using lib::time::TimeSpan;
86  using lib::hash::LuidH;
87  using lib::test::EventLog;
88  using lib::CallQueue;
89 
90  using proc::control::LUMIERA_ERROR_UNBOUND_ARGUMENTS;
91  using lumiera::error::LUMIERA_ERROR_WRONG_TYPE;
92 
93  using ID = lib::idi::BareEntryID const&;
94 
95 
96  namespace {// test data...
97 
98  // --------random-diff-test------
99  uint const MAX_RAND_BORGS = 100; // stay below 400, verification export grows quadratic
100  uint const MAX_RAND_NUMBS = 500;
101  uint const MAX_RAND_DELAY = 5000; // throttle generation, since diff application is slower
102  // --------random-diff-test------
103 
104  int generator_instances = 0;
105  }
106 
107 
108 
109 
110 
111 
112  /**************************************************************************/
134  class BusTerm_test : public Test
135  {
136 
137  virtual void
139  {
143  replayStateMark();
145  clearStates();
146  pushDiff();
147  }
148 
149 
161  void
163  {
165  // our dummy will be linked with this identity
166  BareEntryID elmID = EntryID<MockElm>{"zeitgeist"};
167 
168  // Access the log on the Test-Nexus hub
170  CHECK (nexusLog.ensureNot("zeitgeist"));
171 
172  MockElm mock(elmID);
173  CHECK (nexusLog.verifyCall("routeAdd").on("TestNexus").arg(elmID,"Tangible") // Note: invoked from ctor, so it is just a tangible at the moment
174  .beforeEvent("TestNexus", "added route to bID-zeitgeist"));
175 
176  EventLog elmLog = mock.getLog();
177  CHECK (elmLog.verifyCall("ctor").on(&mock)
178  .beforeEvent("create", "zeitgeist"));
179 
180 
181  // now verify there is indeed bidirectional connectivity...
182  CHECK (elmLog.ensureNot("expanded"));
183  CHECK (elmLog.ensureNot("doFlash"));
184  CHECK (nexusLog.ensureNot("zeitgeist").arg("expand"));
185  CHECK (nexusLog.ensureNot("zeitgeist").arg("Flash"));
186 
187  // invoke action on element to cause upstream message (with a "state mark")
188  mock.slotExpand();
189  CHECK (elmLog.verifyEvent("expanded"));
190  CHECK (nexusLog.verifyCall("note").on("TestNexus").arg(elmID, "GenNode-ID(\"expand\")-DataCap|«bool»|true"));
191 
192  // send a state mark down to the mock element
193  gui::test::Nexus::testUI().mark (elmID, GenNode("Flash", 23));
194  CHECK (nexusLog.verifyCall("mark").on("TestNexus").arg(elmID, "Flash")
195  .beforeEvent("TestNexus", "mark to bID-zeitgeist"));
196  CHECK (elmLog.verifyCall("doFlash").on("zeitgeist"));
197 
198 
199  // kill the zeitgeist and verify disconnection
200  mock.kill();
201  CHECK (elmLog.verifyEvent("destroy","zeitgeist"));
202  CHECK (nexusLog.verifyCall("routeDetach").on("TestNexus").arg(elmID)
203  .beforeEvent("TestNexus", "removed route to bID-zeitgeist"));
204 
205  gui::test::Nexus::testUI().mark (elmID, GenNode("Flash", 88));
206  CHECK (nexusLog.verify("removed route to bID-zeitgeist")
207  .beforeCall("mark").on("TestNexus").arg(elmID, "Flash")
208  .beforeEvent("warn","discarding mark to unknown bID-zeitgeist"));
209  CHECK (elmLog.ensureNot("Flash")
210  .afterEvent("destroy","zeitgeist"));
211 
212 
213  cout << "____Probe-Log_________________\n"
214  << util::join(elmLog, "\n")
215  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
216 
217  cout << "____Nexus-Log_________________\n"
219  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
220  }
221 
222 
224  void
226  {
229  Symbol cmd = gui::test::Nexus::prepareMockCmd<string, TimeSpan, LuidH>();
230 
231  MockElm mock("uiElm");
232 
233  // random command arguments...
234  string text {lib::test::randStr(12)};
235  TimeSpan clip (Time(1,2,3), lib::test::randTime());
236  LuidH luid;
237 
238  // we cannot invoke commands without binding required arguments
239  VERIFY_ERROR (WRONG_TYPE, mock.invoke(cmd) );
240 
241  // proper argument typing is ensured while dispatching the bind message.
242  VERIFY_ERROR (WRONG_TYPE, mock.invoke(cmd, Rec({"lalala"})) );
243 
244  // command can't be issued, since it's still unbound
245  CHECK (not Command::canExec(cmd));
246 
247 
248  mock.invoke (cmd, text, clip, luid);
249 
250  CHECK (Command::canExec(cmd));
251  CHECK (gui::test::Nexus::wasBound(cmd, text, clip, luid));
252  CHECK (not gui::test::Nexus::wasBound(cmd, "lololo"));
253  CHECK (gui::test::Nexus::wasInvoked(cmd));
254  CHECK (gui::test::Nexus::wasInvoked(cmd, text, clip, luid));
255  CHECK (not gui::test::Nexus::wasInvoked(cmd, " huh ", clip, luid));
256  CHECK (not gui::test::Nexus::wasInvoked(cmd, text, clip));
257 
258  // Mock commands are automatically unique
259  auto cmdX = gui::test::Nexus::prepareMockCmd<>();
260  auto cmdY = gui::test::Nexus::prepareMockCmd<>();
261  CHECK (cmd != cmdX);
262  CHECK (cmd != cmdY);
263 
264  CHECK (not gui::test::Nexus::wasInvoked(cmdX));
265  CHECK (not gui::test::Nexus::wasInvoked(cmdY));
266 
267  cout << "____Nexus-Log_________________\n"
269  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
270 
271  gui::test::Nexus::setCommandHandler(); // deinstall custom command handler
272  }
273 
274 
278  void
280  {
284 
285  MockElm mockA("alpha"); BareEntryID alpha = mockA.getID();
286  MockElm mockB("bravo"); BareEntryID bravo = mockB.getID();
287  MockElm mockC("charly"); BareEntryID charly = mockC.getID();
288 
289  mockA.slotExpand();
290 
291  mockB.slotExpand();
292  mockB.slotCollapse();
293 
294  CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", true ));
295  CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", false ));
296 
297  // handling of missing information
298  CHECK (stateManager.currentState(charly, "expand") == Ref::NO); // no data recorded yet
299  CHECK (stateManager.currentState(bravo, "extinct") == Ref::NO); // unknown property
300 
301  EntryID<MockElm> bruno("bruno");
302  CHECK (stateManager.currentState(bruno, "expand") == Ref::NO); // who knows bruno?
303 
304  mockC.slotExpand();
305  CHECK (stateManager.currentState(charly, "expand") == GenNode("expand", true ));
306 
307  // error states can be sticky
308  mockC.markErr("overinflated");
309  CHECK (stateManager.currentState(charly, "Error") == GenNode("Error", "overinflated"));
310 
311  mockC.reset();
312  CHECK (stateManager.currentState(charly, "expand") == Ref::NO); // back to void
313 
314 
315  cout << "____Nexus-Log_________________\n"
317  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
318  }
319 
320 
322  void
324  {
327 
328  MockElm mockA("alpha");
329  // no "bravo" this time
330  MockElm mockC("charly");
331 
332  CHECK (not mockA.isExpanded());
333  CHECK (not mockC.isTouched());
334 
335  BareEntryID alpha = mockA.getID();
336  stateManager.replayState (alpha, "expand");
337  CHECK (mockA.isExpanded());
338 
339  auto& uiBus = gui::test::Nexus::testUI();
340  uiBus.mark (mockA.getID(), GenNode{"expand", false});
341 
342  CHECK (not mockA.isExpanded());
343  CHECK (mockA.isTouched());
344 
345  stateManager.replayAllState ("expand");
346 
347  CHECK (mockA.isExpanded());
348  CHECK (not mockC.isExpanded());
349  CHECK (not mockC.isTouched());
350  }
351 
352 
354  void
356  {
359 
360  MockElm mockA("alpha"); BareEntryID alpha = mockA.getID(); mockA.joinLog (nexusLog);
361  MockElm mockB("bravo"); BareEntryID bravo = mockB.getID(); mockB.joinLog (nexusLog);
362  MockElm mockC("charly"); BareEntryID charly = mockC.getID(); mockC.joinLog (nexusLog);
363 
364  auto& uiBus = gui::test::Nexus::testUI();
365 
366  CHECK (not mockA.isTouched());
367  CHECK (not mockB.isTouched());
368  CHECK (not mockC.isTouched());
369 
370  uiBus.mark (alpha, GenNode{"Message", "Centauri"});
371  uiBus.mark (bravo, GenNode{"Flash", true});
372  uiBus.mark (charly, GenNode{"Message", "Delta"});
373  uiBus.mark (charly, GenNode{"Error", "Echo"});
374 
375  CHECK ( mockA.isTouched());
376  CHECK (not mockB.isTouched());
377  CHECK ( mockC.isTouched());
378 
379  CHECK (not mockA.isError());
380  CHECK (not mockB.isError());
381  CHECK ( mockC.isError());
382 
383  CHECK ("Centauri" == mockA.getMessage());
384  CHECK ("Delta" == mockC.getMessage());
385 
386  CHECK ("Echo" == mockC.getError());
387 
388  // verify the message passing in the combined log...
389  CHECK (nexusLog.verifyEvent("create", "alpha")
390  .beforeCall("mark").on("TestNexus").arg("alpha", "Centauri") // bus API invoked
391  .beforeCall("doMsg").on("alpha").arg("Centauri") // handler on target invoked
392  .beforeEvent("mark", "Centauri") // target action activated
393  .beforeEvent("TestNexus","delivered mark to bID-alpha")); // dispatch done within UI-Bus
394 
395  CHECK (nexusLog.verifyEvent("TestNexus","delivered mark to bID-alpha")
396  .beforeCall("mark").on("TestNexus").arg("bravo", "GenNode-ID(\"Flash\")-DataCap|«bool»|true")
397  .beforeCall("doFlash").on("bravo")
398  .beforeEvent("TestNexus","delivered mark to bID-bravo"));
399 
400  // NOTE: calls are passed down synchronously, in one hop, and in sequence
401  CHECK (nexusLog.verifyEvent("TestNexus","delivered mark to bID-bravo")
402  .beforeCall("mark").on("TestNexus").arg("charly", "GenNode-ID(\"Message\")-DataCap|«string»|Delta")
403  .beforeCall("doMsg").on("charly").arg("Delta")
404  .beforeEvent("mark", "Delta").id("Message")
405  .beforeEvent("TestNexus","delivered mark to bID-charly")
406  .beforeCall("mark").on("TestNexus").arg("charly", "GenNode-ID(\"Error\")-DataCap|«string»|Echo")
407  .beforeCall("doErr").on("charly").arg("Echo")
408  .beforeEvent("mark", "Echo").id("Error")
409  .beforeEvent("TestNexus","delivered mark to bID-charly"));
410 
411 
412  // broadcast message
413  uiBus.markAll (GenNode{"Message", "Foxtrot"});
414  CHECK (not mockA.isError());
415  CHECK (not mockB.isError());
416  CHECK ( mockC.isError());
417  CHECK ( mockA.isTouched());
418  CHECK ( mockB.isTouched());
419  CHECK ( mockC.isTouched());
420  CHECK ("Foxtrot" == mockA.getMessage());
421  CHECK ("Foxtrot" == mockB.getMessage());
422  CHECK ("Foxtrot" == mockC.getMessage());
423  CHECK ( "" == mockA.getError());
424  CHECK ( "" == mockB.getError());
425  CHECK ( "Echo" == mockC.getError());
426 
427  CHECK (nexusLog.verifyEvent("mark", "Echo").id("Error")
428  .beforeCall("markAll").on("TestNexus").arg("Foxtrot")
429  .beforeEvent("Broadcast", "Foxtrot")
430  .beforeCall("mark").on("TestNexus").arg("bravo", "GenNode-ID(\"Message\")-DataCap|«string»|Foxtrot")
431  .beforeCall("doMsg").on("bravo").arg("Foxtrot")
432  .beforeEvent("TestNexus", "broadcasted mark to 3 terminals"));
433 
434  // the order of dispatch is unspecified,
435  // but we know a regular mark call sequence happens for each connected terminal
436  CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
437  .beforeCall("mark").on("TestNexus").arg("alpha", "Foxtrot")
438  .beforeCall("doMsg").on("alpha").arg("Foxtrot")
439  .beforeEvent("TestNexus", "successfully broadcasted"));
440 
441  CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
442  .beforeCall("mark").on("TestNexus").arg("bravo", "Foxtrot")
443  .beforeCall("doMsg").on("bravo").arg("Foxtrot")
444  .beforeEvent("TestNexus", "successfully broadcasted"));
445 
446  CHECK (nexusLog.verifyCall("markAll").on("TestNexus").arg("Foxtrot")
447  .beforeCall("mark").on("TestNexus").arg("charly", "Foxtrot")
448  .beforeCall("doMsg").on("charly").arg("Foxtrot")
449  .beforeEvent("TestNexus", "successfully broadcasted"));
450 
451 
452  cout << "____Nexus-Log_________________\n"
453  << util::join(nexusLog, "\n")
454  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
455  }
456 
457 
459  void
461  {
464 
465  MockElm mockA("alpha"); BareEntryID alpha = mockA.getID(); mockA.joinLog (nexusLog);
466  MockElm mockB("bravo"); BareEntryID bravo = mockB.getID(); mockB.joinLog (nexusLog);
467  MockElm mockC("charly"); BareEntryID charly = mockC.getID(); mockC.joinLog (nexusLog);
468 
469  auto& uiBus = gui::test::Nexus::testUI();
470 
471  CHECK (not mockA.isTouched());
472  CHECK (not mockB.isTouched());
473  CHECK (not mockC.isTouched());
474 
475  mockB.slotExpand();
476  uiBus.mark (alpha, GenNode{"Message", "Centauri"});
477  uiBus.mark (charly, GenNode{"Message", "Delta"});
478  uiBus.mark (charly, GenNode{"Error", "Echo"});
479 
480  CHECK (mockB.isExpanded());
481  CHECK (mockC.isError());
482  CHECK ("Delta" == mockC.getMessage());
483  CHECK ("Centauri" == mockA.getMessage());
484 
485  // reset all notification messages
486  uiBus.markAll (GenNode{"clearMsg", true});
487  CHECK (mockB.isExpanded());
488  CHECK (mockC.isError());
489  CHECK (isnil (mockA.getMessage()));
490  CHECK (isnil (mockC.getMessage()));
491  CHECK ("Echo" == mockC.getError());
492 
493  uiBus.mark (bravo, GenNode{"Message", "miss"});
494  mockA.slotExpand();
495  mockA.slotCollapse();
496 
497  auto& stateManager = gui::test::Nexus::getMockStateManager();
498  CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", false ));
499  CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", true ));
500  CHECK (stateManager.currentState(charly, "expand") == Ref::NO);
501  CHECK (stateManager.currentState(charly, "Error") == GenNode("Error", "Echo")); // sticky error state was recorded
502 
503  // reset error state(s)
504  uiBus.markAll (GenNode{"clearErr", true});
505  CHECK (not mockA.isExpanded());
506  CHECK (mockB.isExpanded());
507  CHECK ("miss" == mockB.getMessage());
508  CHECK (not mockC.isError());
509 
510  CHECK (stateManager.currentState(alpha, "expand") == GenNode("expand", false ));
511  CHECK (stateManager.currentState(bravo, "expand") == GenNode("expand", true ));
512  CHECK (stateManager.currentState(charly, "expand") == Ref::NO);
513  CHECK (stateManager.currentState(charly, "Error") == Ref::NO); // sticky error state was cleared,
514  // because charly sent a clearErr state mark notification back
515 
516  // send global sweeping reset
517  uiBus.markAll (GenNode{"reset", true});
518 
519  CHECK (not mockA.isTouched());
520  CHECK (not mockB.isTouched());
521  CHECK (not mockC.isTouched());
522 
523  CHECK (not mockA.isExpanded());
524  CHECK (not mockB.isExpanded());
525 
526  CHECK (isnil (mockA.getMessage()));
527  CHECK (isnil (mockB.getMessage()));
528  CHECK (isnil (mockC.getMessage()));
529 
530  CHECK (stateManager.currentState(alpha, "expand") == Ref::NO);
531  CHECK (stateManager.currentState(bravo, "expand") == Ref::NO);
532  CHECK (stateManager.currentState(charly, "expand") == Ref::NO);
533  CHECK (stateManager.currentState(charly, "Error" ) == Ref::NO);
534 
535 
536  cout << "____Nexus-Log_________________\n"
537  << util::join(nexusLog, "\n")
538  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
539 
540  gui::test::Nexus::setStateMarkHandler(); // deinstall custom state mark handler
541  }
542 
543 
544 
545 
546 
561  void
563  {
565 
566  struct SessionThread
567  : Sync<>
569  {
570  // shared data
571  uint64_t borgChecksum_ = 0;
572  IterStack<uint> sessionBorgs_;
573 
574  // access to shared session data
575  void
576  scheduleBorg (uint id)
577  {
578  Lock sync(this);
579  borgChecksum_ += id;
580  sessionBorgs_.push(id);
581  }
582 
583  auto
584  dispatchBorgs()
585  {
586  Lock sync(this);
587  return dischargeToSnapshot (sessionBorgs_);
588  }
589 
590 
591 
600  struct BorgGenerator
603  , DiffSource
604  {
605  uint generatorID_;
606  SessionThread& theCube_;
607  IterQueue<DiffStep> steps_;
608 
609  BorgGenerator (SessionThread& motherShip, uint id)
610  : generatorID_{id}
611  , theCube_{motherShip}
612  {
614  ++generator_instances;
615  }
616  ~BorgGenerator()
617  {
619  --generator_instances;
620  }
621 
622 
623  /* == Interface IterSource<DiffStep> == */
624 
625  virtual DiffStep*
626  firstResult () override
627  {
628  REQUIRE (not steps_);
629  auto plannedBorgs = theCube_.dispatchBorgs();
630  uint max = plannedBorgs.size();
631  uint cur = 0;
632 
633  _Fmt borgName{"%d of %d ≺%03d.gen%03d≻"};
634 
635 
636  steps_.feed (after(Ref::ATTRIBS)); // important: retain all existing attributes
637  for (uint id : plannedBorgs) // Generate diff to inject a flock of Borg
638  {
639  GenNode borg = MakeRec().genNode(borgName % ++cur % max % id % generatorID_);
640  steps_.feed (ins(borg));
641  steps_.feed (mut(borg)); // open nested scope for this Borg
642  steps_.feed ( ins(GenNode{"borgID", int(id)}));
643  steps_.feed (emu(borg)); // close nested scope
644  }
645  steps_.feed (after(Ref::END)); // important: fast-forward and accept already existing Borgs
646 
647  return & *steps_; // the IterSource protocol requires us to return a ptr to current element
648  }
649 
650 
651  virtual void
652  nextResult (DiffStep*& pos) override
653  {
654  if (!pos) return;
655  if (steps_) ++steps_;
656  if (steps_)
657  pos = & *steps_; // pointer to current element
658  else
659  pos = NULL; // signal iteration end
660  }
661  };
662 
663 
667  SessionThread(function<void(DiffSource*)> notifyGUI)
668  : ThreadJoinable{"BusTerm_test: asynchronous diff mutation"
669  , [=]() {
670  uint cnt = rand() % MAX_RAND_BORGS;
671  for (uint i=0; i<cnt; ++i)
672  {
673  uint delay = rand() % MAX_RAND_DELAY;
674  uint id = rand() % MAX_RAND_NUMBS;
675  usleep (delay);
676  scheduleBorg (id);
677  notifyGUI (new BorgGenerator{*this, i});
678  }
679  }}
680  { }
681  };
682 
683 
684 
686 
687  MockElm rootMock("alpha zero");
688  ID rootID = rootMock.getID();
689 
690  rootMock.attrib["α"] = "Quadrant";
691  CHECK ("Quadrant" == rootMock.attrib["α"]);
692  CHECK (isnil (rootMock.scope));
693 
694  CHECK (0 == generator_instances);
695 
696 
697  // The final part in the puzzle is to dispatch the diff messages into the UI
698  // In the real application, this operation is provided by the NotificationService.
699  // It has access to the UI-Bus, but has to ensure all bus operations are actually
700  // performed on the UI event thread.
701  auto& uiBus = gui::test::Nexus::testUI();
702  CallQueue uiDispatcher;
703  auto notifyGUI = [&](DiffSource* diffGenerator)
704  {
705  uiDispatcher.feed ([&, diffGenerator]()
706  {
707  // apply and consume diff message stored within closure
708  uiBus.change (rootID, MutationMessage{diffGenerator});
709  });
710  };
711 
712 
713 
714  //----start-multithreaded-mutation---
715  SessionThread session{notifyGUI};
716  usleep (2 * MAX_RAND_DELAY);
717  while (not isnil(uiDispatcher))
718  {
719  usleep (100);
720  uiDispatcher.invoke();
721  }
722  session.join();
723  //------end-multithreaded-mutation---
724 
725  // on rare occasions we (consumer thread)
726  // prematurely empty the queue...
727  while (not isnil(uiDispatcher))
728  {
729  uiDispatcher.invoke();
730  }
731 
732 
733  // now verify rootMock has been properly assimilated...
734  uint generatedBorgs = rootMock.scope.size();
735 
736  // root and all Borg child nodes are connected to the UI-Bus
737  CHECK (1 + generatedBorgs == gui::test::Nexus::size());
738 
739  uint64_t borgChecksum = 0;
740  for (uint i=0; i<generatedBorgs; ++i)
741  {
742  MockElm& borg = *rootMock.scope[i];
743  CHECK (contains (borg.attrib, "borgID"));
744  string borgID = borg.attrib["borgID"];
745  borgChecksum += lexical_cast<int> (borgID);
746  string childID = borg.getID().getSym();
747  CHECK (contains (childID, borgID));
748  CHECK (contains (childID, " of ")); // e.g. "3 of 5"
749 
750  CHECK (nexusLog.verifyCall("routeAdd").arg(rootMock.getID(), memLocation(rootMock)) // rootMock was attached to Nexus
751  .beforeCall("change") .argMatch(rootMock.getID(), // diff message sent via UI-Bus
752  "after.+?_ATTRIBS_.+?" // verify diff pattern generated for each Borg
753  "ins.+?"+childID+".+?"
754  "mut.+?"+childID+".+?"
755  "ins.+?borgID.+?"+borgID+".+?"
756  "emu.+?"+childID)
757  .beforeCall("routeAdd").arg(borg.getID(), memLocation(borg)) // Borg was inserted as child and attached to Nexus
758  .beforeEvent("applied diff to "+string(rootMock.getID()))
759  );
760  }
761 
762  CHECK (rootMock.attrib["α"] == "Quadrant"); // attribute alpha was preserved while injecting all those Borg
763 
764 
765  // sanity checks
766  CHECK (borgChecksum == session.borgChecksum_); // no Borgs got lost
767  CHECK (0 == generator_instances); // no generator instance leaks
768 
769 
770 
771  cout << "____Event-Log_________________\n"
772  << util::join(rootMock.getLog(), "\n")
773  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
774 
775  cout << "____Nexus-Log_________________\n"
776  << util::join(nexusLog, "\n")
777  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
778  }
779 
780 
781  static string
783  {
784  return lib::idi::instanceTypeID (&uiElm);
785  }
786  };
787 
788 
790  LAUNCHER (BusTerm_test, "unit gui");
791 
792 
793 }}} // 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:376
Automatically use custom string conversion in C++ stream output.
std::vector< PMockElm > scope
Definition: mock-elm.hpp:365
Constructor for a specially crafted &#39;ref GenNode&#39;.
Definition: gen-node.hpp:732
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
Interface: a component to maintain persistent interface state.
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:579
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:469
ID getID() const
Definition: mock-elm.hpp:370
EventMatch & arg(ARGS const &...args)
refine filter to additionally require specific arguments
Definition: event-log.hpp:474
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:475
EventMatch & beforeCall(string match)
find a match for some function invocation after the current point of reference
Definition: event-log.hpp:418
A Queue which can be pulled by iterating.
Definition: iter-stack.hpp:191
void slotExpand()
Expand this element and remember the expanded state.
Definition: tangible.cpp:156
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:168
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.hpp:915
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:364
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.hpp:932
LAUNCHER(AbstractTangible_test, "unit gui")
Register this test class...
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:754
#define MARK_TEST_FUN
Macro to mark the current test function in STDOUT.
return NULL
Definition: llist.h:596
Simple test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
string getMessage() const
Definition: mock-elm.hpp:394
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:496
static const Ref NO
symbolic ID ref "_NO_"
Definition: gen-node.hpp:753
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](Nexus::setCommandHandler) this hook allows to install a closu...
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:757
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.hpp:860
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:125
bool isExpanded() const
Definition: mock-elm.hpp:382
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:109
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:467
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.
EventMatch & beforeEvent(string match)
find a match for an "event" after the current point of reference
Definition: event-log.hpp:398
void markErr(string error)
push an error state tag to the element
Definition: tangible.cpp:136
bool isError() const
Definition: mock-elm.hpp:388
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:60
generic data element node within a tree
Definition: gen-node.hpp:213
string getError() const
Definition: mock-elm.hpp:400
EventMatch & on(string targetID)
refine filter to additionally match the &#39;this&#39; attribute
Definition: event-log.hpp:541
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