Lumiera  0.pre.03
»edit your freedom«
test-nexus.cpp
Go to the documentation of this file.
1 /*
2  test::Nexus - implementation base for test user interface backbone
3 
4  Copyright (C)
5  2015, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
14 
37 #include "lib/error.hpp"
38 #include "lib/symbol.hpp"
39 #include "lib/itertools.hpp"
40 #include "test/test-nexus.hpp"
41 #include "lib/test/event-log.hpp"
43 #include "stage/ctrl/nexus.hpp"
46 #include "lib/diff/gen-node.hpp"
47 #include "lib/idi/entry-id.hpp"
48 #include "lib/idi/genfunc.hpp"
49 #include "lib/depend.hpp"
50 #include "lib/format-string.hpp"
51 #include "lib/format-cout.hpp"
52 
53 #include <string>
54 #include <deque>
55 
56 using std::string;
57 
58 using lib::Symbol;
59 using lib::append_all;
61 using lib::diff::Rec;
62 using lib::diff::GenNode;
63 using lib::diff::DataCap;
73 using util::_Fmt;
74 
75 namespace stage {
76 namespace test{
77 
78 
79  namespace { // internal details
80 
81  using BusHub = stage::ctrl::Nexus;
82 
92  class TestNexus
93  : public BusHub
94  {
95  EventLog log_{this};
96 
97  using CommandHandler = test::Nexus::CommandHandler;
98  using StateMarkHandler = test::Nexus::StateMarkHandler;
99 
100  CommandHandler commandHandler_;
101  StateMarkHandler stateMarkHandler_;
102 
103 
104 
105  virtual void
106  act (GenNode const& command)
107  {
108  log_.call (this, "act", command);
109  commandHandler_(command);
110  log_.event("TestNexus", _Fmt("bind and trigger command \"%s\"%s")
111  % command.idi.getSym()
112  % command.data.get<Rec>());
113  }
114 
115  virtual void
116  note (ID subject, GenNode const& mark) override
117  {
118  log_.call (this, "note", subject, mark);
119  stateMarkHandler_(subject, mark);
120  log_.event("TestNexus", _Fmt("processed note from %s |%s") % subject % mark);
121  }
122 
123  virtual bool
124  mark (ID subject, GenNode const& mark) override
125  {
126  log_.call(this, "mark", subject, mark);
127  if (BusHub::mark (subject, mark))
128  {
129  log_.event ("TestNexus", _Fmt("delivered mark to %s |%s") % subject % mark);
130  return true;
131  }
132  else
133  {
134  log_.warn (_Fmt("discarding mark to unknown %s |%s") % subject % mark);
135  return false;
136  }
137  }
138 
139  virtual size_t
140  markAll (GenNode const& mark) override
141  {
142  log_.call(this, "markAll", mark);
143  log_.event("Broadcast", _Fmt("Broadcast mark(\"%s\"): %s") % mark.idi.getSym() % mark.data);
144  size_t cnt = BusHub::markAll (mark);
145  log_.event("TestNexus", _Fmt("successfully broadcasted mark to %d terminals") % cnt);
146  return cnt;
147  }
148 
149  virtual bool
150  change (ID subject, MutationMessage&& diff) override
151  {
152  string diffSeqLog = diff.updateDiagnostics(); // snapshot of generated diff sequence
153  log_.call (this, "change", subject, diffSeqLog);
154  if (BusHub::change (subject, move(diff)))
155  {
156  log_.event ("TestNexus", _Fmt("applied diff to %s |%s") % subject % diffSeqLog);
157  return true;
158  }
159  else
160  {
161  log_.warn (_Fmt("disregarding change/diff to unknown %s |%s") % subject % diffSeqLog);
162  return false;
163  }
164  }
165 
166  virtual BusTerm&
167  routeAdd (ID identity, Tangible& newNode) override
168  {
169  log_.call (this, "routeAdd", identity, instanceTypeID(&newNode));
170  BusHub::routeAdd (identity, newNode);
171  log_.event("TestNexus", _Fmt("added route to %s |%s| table-size=%2d")
172  % identity
173  % instanceTypeID(&newNode)
174  % BusHub::size());
175  return *this;
176  }
177 
178  virtual void
179  routeDetach (ID node) noexcept override
180  {
181  log_.call (this, "routeDetach", node);
182  BusHub::routeDetach (node);
183  log_.event("TestNexus", _Fmt("removed route to %s | table-size=%2d") % node % BusHub::size());
184  }
185 
186  virtual operator string() const
187  {
188  return getID().getSym()+"."+instanceTypeID(this);
189  }
190 
191 
192  public:
193  TestNexus()
194  : BusHub(*this, lib::idi::EntryID<TestNexus>("mock-UI"))
195  {
196  installCommandHandler();
197  installStateMarkHandler();
198  }
199 
200  // standard copy operations
201 
202 
203  EventLog&
204  getLog()
205  {
206  return log_;
207  }
208 
209  void
210  installCommandHandler (CommandHandler newHandler =CommandHandler())
211  {
212  if (newHandler)
213  commandHandler_ = newHandler;
214  else
215  commandHandler_ =
216  [=](GenNode const& cmd)
217  {
218  log_.warn(_Fmt("NOT handling command-message %s in test-mode") % cmd);
219  };
220  }
221 
222  void
223  installStateMarkHandler (StateMarkHandler newHandler =StateMarkHandler())
224  {
225  if (newHandler)
226  stateMarkHandler_ = newHandler;
227  else
228  stateMarkHandler_ =
229  [=](ID subject, GenNode const& mark)
230  {
231  log_.warn(_Fmt("NOT handling state-mark %s passed from %s in test-mode")
232  % mark % subject);
233  };
234  }
235  };
236 
240 
241 
242 
243 
251  : public BusTerm
252  {
253 
254  EventLog&
255  log()
256  {
257  return testNexus().getLog();
258  }
259 
260 
261 
262  /* ==== defunct re-implementation of the BusTerm interface ==== */
263 
264  virtual void
265  act (GenNode const& command)
266  {
267  log().call(this, "act", command);
268  log().error ("sent command invocation to ZombieNexus");
269  cerr << "Command " << command << " -> ZombieNexus" <<endl;
270  }
271 
272  virtual void
273  note (ID subject, GenNode const& mark) override
274  {
275  log().call(this, "note", subject, mark);
276  log().error ("sent note message to ZombieNexus");
277  cerr << "note message "<< mark
278  << " FROM:" << subject
279  << " -> ZombieNexus" <<endl;
280  }
281 
282  virtual bool
283  mark (ID subject, GenNode const& mark) override
284  {
285  log().call(this, "mark", subject, mark);
286  log().error ("request to deliver mark message via ZombieNexus");
287  cerr << "mark message -> ZombieNexus" <<endl;
288  return false;
289  }
290 
291  virtual size_t
292  markAll (GenNode const& mark) override
293  {
294  log().call(this, "markAll", mark);
295  log().error ("request to broadcast to all Zombies");
296  cerr << "broadcast message -> ZombieNexus" <<endl;
297  return 0;
298  }
299 
300  virtual bool
301  change (ID subject, MutationMessage&& diff) override
302  {
303  log().call(this, "change", subject, diff);
304  log().error ("request to apply a diff message via ZombieNexus");
305  cerr << "change diff -> ZombieNexus" <<endl;
306  return false;
307  }
308 
309  virtual BusTerm&
310  routeAdd (ID identity, Tangible& newNode) override
311  {
312  log().call(this, "routeAdd", identity, newNode);
313  log().error ("attempt to connect against ZombieNexus");
314  cerr << "connect("<< identity <<" -> ZombieNexus" <<endl;
315  return *this;
316  }
317 
318  virtual void
319  routeDetach (ID node) noexcept override
320  {
321  log().call(this, "routeDetach", node);
322  log().error ("disconnect from ZombieNexus");
323  cerr << "disconnect("<< node <<" -> ZombieNexus" <<endl;
324  }
325 
326  virtual operator string() const
327  {
328  return getID().getSym()+"."+instanceTypeID(this);
329  }
330 
331 
332  public:
337  ZombieNexus(string formerID, BusTerm& homeland)
338  : BusTerm(lib::idi::EntryID<ZombieNexus>("defunct-"+formerID), homeland)
339  { }
340 
341  explicit
342  ZombieNexus()
343  : ZombieNexus{"zombieland", *this}
344  { }
345 
346  ~ZombieNexus()
347  {
348  cerr << this->getID().getSym() << ": Zombies never die" << endl;
349  }
350  };
351 
352 
353 
354  lib::Depend<ZombieNexus> zombieNexus;
355 
356  }//(End) internal details
357 
358 
359 
360 
367  {
368  return testNexus();
369  }
370 
371  lib::test::EventLog const&
372  Nexus::getLog()
373  {
374  return testNexus().getLog();
375  }
376 
377  lib::test::EventLog const&
378  Nexus::startNewLog()
379  {
380  return testNexus().getLog().clear();
381  }
382 
383  size_t
384  Nexus::size()
385  {
386  return testNexus().size();
387  }
388 
389 
407  void
408  Nexus::setCommandHandler (CommandHandler newHandler)
409  {
410  testNexus().installCommandHandler (newHandler);
411  }
412 
418  void
419  Nexus::setStateMarkHandler(StateMarkHandler newHandler)
420  {
421  testNexus().installStateMarkHandler (newHandler);
422  }
423 
424 
425  namespace { // install a diagnostic dummy-command-handler
426 
435  : public HandlingPattern
436  {
437  mutable EventLog log_;
438  Command command_;
439 
440 
441 
442  /* ==== HandlingPattern - Interface ==== */
443 
444  void
445  performExec (CommandImpl& command) const override
446  {
447  log_.call ("MockHandlingPattern", "exec", command);
448  command.invokeCapture();
449  command.invokeOperation();
450  }
451 
452  void
453  performUndo (CommandImpl& command) const override
454  {
455  log_.call ("MockHandlingPattern", "undo", command);
456  command.invokeUndo();
457  }
458 
459  bool
460  isValid() const override
461  {
462  return true;
463  }
464 
465 
466  public:
467  SimulatedCommandHandler (GenNode const& cmdMsg)
468  : log_(Nexus::getLog())
469  , command_(retrieveCommand(cmdMsg))
470  {
471  log_.event("TestNexus", "HANDLING Command-Message for "+string(command_));
472 
473  Rec const& argData{cmdMsg.data.get<Rec>()};
474  log_.call ("TestNexus", "bind-command", enumerate(argData));
475  command_.bindArg (argData);
476 
477  log_.call ("TestNexus", "exec-command", command_);
478  if (command_.exec (*this))
479  log_.event("TestNexus", "SUCCESS handling "+command_.getID());
480  else
481  log_.warn(_Fmt("FAILED to handle command-message %s in test-mode") % cmdMsg);
482  }
483 
484  private:
485  EventLog::ArgSeq
486  enumerate (Rec const& argData)
487  {
488  EventLog::ArgSeq strings;
489  strings.reserve (argData.childSize());
490  append_all (transformIterator (childData (argData.scope())
491  , util::toString<DataCap>)
492  ,strings);
493  return strings;
494  }
495 
496  static Command
497  retrieveCommand (GenNode const& cmdMsg)
498  {
499  Symbol cmdID {cmdMsg.idi.getSym().c_str()};
500  return Command::get (cmdID);
501  }
502  };
503 
504  }//(End)diagnostic dummy-command-handler
505 
506  void
507  Nexus::prepareDiagnosticCommandHandler()
508  {
509  testNexus().installCommandHandler(
510  [](GenNode const& cmdMsg)
511  {
512  SimulatedCommandHandler{cmdMsg};
513  });
514  }
515 
516 
517 
518 
519 
520 
521 
522  namespace { // install a diagnostic dummy-command-handler
523 
524  using ID = lib::idi::BareEntryID;
525 
527  : public StateRecorder
528  {
529 
530  public:
533  { }
534 
535  using StateManager::clearState;
536  };
537 
539 
540  }//(End)diagnostic mock-state-manager
541 
542 
554  {
555  // discard possible leftover
556  // from previous test installations
557  stateManager().clearState();
558 
559  testNexus().installStateMarkHandler(
560  [&](ID const& elementID, lib::diff::GenNode const& stateMark)
561  {
562  stateManager().recordState (elementID, stateMark);
563  });
564 
565  return getMockStateManager();
566  }
567 
569  Nexus::getMockStateManager()
570  {
571  return stateManager();
572  }
573 
574 
575 
576 
577 
578 
583  void
585  {
586  string lateName = doomed.getID().getSym();
587  doomed.~BusTerm();
588  testNexus().getLog().destroy (lateName);
589 
590  static_assert (sizeof(BusTerm) >= sizeof(ZombieNexus), "Zombie overflow");
591  new(&doomed) ZombieNexus{lateName, zombieNexus()};
592  testNexus().getLog().event(lateName + " successfully zombificated.");
593  }
594 
595 }} // namespace stage::test
virtual bool change(ID subject, MutationMessage &&diff) override
direct a mutation message towards the indicated Tangible.
Definition: test-nexus.cpp:150
type erased baseclass for building a combined hash and symbolic ID.
Definition: entry-id.hpp:133
Simple map based implementation of the PresentationStateManager interface.
Generic Message with an embedded diff, to describe changes to model elements.
EventLog & event(string text)
log some text as event
Definition: event-log.cpp:676
Automatically use custom string conversion in C++ stream output.
connection point at the UI-Bus.
Definition: bus-term.hpp:96
EventLog & warn(string text)
Log a warning entry.
Definition: event-log.cpp:710
Support for verifying the occurrence of events from unit tests.
Definition: run.hpp:40
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:408
virtual BusTerm & routeAdd(ID identity, Tangible &newNode) override
Definition: test-nexus.cpp:310
A fake UI backbone for investigations and unit testing.
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:275
Front-end for printf-style string template interpolation.
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:126
virtual void note(ID subject, GenNode const &mark) override
capture and record a "state mark" for later replay for restoring UI state.
Definition: test-nexus.cpp:273
virtual ~BusTerm()
this is an interface
Definition: ui-bus.cpp:83
ZombieNexus(string formerID, BusTerm &homeland)
fabricate a "dead terminal", marked as deceased, viciously connected to itself.
Definition: test-nexus.cpp:337
Opaque message to effect a structural change on a target, which is likewise only known in an abstract...
virtual void routeDetach(ID node) noexcept override
deactivate and remove a down-link route.
Definition: test-nexus.cpp:179
A front-end for using printf-style formatting.
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:280
Implementation namespace for support and library code.
Generic functions to build identification schemes.
virtual bool change(ID subject, MutationMessage &&diff) override
alter and reshape the designated subject by applying the given diff message.
Definition: test-nexus.cpp:301
Token or Atom with distinct identity.
Definition: symbol.hpp:117
lib::Depend< TestNexus > testNexus
singleton instance of the [TestNexus] used for rigging unit tests
Definition: test-nexus.cpp:239
static ctrl::StateManager & useMockStateManager()
install a standard handler for state mark messages, which is actually backed by a mock implementation...
Definition: test-nexus.cpp:553
virtual size_t markAll(GenNode const &mark) override
broadcast a notification to all connected terminal nodes.
Definition: test-nexus.cpp:140
Marker types to indicate a literal string and a Symbol.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
EventLog & call(string target, string function)
Log occurrence of a function call with no arguments.
Definition: event-log.cpp:690
Implementation of the PresentationStateManager interface through associative (key-value) store...
virtual void act(GenNode const &command)
prepare or trigger invocation of a command.
Definition: test-nexus.cpp:265
Generic building block for tree shaped (meta)data structures.
Steam-Layer command frontend.
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:419
Interface: handling of persistent interface state.
Central hub of the UI-Bus.
Definition: nexus.hpp:67
Singleton services and Dependency Injection.
static void zombificate(ctrl::BusTerm &)
kill the given [BusTerm] and implant a dead terminal in place
Definition: test-nexus.cpp:584
virtual bool mark(ID subject, GenNode const &mark) override
route mark messages down to the individual Tangible.
Definition: test-nexus.cpp:124
virtual void act(GenNode const &command)
prepare or trigger invocation of a command.
Definition: test-nexus.cpp:106
Lumiera error handling (C++ interface).
virtual void note(ID subject, GenNode const &mark) override
capture and record a "state mark" for later replay for restoring UI state.
Definition: test-nexus.cpp:116
string instanceTypeID(const TY *const obj)
designation of an distinct object instance
Definition: genfunc.hpp:116
static ctrl::BusTerm & testUI()
get a connection point to a UI backbone faked for test
Definition: test-nexus.cpp:366
virtual void routeDetach(ID node) noexcept override
Definition: test-nexus.cpp:319
Handle object representing a single Command instance to be used by client code.
Definition: command.hpp:115
Bare symbolic and hash ID used for accounting of asset like entries.
Steam-Layer Command implementation.
Helpers for working with iterators based on the pipeline model.
Interface common to all UI elements of relevance for the Lumiera application.
Definition: tangible.hpp:156
auto transformIterator(IT const &src, FUN processingFunc)
Build a TransformIter: convenience free function shortcut, picking up the involved types automaticall...
Definition: itertools.hpp:788
object-like record of data.
Definition: record.hpp:141
virtual bool mark(ID subject, GenNode const &mark) override
route a state update or notification to the given subject.
Definition: test-nexus.cpp:283
virtual BusTerm & routeAdd(ID identity, Tangible &newNode) override
add a new down-link connection to the routing table
Definition: test-nexus.cpp:167
Interface: Operation Skeleton how to invoke or undo a command.
Core hub and routing table of the UI-Bus.
generic data element node within a tree
Definition: gen-node.hpp:222
virtual size_t markAll(GenNode const &mark) override
broadcast a notification message to all currently connected bus terminals.
Definition: test-nexus.cpp:292
EventLog & error(string text)
Log an error note.
Definition: event-log.cpp:717