Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
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"
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
56using std::string;
57
58using lib::Symbol;
59using lib::append_all;
61using lib::diff::Rec;
73using util::_Fmt;
74
75namespace stage {
76namespace test{
77
78
79 namespace { // internal details
80
82
93 : public BusHub
94 {
95 EventLog log_{this};
96
99
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:
194 : BusHub(*this, lib::idi::EntryID<TestNexus>("mock-UI"))
195 {
196 installCommandHandler();
197 installStateMarkHandler();
198 }
199
200 // standard copy operations
201
202
203 EventLog&
205 {
206 return log_;
207 }
208
209 void
211 {
212 if (newHandler)
213 commandHandler_ = newHandler;
214 else
215 commandHandler_ =
216 [this](GenNode const& cmd)
217 {
218 log_.warn(_Fmt("NOT handling command-message %s in test-mode") % cmd);
219 };
220 }
221
222 void
224 {
225 if (newHandler)
226 stateMarkHandler_ = newHandler;
227 else
228 stateMarkHandler_ =
229 [this](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&
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
343 : ZombieNexus{"zombieland", *this}
344 { }
345
347 {
348 cerr << this->getID().getSym() << ": Zombies never die" << endl;
349 }
350 };
351
352
353
355
356 }//(End) internal details
357
358
359
360
367 {
368 return testNexus();
369 }
370
373 {
374 return testNexus().getLog();
375 }
376
379 {
380 return testNexus().getLog().clear();
381 }
382
383 size_t
385 {
386 return testNexus().size();
387 }
388
389
407 void
409 {
410 testNexus().installCommandHandler (newHandler);
411 }
412
418 void
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_;
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:
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:
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
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
525
527 : public StateRecorder
528 {
529
530 public:
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
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
Access point to singletons and other kinds of dependencies designated by type.
Definition depend.hpp:281
Token or Atom with distinct identity.
Definition symbol.hpp:120
object-like record of data.
Definition record.hpp:142
type erased baseclass for building a combined hash and symbolic ID.
Definition entry-id.hpp:134
string const & getSym() const
Definition entry-id.hpp:169
Helper to log and verify the occurrence of events.
EventLog & warn(string text)
Log a warning entry.
lib::diff::RecordSetup< string >::Storage ArgSeq
EventLog & event(string text)
log some text as event
EventLog & error(string text)
Log an error note.
EventLog & call(string target, string function)
Log occurrence of a function call with no arguments.
connection point at the UI-Bus.
Definition bus-term.hpp:98
virtual ~BusTerm()
this is an interface
Definition ui-bus.cpp:66
EntryID const & ID
Definition bus-term.hpp:107
Central hub of the UI-Bus.
Definition nexus.hpp:70
Interface: handling of persistent interface state.
Simple map based implementation of the PresentationStateManager interface.
Interface common to all UI elements of relevance for the Lumiera application.
Definition tangible.hpp:160
Mock UI backbone for unit testing.
static size_t size()
static ctrl::BusTerm & testUI()
get a connection point to a UI backbone faked for test
static void prepareDiagnosticCommandHandler()
std::function< void(lib::idi::BareEntryID const &, lib::diff::GenNode const &)> StateMarkHandler
static void zombificate(ctrl::BusTerm &)
kill the given [BusTerm] and implant a dead terminal in place
static ctrl::StateManager & getMockStateManager()
std::function< void(lib::diff::GenNode const &)> CommandHandler
static void setStateMarkHandler(StateMarkHandler=StateMarkHandler())
similar to the custom command handler this hook allows to install a closure to intercept any "state m...
static void setCommandHandler(CommandHandler=CommandHandler())
install a closure (custom handler function) to deal with any command invocations encountered in the t...
static lib::test::EventLog const & getLog()
static ctrl::StateManager & useMockStateManager()
install a standard handler for state mark messages, which is actually backed by a mock implementation...
static lib::test::EventLog const & startNewLog()
virtual bool change(ID subject, MutationMessage &&diff) override
direct a mutation message towards the indicated Tangible.
void installCommandHandler(CommandHandler newHandler=CommandHandler())
void installStateMarkHandler(StateMarkHandler newHandler=StateMarkHandler())
virtual void act(GenNode const &command)
prepare or trigger invocation of a command.
virtual size_t markAll(GenNode const &mark) override
broadcast a notification to all connected terminal nodes.
virtual void note(ID subject, GenNode const &mark) override
capture and record a "state mark" for later replay for restoring UI state.
virtual BusTerm & routeAdd(ID identity, Tangible &newNode) override
add a new down-link connection to the routing table
virtual void routeDetach(ID node) noexcept override
deactivate and remove a down-link route.
virtual bool mark(ID subject, GenNode const &mark) override
route mark messages down to the individual Tangible.
ZombieNexus(string formerID, BusTerm &homeland)
fabricate a "dead terminal", marked as deceased, viciously connected to itself.
virtual bool change(ID subject, MutationMessage &&diff) override
alter and reshape the designated subject by applying the given diff message.
virtual void act(GenNode const &command)
prepare or trigger invocation of a command.
virtual size_t markAll(GenNode const &mark) override
broadcast a notification message to all currently connected bus terminals.
virtual void note(ID subject, GenNode const &mark) override
capture and record a "state mark" for later replay for restoring UI state.
virtual BusTerm & routeAdd(ID identity, Tangible &newNode) override
virtual void routeDetach(ID node) noexcept override
virtual bool mark(ID subject, GenNode const &mark) override
route a state update or notification to the given subject.
Steam-Layer Command implementation.
Handle object representing a single Command instance to be used by client code.
Definition command.hpp:120
Command & bindArg(std::tuple< TYPES... > const &)
Definition command.hpp:243
Symbol getID() const noexcept
Definition command.cpp:366
Interface: Operation Skeleton how to invoke or undo a command.
A front-end for using printf-style formatting.
Steam-Layer command frontend.
Singleton services and Dependency Injection.
Bare symbolic and hash ID used for accounting of asset like entries.
Lumiera error handling (C++ interface).
Support for verifying the occurrence of events from unit tests.
Automatically use custom string conversion in C++ stream output.
Front-end for printf-style string template interpolation.
Generic building block for tree shaped (meta)data structures.
Generic functions to build identification schemes.
Helpers for working with iterators based on the pipeline model.
Generic Message with an embedded diff, to describe changes to model elements.
string instanceTypeID(const TY *const obj)
designation of an distinct object instance
Definition genfunc.hpp:116
Implementation namespace for support and library code.
void append_all(IT iter, CON &container)
auto transformIterator(IT const &src, FUN processingFunc)
Build a TransformIter: convenience free function shortcut, picking up the involved types automaticall...
lib::Depend< SimulatedStateManager > stateManager
lib::Depend< TestNexus > testNexus
singleton instance of the [TestNexus] used for rigging unit tests
Lumiera GTK UI implementation root.
Definition guifacade.cpp:37
lib::idi::BareEntryID const & ID
Test runner and basic definitions for tests.
Core hub and routing table of the UI-Bus.
Implementation of the PresentationStateManager interface through associative (key-value) store.
generic data element node within a tree
Definition gen-node.hpp:224
Opaque message to effect a structural change on a target, which is likewise only known in an abstract...
Marker types to indicate a literal string and a Symbol.
A fake UI backbone for investigations and unit testing.