Lumiera  0.pre.03
»edit your freedom«
test-mutation-target.hpp
Go to the documentation of this file.
1 /*
2  TEST-MUTATION-TARGET.hpp - diagnostic helper for TreeMutator bindings
3 
4  Copyright (C)
5  2016, 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 
35 #ifndef LIB_DIFF_TEST_MUTATION_TARGET_H
36 #define LIB_DIFF_TEST_MUTATION_TARGET_H
37 
38 
39 #include "lib/error.hpp"
40 #include "lib/symbol.hpp"
41 #include "lib/diff/record.hpp"
43 #include "lib/idi/genfunc.hpp"
44 #include "lib/format-string.hpp"
45 #include "lib/format-util.hpp"
46 #include "lib/test/event-log.hpp"
47 #include "lib/nocopy.hpp"
48 #include "lib/util.hpp"
49 
50 #include <utility>
51 #include <string>
52 #include <vector>
53 
54 
55 namespace lib {
56 namespace diff{
57 
58  namespace error = lumiera::error;
59 
60  using lib::Literal;
61  using lib::test::EventLog;
63  using iter_stl::eachElm;
64  using util::unConst;
65  using util::isnil;
66  using util::join;
67  using util::_Fmt;
68 
69  using std::string;
70  using std::forward;
71  using std::move;
72 
73 
74  namespace { // diagnostic helpers: render diff spec....
75 
76  template<typename T>
77  inline string
78  identify (const T* const entity)
79  {
80  return lib::idi::instanceTypeID(entity);
81  }
82 
83 
84  string render (DataCap const&);
85 
86 
87  inline string
88  renderAttribute (GenNode const& elm)
89  {
90  return elm.idi.getSym()
91  + " = "
92  + render (elm.data);
93  }
94 
95  inline string
96  renderChild (GenNode const& elm)
97  {
98  return render (elm.data);
99  }
100 
101  inline string
102  renderNode (GenNode const& n)
103  {
104  return n.isNamed()? renderAttribute(n)
105  : renderChild(n);
106  }
107 
108 
109  inline string
110  renderRecord (Rec const& record)
111  {
112  using util::join;
114 
115  return "Rec("
116  + (Rec::TYPE_NIL==record.getType()? "" : record.getType())
117  + (isnil(record.attribs())? "" : "| "+join (transformIterator (record.attribs(), renderAttribute))+" ")
118  + (isnil(record.scope())? "" : "|{"+join (transformIterator (record.scope(), renderChild))+"}")
119  + ")"
120  ;
121  }
122 
123 
124  inline string
125  render (DataCap const& content)
126  {
127  struct StringRenderer
128  : public Variant<DataValues>::Visitor
129  {
130  string representation{""};
131 
132 #define STRINGIFY_CONTENT(_TY_) \
133  virtual void handle (_TY_& val) override { representation = util::toString (val); }
134 
135  STRINGIFY_CONTENT (int)
136  STRINGIFY_CONTENT (int64_t)
137  STRINGIFY_CONTENT (short)
138  STRINGIFY_CONTENT (char)
139  STRINGIFY_CONTENT (bool)
140  STRINGIFY_CONTENT (double)
141  STRINGIFY_CONTENT (string)
142  STRINGIFY_CONTENT (time::Time)
143  STRINGIFY_CONTENT (time::Offset)
144  STRINGIFY_CONTENT (time::Duration)
145  STRINGIFY_CONTENT (time::TimeSpan)
146  STRINGIFY_CONTENT (hash::LuidH)
147 
148 
150  virtual void
151  handle (Rec & rec) override
152  {
153  representation = renderRecord (rec);
154  }
155 
156  virtual void
157  handle (RecRef & ref) override
158  {
159  if (ref)
160  representation = renderRecord (ref);
161  else
162  representation = util::BOTTOM_INDICATOR;
163  }
164  };
165 
166  StringRenderer visitor;
167  unConst(content).accept (visitor);
168  return visitor.representation;
169  }
170 
171  }//(End)diagnostic helpers
172 
173 
174 
175 
176 
188  {
189  using VecG = std::vector<GenNode>;
190 
191  EventLog log_{identify (this)};
192 
193  VecG content_{};
194  VecG prev_content_{};
195 
196 
197  public:
198  using iterator = typename iter_stl::_SeqT<VecG>::Range;
199  using const_iterator = typename iter_stl::_SeqT<const VecG>::Range;
200 
201  const_iterator begin() const { return eachElm(content_); }
202  const_iterator end() const { return const_iterator(); }
203 
204  iterator srcIter() { return eachElm(prev_content_); }
205  iterator lastElm() { return iterator(VecG::iterator(&content_.back()), content_.end());}
206 
207 
208  friend const_iterator begin (TestMutationTarget const& target) { return target.begin(); }
209  friend const_iterator end (TestMutationTarget const& target) { return target.end(); }
210 
211 
212 
213  /* === Operation / Mutation API === */
214 
215  iterator
216  initMutation (string mutatorID)
217  {
218  prev_content_.clear();
219  swap (content_, prev_content_);
220  log_.event ("attachMutator "+mutatorID);
221  return srcIter();
222  }
223 
224  void
225  inject (GenNode&& elm, string operationID)
226  {
227  content_.emplace_back (forward<GenNode>(elm));
228  log_.event (operationID, renderNode (content_.back()));
229  }
230 
231  static iterator
232  search (GenNode::ID const& targetID, iterator pos)
233  {
234  while (pos and not pos->matches(targetID))
235  ++pos;
236  return pos;
237  }
238 
239  iterator
240  locate (GenNode::ID const& targetID)
241  {
242  if (!empty() and content_.back().matches(targetID))
243  return lastElm();
244  else
245  return search (targetID, eachElm(content_));
246  }
247 
248  void
249  logSkip (GenNode const& content)
250  {
251  log_.event ("skipSrc", isnil(content.idi.getSym())? util::BOTTOM_INDICATOR : renderNode(content));
252  }
253 
254  void
255  logAssignment (GenNode const& target, string oldPayload)
256  {
257  log_.event ("assignElm", _Fmt{"%s: %s ⤅ %s"}
258  % target.idi.getSym()
259  % oldPayload
260  % render(target.data));
261  }
262 
263  void
264  logMutation (GenNode const& target)
265  {
266  log_.event ("mutateChild", _Fmt{"%s: start mutation...%s"}
267  % target.idi.getSym()
268  % render(target.data));
269  }
270 
271  void
272  logScopeCompletion (iterator processingPos)
273  {
274  log_.event ("completeScope", _Fmt{"⤴ scope%s completed / %d waste elm(s)"}
275  % (processingPos? " NOT":"")
276  % prev_content_.size());
277  }
278 
279 
280  /* === Diagnostic / Verification === */
281 
282  bool
283  empty() const
284  {
285  return content_.empty();
286  }
287 
289  string
290  showContent () const
291  {
292  return join (transformIterator (begin(), renderNode));
293  }
294 
296  string
297  showSrcBuffer () const
298  {
299  return join (transformIterator (eachElm(prev_content_), renderNode));
300  }
301 
302  EventMatch
303  verify (string match) const
304  {
305  return getLog().verify(match);
306  }
307 
308  EventMatch
309  verifyMatch (string regExp) const
310  {
311  return getLog().verifyMatch(regExp);
312  }
313 
314  EventMatch
315  verifyEvent (string match) const
316  {
317  return getLog().verifyEvent(match);
318  }
319 
320  EventMatch
321  verifyEvent (string classifier, string match) const
322  {
323  return getLog().verifyEvent (classifier,match);
324  }
325 
326  EventMatch
327  verifyCall (string match) const
328  {
329  return getLog().verifyCall(match);
330  }
331 
332  EventMatch
333  ensureNot (string match) const
334  {
335  return getLog().ensureNot(match);
336  }
337 
338  EventLog const&
339  getLog() const
340  {
341  return log_;
342  }
343  };
344 
345 
346 
347  namespace { // supply a suitable decorator for the TreeMutator
348 
349  template<class PAR>
351  : public PAR
352  {
353  using Target = TestMutationTarget;
354  using Iter = TestMutationTarget::iterator;
355 
356  Target& target_;
357  Iter pos_;
358 
359  public:
360  TestWireTap(Target& dummy, PAR&& chain)
361  : PAR{forward<PAR> (chain)}
362  , target_(dummy)
363  , pos_()
364  { }
365 
366 
367 
368  /* ==== re-Implementation of the operation API ==== */
369 
370  virtual void
371  init() override
372  {
373  pos_ = target_.initMutation (identify(this));
374  PAR::init();
375  }
376 
384  virtual bool
385  injectNew (GenNode const& n) override
386  {
387  target_.inject (GenNode{n}, "injectNew");
388  return PAR::injectNew (n);
389  }
390 
391  virtual bool
392  hasSrc () override
393  {
394  return pos_
395  or PAR::hasSrc();
396  }
397 
400  virtual bool
401  matchSrc (GenNode const& n) override
402  {
403  return PAR::matchSrc(n)
404  or(pos_ and n.matches(*pos_));
405  }
406 
408  virtual void
409  skipSrc (GenNode const& n) override
410  {
411  if (pos_)
412  {
413  GenNode const& skippedElm = *pos_;
414  ++pos_;
415  target_.logSkip (skippedElm);
416  }
417  PAR::skipSrc(n);
418  }
419 
421  virtual bool
422  acceptSrc (GenNode const& n) override
423  {
424  bool isSrcMatch = pos_ and n.matches(*pos_);
425  if (isSrcMatch) //NOTE: important to invoke our own match here
426  {
427  target_.inject (move(*pos_), "acceptSrc");
428  ++pos_;
429  }
430  return PAR::acceptSrc(n)
431  or isSrcMatch;
432  }
433 
435  virtual bool
436  findSrc (GenNode const& ref) override
437  {
438  Iter found = TestMutationTarget::search (ref.idi, pos_);
439  if (found)
440  {
441  target_.inject (move(*found), "findSrc");
442  }
443  return PAR::findSrc(ref)
444  or found;
445  }
446 
448  virtual bool
449  accept_until (GenNode const& spec)
450  {
451  bool foundTarget = true;
452 
453  if (spec.matches (Ref::END))
454  for ( ; pos_; ++pos_)
455  target_.inject (move(*pos_), "accept_until END");
456  else
457  if (spec.matches (Ref::ATTRIBS))
458  for ( ; pos_ and pos_->isNamed(); ++pos_)
459  target_.inject (move(*pos_), "accept_until after ATTRIBS");
460  else
461  {
462  string logMsg{"accept_until "+spec.idi.getSym()};
463  while (pos_ and not TestWireTap::matchSrc(spec))
464  {
465  target_.inject (move(*pos_), logMsg);
466  ++pos_;
467  }
468  if (TestWireTap::matchSrc(spec))
469  {
470  target_.inject (move(*pos_), logMsg);
471  ++pos_;
472  }
473  else
474  foundTarget = false;
475  }
476  return PAR::accept_until(spec)
477  or foundTarget;
478  }
479 
482  virtual bool
483  assignElm (GenNode const& spec)
484  {
485  Iter targetElm = target_.locate (spec.idi);
486  if (targetElm)
487  {
488  string logOldPayload{render(targetElm->data)};
489  *targetElm = spec;
490  target_.logAssignment (*targetElm, logOldPayload);
491  }
492  return PAR::assignElm(spec)
493  or targetElm;
494  }
495 
498  virtual bool
499  mutateChild (GenNode const& spec, TreeMutator::Handle targetBuff)
500  {
501  if (PAR::mutateChild (spec, targetBuff))
502  return true;
503  else // Test mode only --
504  { // no other layer was able to provide a mutator
505  Iter targetElm = target_.locate (spec.idi);
506  if (targetElm)
507  {
508  targetBuff.emplace (TreeMutator::build());
509  target_.logMutation (*targetElm);
510  return true;
511  }
512  return false;
513  }
514  }
515 
518  virtual bool
520  {
521  target_.logScopeCompletion (pos_);
522  return PAR::completeScope()
523  and isnil(this->pos_);
524  }
525  };
526 
527 
528  template<class PAR>
529  auto
531  {
532  return chainedBuilder<TestWireTap<PAR>> (dummy);
533  }
534 
535  }
536 
537 
538 
539 }} // namespace lib::diff
540 #endif /*LIB_DIFF_TREE_MUTATOR_H*/
virtual bool completeScope()
verify all our pending (old) source elements where mentioned.
string showContent() const
render payload content for diagnostics
virtual bool assignElm(GenNode const &spec)
locate element already accepted into the target sequence and assign the designated payload value to i...
Builder-DSL to create and configure a concrete TreeMutator.
Support for verifying the occurrence of events from unit tests.
Test adapter to watch and verify how the TreeMutator binds to custom tree data structures.
Any copy and copy construction prohibited.
Definition: nocopy.hpp:37
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
Typesafe union record.
Definition: variant.hpp:215
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:275
Front-end for printf-style string template interpolation.
A front-end for using printf-style formatting.
virtual bool accept_until(GenNode const &spec)
repeatedly accept, until after the designated location
Implementation namespace for support and library code.
Generic functions to build identification schemes.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:299
virtual bool matchSrc(GenNode const &n) override
ensure the next recorded source element matches on a formal level with given spec ...
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
string showSrcBuffer() const
render elements waiting in source buffer to be accepted
Mix-Ins to allow or prohibit various degrees of copying and cloning.
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.
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:862
Marker types to indicate a literal string and a Symbol.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
wrapped record reference.
Definition: record.hpp:612
virtual void skipSrc(GenNode const &n) override
skip next recorded src element without touching it
Lumiera error handling (C++ interface).
string instanceTypeID(const TY *const obj)
designation of an distinct object instance
Definition: genfunc.hpp:116
Hash implementation based on a lumiera unique object id (LUID) When invoking the default ctor...
bool matches(GenNode const &o) const
Definition: gen-node.hpp:351
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:865
Offset measures a distance in time.
Definition: timevalue.hpp:358
Customisable intermediary to abstract generic tree mutation operations.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
virtual bool mutateChild(GenNode const &spec, TreeMutator::Handle targetBuff)
locate the designated target element and build a suitable sub-mutator for this element into the provi...
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:468
virtual bool findSrc(GenNode const &ref) override
locate designated element and accept it at current position
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:573
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 injectNew(GenNode const &n) override
record in the test target that a new child element is being inserted at current position ...
generic data element node within a tree
Definition: gen-node.hpp:222
virtual bool acceptSrc(GenNode const &n) override
accept existing element, when matching the given spec