Lumiera  0.pre.03
»edit your freedom«
mock-elm.hpp
Go to the documentation of this file.
1 /*
2  MOCK-ELM.hpp - generic mock UI element for unit testing
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 
50 #ifndef STAGE_TEST_MOCK_ELM_H
51 #define STAGE_TEST_MOCK_ELM_H
52 
53 
54 #include "lib/error.hpp"
55 #include "include/ui-protocol.hpp"
56 #include "lib/test/event-log.hpp"
57 #include "stage/model/tangible.hpp"
58 #include "lib/diff/record.hpp"
59 #include "lib/idi/genfunc.hpp"
60 #include "test/test-nexus.hpp"
62 #include "lib/format-cout.hpp"
63 #include "lib/symbol.hpp"
64 #include "lib/util.hpp"
65 
66 #include <string>
67 #include <memory>
68 #include <vector>
69 #include <map>
70 
71 
72 namespace stage {
73  using lib::test::EventLog;
75 
76 namespace test{
77 
78 
80  using util::isnil;
81  using lib::Symbol;
82  using std::string;
83 
84  class MockElm;
85  using PMockElm = std::unique_ptr<MockElm>;
86 
87 
99  class MockElm
100  : public stage::model::Tangible
101  {
103 
104  EventLog log_{this->identify()};
105 
106  bool virgin_{true};
107  bool expanded_{false};
108 
109  string message_;
110  string error_;
111 
112 
113 
114  /* ==== Tangible interface ==== */
115 
116  virtual bool
117  doReset() override
118  {
119  log_.call(this->identify(), string{MARK_reset});
120  if (virgin_)
121  return false; // there was nothing to reset
122 
123  error_ = "";
124  message_ = "";
125  expanded_ = false;
126  virgin_ = true;
127  log_.event(string{MARK_reset});
128  return true; // we did indeed reset something
129  } // and thus a state mark should be captured
130 
131  virtual bool
132  doExpand (bool yes) override
133  {
134  log_.call(this->identify(), string{MARK_expand}, yes);
135  return Tangible::doExpand (yes);
136  }
137 
138  virtual void
139  doReveal() override
140  {
141  log_.call(this->identify(), string{MARK_reveal});
142  Tangible::doReveal(); // NOTE: without specific configuration this is NOP
143  }
144 
145  virtual bool
146  doMsg (string text) override
147  {
148  log_.call (this->identify(), "doMsg", text);
149  cout << this->identify() << " <-- Message(\""<<text<<"\")" <<endl;
150  message_ = text;
151  virgin_ = false;
152  log_.note ("type=mark", "ID=Message", text);
153 
154  return false; // messages not sticky for this mock implementation
155  }
156 
157  virtual bool
158  doClearMsg () override
159  {
160  log_.call (this->identify(), "doClearMsg");
161  if (isnil (message_))
162  return false;
163 
164  message_ = "";
165  log_.note ("type=mark", "ID=Message", "Message notification cleared");
166  return true;
167  }
168 
169  virtual bool
170  doErr (string text) override
171  {
172  log_.call (this->identify(), "doErr", text);
173  cerr << this->identify() << " <-- Error(\""<<text<<"\")" <<endl;
174  error_ = text;
175  virgin_ = false;
176  log_.note ("type=mark", "ID=Error", text);
177 
178  return true; // error states are sticky for this mock implementation
179  }
180 
181  virtual bool
182  doClearErr () override
183  {
184  log_.call (this->identify(), "doClearErr");
185  if (not isError())
186  return false;
187 
188  error_ = "";
189  log_.note ("type=mark", "ID=Error", "Error state cleared");
190  return true;
191  }
192 
193  virtual void
194  doFlash() override
195  {
196  log_.call (this->identify(), "doFlash");
197  cout << this->identify() << " <-- Flash!" <<endl;
198  log_.note ("type=mark", "ID=Flash");
199  }
200 
201  virtual void
202  doMark (GenNode const& mark) override
203  {
204  log_.call (this->identify(), "doMark", mark);
205  cout << this->identify() << " <-- state-mark = "<< mark <<endl;
206  log_.note ("type=mark", "ID="+mark.idi.getSym(), mark);
207 
208  this->virgin_ = false; // assume state change....
209 
210  // forward to default handler
211  Tangible::doMark (mark);
212  }
213 
214  virtual void
216  {
217  using Attrib = std::pair<const string,string>;
218  using lib::diff::collection;
219  using lib::diff::render;
220 
221  log_.call (this->identify(), "buildMutator");
222  cout << this->identify() << " <-- DIFF" <<endl;
223 
224  buffer.emplace(
226  .attach (collection(scope)
227  .isApplicableIf ([&](GenNode const& spec) -> bool
228  {
229  return spec.data.isNested(); // »Selector« : require object-like sub scope
230  })
231  .matchElement ([&](GenNode const& spec, PMockElm const& elm) -> bool
232  {
233  return spec.idi == elm->getID();
234  })
235  .constructFrom ([&](GenNode const& spec) -> PMockElm
236  {
237  log_.event("diff", "create child \""+spec.idi.getSym()+"\"");
238  PMockElm child = std::make_unique<MockElm>(spec.idi, this->uiBus_);
239  child->joinLog(*this); // create a child element wired via this Element's BusTerm
240  return child;
241  })
242  .buildChildMutator ([&](PMockElm& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
243  {
244  if (target->getID() != subID) return false; //require match on already existing child object
245  target->buildMutator (buff); // delegate to child to build nested TreeMutator
246  log_.event("diff", ">>Scope>> "+subID.getSym());
247  return true;
248  }))
249  .attach (collection(attrib)
250  .isApplicableIf ([&](GenNode const& spec) -> bool
251  {
252  return spec.isNamed() // »Selector« : accept attribute-like values
253  and not spec.data.isNested(); // but no nested objects
254  })
255  .matchElement ([&](GenNode const& spec, Attrib const& elm) -> bool
256  {
257  return elm.first == spec.idi.getSym();
258  })
259  .constructFrom ([&](GenNode const& spec) -> Attrib
260  {
261  string key{spec.idi.getSym()},
262  val{render(spec.data)};
263  log_.event("diff", "++Attrib++ "+key+" = "+val);
264  return {key, val};
265  })
266  .assignElement ([&](Attrib& target, GenNode const& spec) -> bool
267  {
268  string key{spec.idi.getSym()},
269  newVal{render (spec.data)};
270  log_.event("diff", "set Attrib "+key+" <-"+newVal);
271  target.second = newVal;
272  return true;
273  })));
274 
275  log_.event ("diff", getID().getSym() +" accepts mutation...");
276  }
277 
278 
279  protected:
280  string
281  identify() const
282  {
283  return getID().getSym() +"."+ lib::idi::instanceTypeID(this);
284  }
285 
286 
287  public:
288  explicit
289  MockElm(string id)
291  { }
292 
293  explicit
294  MockElm(ID identity, ctrl::BusTerm& nexus =Nexus::testUI())
295  : stage::model::Tangible(identity, nexus)
296  {
297  log_.call (this->identify(), "ctor", identity, string(nexus));
298  log_.create (getID().getSym());
299  installExpander ([&](){ return this->expanded_; }
300  ,[&](bool yes)
301  {
302  virgin_ = false;
303  expanded_ = yes;
304  log_.event (expanded_? "expanded" : "collapsed");
305  });
306  }
307 
308 
311  {
312  try {
313  log_.call (this->identify(), "dtor");
314  log_.destroy (getID().getSym());
315  }
316  catch(...)
317  {
318  const char* errID = lumiera_error();
319  if (errID)
320  cerr << "Error while logging shutdown of Mock-UI-Element: " << errID <<endl;
321  else
322  cerr << "Unknown Error while logging shutdown of Mock-UI-Element." <<endl;
323  }
324  }
325 
326 
327 
328 
329  /* ==== special operations API ==== */
330 
339  void
341  {
342  log_.call (this->identify(), "kill");
343  log_.destroy (getID().getSym());
344 
345  Nexus::zombificate (this->uiBus_);
346  log_.event (string(getID()) + " successfully connected to zombie bus");
347  }
348 
349 
350 
351 
352  /* ==== Attributes and mock children ==== */
353 
354  std::map<string, string> attrib;
355  std::vector<PMockElm> scope;
356 
357 
358  /* ==== Query/Verification API ==== */
359 
360  ID getID() const
361  {
362  return uiBus_.getID();
363  }
364 
365  bool
366  isTouched() const
367  {
368  return not virgin_;
369  }
370 
371  bool
372  isExpanded() const
373  {
374  return expanded_;
375  }
376 
377  bool
378  isError() const
379  {
380  return not isnil(error_);
381  }
382 
383  string
384  getMessage() const
385  {
386  return message_;
387  }
388 
389  string
390  getError() const
391  {
392  return error_;
393  }
394 
395 
396 
397  EventMatch
398  verify (string match) const
399  {
400  return getLog().verify(match);
401  }
402 
403  EventMatch
404  verifyMatch (string regExp) const
405  {
406  return getLog().verifyMatch(regExp);
407  }
408 
409  EventMatch
410  verifyEvent (string match) const
411  {
412  return getLog().verifyEvent(match);
413  }
414 
415  EventMatch
416  verifyEvent (string classifier, string match) const
417  {
418  return getLog().verifyEvent (classifier,match);
419  }
420 
421  EventMatch
422  verifyCall (string match) const
423  {
424  return getLog().verifyCall(match);
425  }
426 
427  EventMatch
428  ensureNot (string match) const
429  {
430  return getLog().ensureNot(match);
431  }
432 
434  EventMatch
435  verifyMark (string id) const
436  {
437  return getLog().verify(id).type("mark").id(id);
438  }
439 
444  EventMatch
445  verifyMark (string id, string payloadMatch) const
446  {
447  return getLog().verifyEvent("mark", payloadMatch).type("mark").id(id);
448  }
449 
450  template<typename X>
451  EventMatch
452  verifyMark (string id, X const& something) const
453  {
454  return getLog().verifyEvent("mark", something).type("mark").id(id);
455  }
456 
457 
458  EventLog const&
459  getLog() const
460  {
461  return log_;
462  }
463 
464  EventLog&
465  joinLog (MockElm& otherMock)
466  {
467  log_.joinInto (otherMock.log_);
468  return log_;
469  }
470 
471  EventLog&
472  joinLog (EventLog& otherLog)
473  {
474  log_.joinInto (otherLog);
475  return log_;
476  }
477  };
478 
479 
480 
481 }} // namespace stage::test
482 #endif /*STAGE_TEST_MOCK_ELM_H*/
EventMatch & type(string typeID)
refine filter to additionally require a matching log entry type
Definition: event-log.cpp:539
EventLog & event(string text)
log some text as event
Definition: event-log.cpp:676
virtual void doReveal() override
generic default implementation of the "reveal" functionality.
Definition: mock-elm.hpp:139
Automatically use custom string conversion in C++ stream output.
Abstraction: a tangible element of the User Interface.
Hard wired key constants and basic definitions for communication with the GUI.
connection point at the UI-Bus.
Definition: bus-term.hpp:96
bool isNested() const
determine if payload constitutes a nested scope ("object")
Definition: gen-node.hpp:768
Support for verifying the occurrence of events from unit tests.
Definition: run.hpp:40
A fake UI backbone for investigations and unit testing.
EventMatch verifyMark(string id) const
special verification match on a "state mark" message to this element
Definition: mock-elm.hpp:435
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:275
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:126
EventLog & create(string text)
Log the creation of an object.
Definition: event-log.cpp:733
void mark(GenNode const &)
generic handler for all incoming "state mark" messages
Definition: tangible.cpp:251
EventMatch verifyEvent(string match) const
start a query to match for some event.
Definition: event-log.cpp:770
Generic functions to build identification schemes.
static Builder< TreeMutator > build()
DSL: start building a custom adapted tree mutator, where the operations are tied by closures or wrapp...
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
Token or Atom with distinct identity.
Definition: symbol.hpp:117
EventMatch verifyCall(string match) const
start a query to match especially a function call
Definition: event-log.cpp:788
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:104
Special collection to represent object-like data.
EventMatch ensureNot(string match) const
start a query to ensure the given expression does not match.
Definition: event-log.cpp:805
Marker types to indicate a literal string and a Symbol.
~MockElm()
document our death in the diagnostic log.
Definition: mock-elm.hpp:310
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
EventLog & call(string target, string function)
Log occurrence of a function call with no arguments.
Definition: event-log.cpp:690
lumiera_err lumiera_error(void)
Get and clear current error state.
Definition: error-state.c:115
EventLog & joinInto(EventLog &otherLog)
Merge this log into another log, forming a combined log.
Definition: event-log.cpp:625
virtual bool doExpand(bool yes) override
generic default implementation of the expand/collapse functionality.
Definition: mock-elm.hpp:132
EventMatch verifyMark(string id, string payloadMatch) const
verification match on a specific "state mark" message
Definition: mock-elm.hpp:445
void kill()
commit suicide.
Definition: mock-elm.hpp:340
EventMatch & id(string classifier)
refine filter to additionally match on the ID attribute
Definition: event-log.cpp:572
virtual void doMark(GenNode const &mark) override
default implementation and catch-all handler for receiving »state mark« messages. ...
Definition: mock-elm.hpp:202
static void zombificate(ctrl::BusTerm &)
kill the given [BusTerm] and implant a dead terminal in place
Definition: test-nexus.cpp:584
void installExpander(Expander::ProbeFun, Expander::ChangeFun)
Configure the (optional) functionality to expand or collapse the UI-Element.
Definition: tangible.hpp:273
Lumiera error handling (C++ interface).
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
Mock UI element or controller.
Definition: mock-elm.hpp:99
EventMatch verify(string match) const
start a query to match for some substring.
Definition: event-log.cpp:752
EventLog & destroy(string text)
Log the destruction of an object.
Definition: event-log.cpp:740
EventMatch verifyMatch(string regExp) const
start a query to match with a regular expression
Definition: event-log.cpp:761
Interface common to all UI elements of relevance for the Lumiera application.
Definition: tangible.hpp:156
auto collection(COLL &coll)
Entry point to a nested DSL for setup and configuration of a collection binding.
virtual void buildMutator(TreeMutator::Handle buffer) override
build a custom implementation of the TreeMutator interface, suitably wired to cause appropriate chang...
Definition: mock-elm.hpp:215
Diagnostic helper for unit tests regarding mutation of custom data.
generic data element node within a tree
Definition: gen-node.hpp:222
Customisable intermediary to abstract mutating operations on arbitrary, hierarchical object-like data...