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) Lumiera.org
5  2016, 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 
44 #ifndef LIB_DIFF_TEST_MUTATION_TARGET_H
45 #define LIB_DIFF_TEST_MUTATION_TARGET_H
46 
47 
48 #include "lib/error.hpp"
49 #include "lib/symbol.hpp"
50 #include "lib/diff/record.hpp"
52 #include "lib/idi/genfunc.hpp"
53 #include "lib/format-string.hpp"
54 #include "lib/format-util.hpp"
55 #include "lib/test/event-log.hpp"
56 #include "lib/nocopy.hpp"
57 #include "lib/util.hpp"
58 
59 #include <utility>
60 #include <string>
61 #include <vector>
62 
63 
64 namespace lib {
65 namespace diff{
66 
67  namespace error = lumiera::error;
68 
69  using lib::Literal;
70  using lib::test::EventLog;
72  using iter_stl::eachElm;
73  using util::unConst;
74  using util::isnil;
75  using util::join;
76  using util::_Fmt;
77 
78  using std::string;
79  using std::forward;
80  using std::move;
81 
82 
83  namespace { // diagnostic helpers: render diff spec....
84 
85  template<typename T>
86  inline string
87  identify (const T* const entity)
88  {
89  return lib::idi::instanceTypeID(entity);
90  }
91 
92 
93  string render (DataCap const&);
94 
95 
96  inline string
97  renderAttribute (GenNode const& elm)
98  {
99  return elm.idi.getSym()
100  + " = "
101  + render (elm.data);
102  }
103 
104  inline string
105  renderChild (GenNode const& elm)
106  {
107  return render (elm.data);
108  }
109 
110  inline string
111  renderNode (GenNode const& n)
112  {
113  return n.isNamed()? renderAttribute(n)
114  : renderChild(n);
115  }
116 
117 
118  inline string
119  renderRecord (Rec const& record)
120  {
121  using util::join;
123 
124  return "Rec("
125  + (Rec::TYPE_NIL==record.getType()? "" : record.getType())
126  + (isnil(record.attribs())? "" : "| "+join (transformIterator (record.attribs(), renderAttribute))+" ")
127  + (isnil(record.scope())? "" : "|{"+join (transformIterator (record.scope(), renderChild))+"}")
128  + ")"
129  ;
130  }
131 
132 
133  inline string
134  render (DataCap const& content)
135  {
136  struct StringRenderer
137  : public Variant<DataValues>::Visitor
138  {
139  string representation{""};
140 
141 #define STRINGIFY_CONTENT(_TY_) \
142  virtual void handle (_TY_& val) override { representation = util::toString (val); }
143 
144  STRINGIFY_CONTENT (int)
145  STRINGIFY_CONTENT (int64_t)
146  STRINGIFY_CONTENT (short)
147  STRINGIFY_CONTENT (char)
148  STRINGIFY_CONTENT (bool)
149  STRINGIFY_CONTENT (double)
150  STRINGIFY_CONTENT (string)
151  STRINGIFY_CONTENT (time::Time)
152  STRINGIFY_CONTENT (time::Offset)
153  STRINGIFY_CONTENT (time::Duration)
154  STRINGIFY_CONTENT (time::TimeSpan)
155  STRINGIFY_CONTENT (hash::LuidH)
156 
157 
159  virtual void
160  handle (Rec & rec) override
161  {
162  representation = renderRecord (rec);
163  }
164 
165  virtual void
166  handle (RecRef & ref) override
167  {
168  if (ref)
169  representation = renderRecord (ref);
170  else
171  representation = util::BOTTOM_INDICATOR;
172  }
173  };
174 
175  StringRenderer visitor;
176  unConst(content).accept (visitor);
177  return visitor.representation;
178  }
179 
180  }//(End)diagnostic helpers
181 
182 
183 
184 
185 
197  {
198  using VecG = std::vector<GenNode>;
199 
200  EventLog log_{identify (this)};
201 
202  VecG content_{};
203  VecG prev_content_{};
204 
205 
206  public:
207  using iterator = typename iter_stl::_SeqT<VecG>::Range;
208  using const_iterator = typename iter_stl::_SeqT<const VecG>::Range;
209 
210  const_iterator begin() const { return eachElm(content_); }
211  const_iterator end() const { return const_iterator(); }
212 
213  iterator srcIter() { return eachElm(prev_content_); }
214  iterator lastElm() { return iterator(VecG::iterator(&content_.back()), content_.end());}
215 
216 
217  friend const_iterator begin (TestMutationTarget const& target) { return target.begin(); }
218  friend const_iterator end (TestMutationTarget const& target) { return target.end(); }
219 
220 
221 
222  /* === Operation / Mutation API === */
223 
224  iterator
225  initMutation (string mutatorID)
226  {
227  prev_content_.clear();
228  swap (content_, prev_content_);
229  log_.event ("attachMutator "+mutatorID);
230  return srcIter();
231  }
232 
233  void
234  inject (GenNode&& elm, string operationID)
235  {
236  content_.emplace_back (forward<GenNode>(elm));
237  log_.event (operationID, renderNode (content_.back()));
238  }
239 
240  static iterator
241  search (GenNode::ID const& targetID, iterator pos)
242  {
243  while (pos and not pos->matches(targetID))
244  ++pos;
245  return pos;
246  }
247 
248  iterator
249  locate (GenNode::ID const& targetID)
250  {
251  if (!empty() and content_.back().matches(targetID))
252  return lastElm();
253  else
254  return search (targetID, eachElm(content_));
255  }
256 
257  void
258  logSkip (GenNode const& content)
259  {
260  log_.event ("skipSrc", isnil(content.idi.getSym())? util::BOTTOM_INDICATOR : renderNode(content));
261  }
262 
263  void
264  logAssignment (GenNode const& target, string oldPayload)
265  {
266  log_.event ("assignElm", _Fmt{"%s: %s ⤅ %s"}
267  % target.idi.getSym()
268  % oldPayload
269  % render(target.data));
270  }
271 
272  void
273  logMutation (GenNode const& target)
274  {
275  log_.event ("mutateChild", _Fmt{"%s: start mutation...%s"}
276  % target.idi.getSym()
277  % render(target.data));
278  }
279 
280  void
281  logScopeCompletion (iterator processingPos)
282  {
283  log_.event ("completeScope", _Fmt{"⤴ scope%s completed / %d waste elm(s)"}
284  % (processingPos? " NOT":"")
285  % prev_content_.size());
286  }
287 
288 
289  /* === Diagnostic / Verification === */
290 
291  bool
292  empty() const
293  {
294  return content_.empty();
295  }
296 
298  string
299  showContent () const
300  {
301  return join (transformIterator (begin(), renderNode));
302  }
303 
305  string
306  showSrcBuffer () const
307  {
308  return join (transformIterator (eachElm(prev_content_), renderNode));
309  }
310 
311  EventMatch
312  verify (string match) const
313  {
314  return getLog().verify(match);
315  }
316 
317  EventMatch
318  verifyMatch (string regExp) const
319  {
320  return getLog().verifyMatch(regExp);
321  }
322 
323  EventMatch
324  verifyEvent (string match) const
325  {
326  return getLog().verifyEvent(match);
327  }
328 
329  EventMatch
330  verifyEvent (string classifier, string match) const
331  {
332  return getLog().verifyEvent (classifier,match);
333  }
334 
335  EventMatch
336  verifyCall (string match) const
337  {
338  return getLog().verifyCall(match);
339  }
340 
341  EventMatch
342  ensureNot (string match) const
343  {
344  return getLog().ensureNot(match);
345  }
346 
347  EventLog const&
348  getLog() const
349  {
350  return log_;
351  }
352  };
353 
354 
355 
356  namespace { // supply a suitable decorator for the TreeMutator
357 
358  template<class PAR>
360  : public PAR
361  {
362  using Target = TestMutationTarget;
363  using Iter = TestMutationTarget::iterator;
364 
365  Target& target_;
366  Iter pos_;
367 
368  public:
369  TestWireTap(Target& dummy, PAR&& chain)
370  : PAR{forward<PAR> (chain)}
371  , target_(dummy)
372  , pos_()
373  { }
374 
375 
376 
377  /* ==== re-Implementation of the operation API ==== */
378 
379  virtual void
380  init() override
381  {
382  pos_ = target_.initMutation (identify(this));
383  PAR::init();
384  }
385 
393  virtual bool
394  injectNew (GenNode const& n) override
395  {
396  target_.inject (GenNode{n}, "injectNew");
397  return PAR::injectNew (n);
398  }
399 
400  virtual bool
401  hasSrc () override
402  {
403  return pos_
404  or PAR::hasSrc();
405  }
406 
409  virtual bool
410  matchSrc (GenNode const& n) override
411  {
412  return PAR::matchSrc(n)
413  or(pos_ and n.matches(*pos_));
414  }
415 
417  virtual void
418  skipSrc (GenNode const& n) override
419  {
420  if (pos_)
421  {
422  GenNode const& skippedElm = *pos_;
423  ++pos_;
424  target_.logSkip (skippedElm);
425  }
426  PAR::skipSrc(n);
427  }
428 
430  virtual bool
431  acceptSrc (GenNode const& n) override
432  {
433  bool isSrcMatch = pos_ and n.matches(*pos_);
434  if (isSrcMatch) //NOTE: important to invoke our own match here
435  {
436  target_.inject (move(*pos_), "acceptSrc");
437  ++pos_;
438  }
439  return PAR::acceptSrc(n)
440  or isSrcMatch;
441  }
442 
444  virtual bool
445  findSrc (GenNode const& ref) override
446  {
447  Iter found = TestMutationTarget::search (ref.idi, pos_);
448  if (found)
449  {
450  target_.inject (move(*found), "findSrc");
451  }
452  return PAR::findSrc(ref)
453  or found;
454  }
455 
457  virtual bool
458  accept_until (GenNode const& spec)
459  {
460  bool foundTarget = true;
461 
462  if (spec.matches (Ref::END))
463  for ( ; pos_; ++pos_)
464  target_.inject (move(*pos_), "accept_until END");
465  else
466  if (spec.matches (Ref::ATTRIBS))
467  for ( ; pos_ and pos_->isNamed(); ++pos_)
468  target_.inject (move(*pos_), "accept_until after ATTRIBS");
469  else
470  {
471  string logMsg{"accept_until "+spec.idi.getSym()};
472  while (pos_ and not TestWireTap::matchSrc(spec))
473  {
474  target_.inject (move(*pos_), logMsg);
475  ++pos_;
476  }
477  if (TestWireTap::matchSrc(spec))
478  {
479  target_.inject (move(*pos_), logMsg);
480  ++pos_;
481  }
482  else
483  foundTarget = false;
484  }
485  return PAR::accept_until(spec)
486  or foundTarget;
487  }
488 
491  virtual bool
492  assignElm (GenNode const& spec)
493  {
494  Iter targetElm = target_.locate (spec.idi);
495  if (targetElm)
496  {
497  string logOldPayload{render(targetElm->data)};
498  *targetElm = spec;
499  target_.logAssignment (*targetElm, logOldPayload);
500  }
501  return PAR::assignElm(spec)
502  or targetElm;
503  }
504 
507  virtual bool
508  mutateChild (GenNode const& spec, TreeMutator::Handle targetBuff)
509  {
510  if (PAR::mutateChild (spec, targetBuff))
511  return true;
512  else // Test mode only --
513  { // no other layer was able to provide a mutator
514  Iter targetElm = target_.locate (spec.idi);
515  if (targetElm)
516  {
517  targetBuff.emplace (TreeMutator::build());
518  target_.logMutation (*targetElm);
519  return true;
520  }
521  return false;
522  }
523  }
524 
527  virtual bool
529  {
530  target_.logScopeCompletion (pos_);
531  return PAR::completeScope()
532  and isnil(this->pos_);
533  }
534  };
535 
536 
537  template<class PAR>
538  auto
540  {
541  return chainedBuilder<TestWireTap<PAR>> (dummy);
542  }
543 
544  }
545 
546 
547 
548 }} // namespace lib::diff
549 #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:46
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:85
Typesafe union record.
Definition: variant.hpp:224
Helper to log and verify the occurrence of events.
Definition: event-log.hpp:284
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:308
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:113
Special collection to represent object-like data.
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:871
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:620
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:125
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:360
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:874
Offset measures a distance in time.
Definition: timevalue.hpp:367
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:477
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:582
auto transformIterator(IT const &src, FUN processingFunc)
Build a TransformIter: convenience free function shortcut, picking up the involved types automaticall...
Definition: itertools.hpp:797
object-like record of data.
Definition: record.hpp:150
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:231
virtual bool acceptSrc(GenNode const &n) override
accept existing element, when matching the given spec