Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
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"
47#include "lib/nocopy.hpp"
48#include "lib/util.hpp"
49
50#include <utility>
51#include <string>
52#include <vector>
53
54
55namespace lib {
56namespace diff{
57
58 namespace error = lumiera::error;
59
60 using lib::Literal;
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
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
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
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)
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
195
196
197 public:
198 using iterator = iter_stl::_SeqT<VecG>::Range;
199 using const_iterator = 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
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
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
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
298 {
299 return join (transformIterator (eachElm(prev_content_), renderNode));
300 }
301
303 verify (string match) const
304 {
305 return getLog().verify(match);
306 }
307
309 verifyMatch (string regExp) const
310 {
311 return getLog().verifyMatch(regExp);
312 }
313
315 verifyEvent (string match) const
316 {
317 return getLog().verifyEvent(match);
318 }
319
321 verifyEvent (string classifier, string match) const
322 {
323 return getLog().verifyEvent (classifier,match);
324 }
325
327 verifyCall (string match) const
328 {
329 return getLog().verifyCall(match);
330 }
331
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 {
355
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*/
Inline string literal.
Definition symbol.hpp:78
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
Typesafe union record.
Definition variant.hpp:216
wrapped record reference.
Definition record.hpp:618
object-like record of data.
Definition record.hpp:142
scopeIter attribs() const
Definition record.hpp:318
scopeIter scope() const
Definition record.hpp:319
string getType() const
Definition record.hpp:227
Test adapter to watch and verify how the TreeMutator binds to custom tree data structures.
static iterator search(GenNode::ID const &targetID, iterator pos)
iterator initMutation(string mutatorID)
friend const_iterator begin(TestMutationTarget const &target)
iterator locate(GenNode::ID const &targetID)
EventMatch verifyEvent(string classifier, string match) const
EventMatch verify(string match) const
string showContent() const
render payload content for diagnostics
iter_stl::_SeqT< const VecG >::Range const_iterator
void logMutation(GenNode const &target)
EventMatch verifyCall(string match) const
EventMatch verifyEvent(string match) const
EventMatch verifyMatch(string regExp) const
void inject(GenNode &&elm, string operationID)
friend const_iterator end(TestMutationTarget const &target)
void logSkip(GenNode const &content)
void logScopeCompletion(iterator processingPos)
EventMatch ensureNot(string match) const
string showSrcBuffer() const
render elements waiting in source buffer to be accepted
void logAssignment(GenNode const &target, string oldPayload)
iter_stl::_SeqT< VecG >::Range iterator
virtual bool acceptSrc(GenNode const &n) override
accept existing element, when matching the given spec
virtual bool accept_until(GenNode const &spec)
repeatedly accept, until after the designated location
virtual bool assignElm(GenNode const &spec)
locate element already accepted into the target sequence and assign the designated payload value to i...
virtual bool completeScope()
verify all our pending (old) source elements where mentioned.
virtual void skipSrc(GenNode const &n) override
skip next recorded src element without touching it
virtual bool injectNew(GenNode const &n) override
record in the test target that a new child element is being inserted at current position
virtual bool findSrc(GenNode const &ref) override
locate designated element and accept it at current position
virtual bool matchSrc(GenNode const &n) override
ensure the next recorded source element matches on a formal level with given spec
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...
Hash implementation based on a lumiera unique object id (LUID) When invoking the default ctor,...
string const & getSym() const
Definition entry-id.hpp:169
Helper to log and verify the occurrence of events.
EventLog & event(string text)
log some text as event
EventMatch verify(string match) const
start a query to match for some substring.
EventMatch verifyCall(string match) const
start a query to match especially a function call
EventMatch verifyEvent(string match) const
start a query to match for some event.
EventMatch verifyMatch(string regExp) const
start a query to match with a regular expression
EventMatch ensureNot(string match) const
start a query to ensure the given expression does not match.
Duration is the internal Lumiera time metric.
Offset measures a distance in time.
A time interval anchored at a specific point in time.
Lumiera's internal time value datatype.
Any copy and copy construction prohibited.
Definition nocopy.hpp:38
A front-end for using printf-style formatting.
Lumiera error handling (C++ interface).
Support for verifying the occurrence of events from unit tests.
Front-end for printf-style string template interpolation.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Generic functions to build identification schemes.
string instanceTypeID(const TY *const obj)
designation of an distinct object instance
Definition genfunc.hpp:116
_SeqT< CON >::Range eachElm(CON &coll)
Implementation namespace for support and library code.
auto transformIterator(IT const &src, FUN processingFunc)
Build a TransformIter: convenience free function shortcut, picking up the involved types automaticall...
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Mix-Ins to allow or prohibit various degrees of copying and cloning.
Special collection to represent object-like data.
generic data element node within a tree
Definition gen-node.hpp:224
bool matches(GenNode const &o) const
Definition gen-node.hpp:352
bool isNamed() const
Definition gen-node.hpp:337
Builder-DSL to create and configure a concrete TreeMutator.
Marker types to indicate a literal string and a Symbol.
#define STRINGIFY_CONTENT(_TY_)
Customisable intermediary to abstract generic tree mutation operations.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...