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