Lumiera  0.pre.03
»edit your freedom«
tree-mutator-binding-test.cpp
Go to the documentation of this file.
1 /*
2  TreeMutatorBinding(Test) - techniques to map generic changes onto concrete tree shaped data
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 
28 #include "lib/test/run.hpp"
29 #include "lib/format-util.hpp"
30 #include "lib/test/test-helper.hpp"
33 #include "lib/iter-adapter-stl.hpp"
34 #include "lib/time/timevalue.hpp"
35 #include "lib/format-cout.hpp"
36 #include "lib/format-util.hpp"
37 #include "lib/error.hpp"
38 #include "lib/util.hpp"
39 
40 #include <vector>
41 #include <string>
42 
43 using util::join;
44 using util::isnil;
45 using util::contains;
46 using util::stringify;
48 using lib::time::Time;
49 using std::string;
50 
51 using util::typeStr;
52 
53 
54 namespace lib {
55 namespace diff{
56 namespace test{
57 
58  using lumiera::error::LERR_(LOGIC);
59 
60 
61  namespace {//Test fixture....
62 
63  // define some GenNode elements
64  // to act as templates within the concrete diff
65  // NOTE: everything in this diff language is by-value
66  const GenNode ATTRIB1("α", 1), // attribute α = 1
67  ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t)
68  ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
69  TYPE_X("type", "ξ"), // a "magic" type attribute "Xi"
70  TYPE_Z("type", "ζ"), //
71  CHILD_A("a"), // unnamed string child node
72  CHILD_B('b'), // unnamed char child node
73  CHILD_T(Time(12,34,56,78)), // unnamed time value child
74  SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope
75  ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ
76  GAMMA_PI("γ", 3.14159265); // happens to have the same identity (ID) as ATTRIB3
77 
78  }//(End)Test fixture
79 
80 
81 
82 
83 
84 
85 
86 
87 
88  /********************************************************************************/
154  class TreeMutatorBinding_test : public Test
155  {
156 
157  virtual void
158  run (Arg)
159  {
160  mutateDummy();
162  mutateAttribute();
163  mutateGenNode();
164  }
165 
166 
168  void
170  {
172  TestMutationTarget target;
173  auto mutator =
175  .attachDummy (target);
176  mutator.init();
177 
178  CHECK (isnil (target));
179  CHECK (not mutator.hasSrc());
180 
181  mutator.injectNew (ATTRIB1);
182  CHECK (!isnil (target));
183  CHECK (contains(target.showContent(), "α = 1"));
184  CHECK (target.verifyEvent("injectNew","α = 1")
185  .after("attachMutator"));
186 
187  mutator.injectNew (ATTRIB3);
188  mutator.injectNew (ATTRIB3);
189  mutator.injectNew (CHILD_B);
190  mutator.injectNew (CHILD_B);
191  mutator.injectNew (CHILD_T);
192  CHECK (mutator.completeScope());
193  CHECK (target.verify("attachMutator")
194  .beforeEvent("injectNew","α = 1")
195  .beforeEvent("injectNew","γ = 3.45")
196  .beforeEvent("injectNew","γ = 3.45")
197  .beforeEvent("injectNew","b")
198  .beforeEvent("injectNew","b")
199  .beforeEvent("injectNew","78:56:34.012")
200  .beforeEvent("completeScope","scope completed")
201  );
202  CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45, b, b, 78:56:34.012");
203  cout << "Content after population; "
204  << target.showContent() <<endl;
205 
206 
207  // now attach new mutator for second round...
208  auto mutator2 =
210  .attachDummy (target);
211  mutator2.init();
212 
213  CHECK (target.verify("attachMutator")
214  .beforeEvent("injectNew","78:56:34.012")
215  .before("attachMutator"));
216 
217  CHECK (isnil (target)); // the "visible" new content is still void
218  CHECK (mutator2.hasSrc()); // content was moved into hidden "src" buffer
219  CHECK (target.showSrcBuffer() == "α = 1, γ = 3.45, γ = 3.45, b, b, 78:56:34.012");
220 
221  CHECK (mutator2.matchSrc (ATTRIB1)); // current head element of src "matches" the given spec
222  CHECK (isnil (target)); // the match didn't change anything
223 
224  CHECK (mutator2.accept_until(Ref::ATTRIBS)); // accept_until
225  CHECK (mutator2.hasSrc());
226  CHECK (!isnil (target)); // the fast forward did accept some entries
227  CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45");
228  CHECK (mutator2.matchSrc (CHILD_B)); // ...and we're located behind the attributes, at first child
229 
230  mutator2.injectNew (ATTRIB2); // injectNew
231  CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45, β = 2");
232 
233  // now proceeding with the children.
234  // NOTE: the TestWireTap / TestMutationTarget does not enforce the attribute / children distinction!
235  CHECK (mutator2.hasSrc()); // still located behind the attributes...
236  CHECK (mutator2.matchSrc (CHILD_B)); // first child waiting in src is CHILD_B
237  mutator2.skipSrc (CHILD_B); // ...which will be skipped (and thus discarded) // skipSrc
238  mutator2.injectNew (SUB_NODE); // inject a new nested sub-structure here // injectNew
239  CHECK (mutator2.matchSrc (CHILD_B)); // yet another B-child is waiting
240  CHECK (not mutator2.findSrc (CHILD_A)); // unsuccessful find operation won't do anything
241  CHECK (mutator2.hasSrc());
242  CHECK (mutator2.matchSrc (CHILD_B)); // child B still waiting, unaffected
243  CHECK (not mutator2.acceptSrc (CHILD_T)); // refusing to accept/pick a non matching element
244  CHECK (mutator2.matchSrc (CHILD_B)); // child B still patiently waiting, unaffected
245  CHECK (mutator2.hasSrc());
246  CHECK (mutator2.findSrc (CHILD_T)); // search for an element further down into src... // findSrc
247  CHECK (!isnil (target)); // ...pick and accept it into the "visible" part of target
248  CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45, β = 2, Rec(), 78:56:34.012");
249 
250  CHECK (mutator2.matchSrc (CHILD_B)); // element at head of src is still CHILD_B (as before)
251  CHECK (mutator2.acceptSrc (CHILD_B)); // now pick and accept this src element as child // acceptSrc
252 
253  CHECK (mutator2.hasSrc()); // next we have to clean up waste
254  mutator2.skipSrc (CHILD_T); // left behind by the findSrc() operation // skipSrc
255  CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45, β = 2, Rec(), 78:56:34.012, b");
256  CHECK (not mutator2.hasSrc()); // source contents exhausted
257  CHECK (not mutator2.acceptSrc (CHILD_T));
258  CHECK (mutator2.completeScope()); // no pending elements left, everything resolved
259  CHECK (target.verify("attachMutator")
260  .beforeEvent("injectNew","78:56:34.012")
261  .before("attachMutator")
262  .beforeEvent("accept_until after ATTRIBS","α = 1")
263  .beforeEvent("accept_until after ATTRIBS","γ = 3.45")
264  .beforeEvent("accept_until after ATTRIBS","γ = 3.45")
265  .beforeEvent("injectNew","β = 2")
266  .beforeEvent("skipSrc","b")
267  .beforeEvent("injectNew","Rec()")
268  .beforeEvent("findSrc","78:56:34.012")
269  .beforeEvent("acceptSrc","b")
270  .beforeEvent("skipSrc","⟂")
271  .beforeEvent("completeScope","scope completed / 6 waste elm(s)")
272  );
273  CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45, β = 2, Rec(), 78:56:34.012, b");
274  cout << "Content after reordering; "
275  << target.showContent() <<endl;
276 
277 
278 
279  // the third round will cover tree mutation primitives...
280  auto mutator3 =
282  .attachDummy (target);
283  mutator3.init();
284 
285  // the first thing we try out is how to navigate through the sequence partially
286  CHECK (isnil (target));
287  CHECK (mutator3.matchSrc (ATTRIB1)); // new mutator starts out anew at the beginning
288  CHECK (mutator3.accept_until (CHILD_T)); // fast forward behind the second-last child (CHILD_T) // accept_until
289  CHECK (mutator3.matchSrc (CHILD_B)); // this /would/ be the next source element...
290  CHECK (not mutator3.completeScope()); // CHILD_B is still pending, not done yet...
291  CHECK (mutator3.accept_until (Ref::END)); // fast forward, since we do not want to re-order anything // accept_until
292  CHECK ( mutator3.completeScope()); // now any pending elements where default-resolved
293 
294  // next thing will be an assignment to some element by ID
295  CHECK (not contains(target.showContent(), "γ = 3.1415927"));
296  CHECK (mutator3.assignElm(GAMMA_PI)); // ...we assign a new payload to the current element first // assignElm
297  CHECK ( contains(target.showContent(), "γ = 3.1415927"));
298  CHECK ( mutator3.completeScope()); // now any pending elements where default-resolved
299  cout << "Content after assignment; "
300  << target.showContent() <<endl;
301 
302  // for mutation of an enclosed scope, in real usage the managing TreeDiffInterpreter
303  // would maintain a stack of "mutation frames", where each one provides an OpaqueHolder
304  // to place a suitable sub-mutator for this nested scope. At this point, we can't get any further
305  // with this TestWireTap / TestMutationTarget approach, since the latter just records actions and
306  // otherwise forwards operation to the rest of the TreeMutator. In case there is no /real/ mutator
307  // in any "onion layer" below the TestWireTap within this TreeMutator, we'll just get a default (NOP)
308  // implementation of TreeMutator without any further functionality.
309 
311  TreeMutator::Handle placementHandle(subMutatorBuffer);
312 
313  CHECK (mutator3.mutateChild (SUB_NODE, placementHandle)); // mutateChild
314  CHECK (not subMutatorBuffer->hasSrc()); // ...this is all we can do here
315  // the real implementation would instead find a suitable
316  // sub-mutator within this buffer and recurse into that.
317 
318  // error handling: assignment might throw
319  GenNode differentTime{CHILD_T.idi.getSym(), Time(11,22)};
320  VERIFY_ERROR (LOGIC, mutator3.assignElm (differentTime));
321 
322  CHECK (target.showContent() == "α = 1, γ = 3.1415927, γ = 3.45, β = 2, Rec(), 78:56:34.012, b");
323  CHECK (target.verifyEvent("findSrc","78:56:34.012")
324  .beforeEvent("acceptSrc","b")
325  .before("attachMutator TestWireTap")
326  .beforeEvent("accept_until _CHILD_Time.","α = 1")
327  .beforeEvent("accept_until _CHILD_Time.","γ = 3.45")
328  .beforeEvent("accept_until _CHILD_Time.","γ = 3.45")
329  .beforeEvent("accept_until _CHILD_Time.","β = 2")
330  .beforeEvent("accept_until _CHILD_Time.","Rec()")
331  .beforeEvent("accept_until _CHILD_Time.","78:56:34.012")
332  .beforeEvent("completeScope","scope NOT completed")
333  .beforeEvent("accept_until END","b")
334  .beforeEvent("completeScope","scope completed / 7 waste elm(s)")
335  .beforeEvent("assignElm","γ: 3.45 ⤅ 3.1415927")
336  .beforeEvent("completeScope","scope completed / 7 waste elm(s)")
337  .beforeEvent("mutateChild","start mutation...Rec()")
338  );
339 
340  cout << "____Mutation-Log______________\n"
341  << join(target.getLog(), "\n")
342  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
343  }
344 
345 
346 
347 
348 
362  void
364  {
366 
367  // private data structures to be mutated
368  struct Data
369  {
370  string key;
371  string val;
372 
373  operator string() const { return _Fmt{"≺%s∣%s≻"} % key % val; }
374  bool operator== (Data const& o) const { return key==o.key and val==o.val; }
375  bool operator!= (Data const& o) const { return not (*this == o); }
376  };
377 
378  using VecD = std::vector<Data>;
379  using MapD = std::map<string, VecD>;
380 
381  VecD target;
382  MapD subScopes;
383 
384  // now set up a binding to these opaque private structures...
385  auto mutator1 =
387  .attach (collection(target)
388  .constructFrom ([&](GenNode const& spec) -> Data
389  {
390  cout << "constructor invoked on "<<spec<<endl;
391  return {spec.idi.getSym(), render(spec.data)};
392  }));
393 
394  CHECK (sizeof(mutator1) <= sizeof(VecD) // the buffer for pending elements
395  + sizeof(VecD*) // the reference to the original collection
396  + 2 * sizeof(VecD::iterator) // one Lumiera RangeIter (comprised of pos and end iterators)
397  + 4 * sizeof(void*) // the four unused default configured binding functions
398  + 1 * sizeof(void*) // one back reference from the closure to this scope
399  + sizeof(void*)); // the TreeMutator VTable
400 
401 
402  // --- first round: populate the collection ---
403 
404  mutator1.init();
405 
406  CHECK (isnil (target));
407  CHECK (not mutator1.hasSrc());
408 
409  mutator1.injectNew (ATTRIB1);
410  CHECK (!isnil (target));
411  CHECK (contains(join(target), "≺α∣1≻"));
412 
413  mutator1.injectNew (ATTRIB3);
414  mutator1.injectNew (ATTRIB3);
415  mutator1.injectNew (CHILD_B);
416  mutator1.injectNew (CHILD_B);
417  mutator1.injectNew (CHILD_T);
418  CHECK (mutator1.completeScope());
419 
420  auto contents = stringify(eachElm(target));
421  CHECK ("≺α∣1≻" == *contents);
422  ++contents;
423  CHECK ("≺γ∣3.45≻" == *contents);
424  ++contents;
425  CHECK ("≺γ∣3.45≻" == *contents);
426  ++contents;
427  CHECK (contains(*contents, "∣b≻"));
428  ++contents;
429  CHECK (contains(*contents, "∣b≻"));
430  ++contents;
431  CHECK (contains(*contents, "∣78:56:34.012≻"));
432  ++contents;
433  CHECK (isnil (contents));
434 
435  cout << "injected......" << join(target) <<endl;
436 
437 
438  // --- second round: reorder the collection ---
439 
440 
441  // Mutators are one-time disposable objects,
442  // thus we'll have to build a new one for the second round...
443  auto mutator2 =
445  .attach (collection(target)
446  .constructFrom ([&](GenNode const& spec) -> Data
447  {
448  cout << "constructor invoked on "<<spec<<endl;
449  return {spec.idi.getSym(), render(spec.data)};
450  })
451  .matchElement ([&](GenNode const& spec, Data const& elm)
452  {
453  cout << "match? "<<spec.idi.getSym()<<"=?="<<elm.key<<endl;
454  return spec.idi.getSym() == elm.key;
455  }));
456 
457  // we have two lambdas now and thus can save on the size of one function pointer....
458  CHECK (sizeof(mutator1) - sizeof(mutator2) == sizeof(void*));
459 
460  mutator2.init();
461  CHECK (isnil (target)); // the "visible" new content is still void
462 
463  CHECK (mutator2.matchSrc (ATTRIB1)); // current head element of src "matches" the given spec
464  CHECK (isnil (target)); // the match didn't change anything
465 
466  CHECK (mutator2.accept_until(Ref::ATTRIBS));
467  CHECK (mutator2.matchSrc (ATTRIB1)); // NOTE: collection values can be anything; thus this
468  // collection binding layer can not have any notion of
469  // "this is an attribute". It will not accept anything
470  // and just delegate to the next lower layer, which here
471  // is the empty binding and thus finally returns true
472 
473  CHECK (mutator2.accept_until(ATTRIB3)); // ...but of course we can fast forward to dedicated values // accept_until
474  CHECK (!isnil (target)); // the fast forward did indeed accept some entries
475  CHECK (mutator2.acceptSrc(ATTRIB3)); // we have a duplicate in list, need to accept that as well // accept
476  CHECK (mutator2.hasSrc());
477  CHECK (mutator2.matchSrc (CHILD_B)); // ...now we're located behind the attributes, at first child
478  mutator2.injectNew (ATTRIB2); // injectNew
479 
480  CHECK (mutator2.matchSrc (CHILD_B)); // first child waiting in src is CHILD_B
481  mutator2.skipSrc (CHILD_B); // ...which will be skipped (and thus discarded) // skipSrc
482  mutator2.injectNew (SUB_NODE); // inject a nested sub-structure (implementation defined) // injectNew
483  CHECK (mutator2.matchSrc (CHILD_B)); // yet another B-child is waiting
484  CHECK (not mutator2.findSrc (CHILD_A)); // unsuccessful find operation won't do anything
485  CHECK (mutator2.hasSrc());
486  CHECK (mutator2.matchSrc (CHILD_B)); // child B still waiting, unaffected
487  CHECK (not mutator2.acceptSrc (CHILD_T)); // refusing to accept/pick a non matching element
488  CHECK (mutator2.matchSrc (CHILD_B)); // child B still patiently waiting, unaffected
489  CHECK (mutator2.hasSrc());
490  CHECK (mutator2.findSrc (CHILD_T)); // search for an element further down into src... // findSrc
491  CHECK (mutator2.matchSrc (CHILD_B)); // element at head of src is still CHILD_B (as before)
492  CHECK (mutator2.acceptSrc (CHILD_B)); // now pick and accept this src element as child // acceptSrc
493 
494  CHECK (mutator2.hasSrc()); // next we have to clean up waste
495  mutator2.skipSrc (CHILD_T); // left behind by the findSrc() operation // skipSrc
496  CHECK (not mutator2.hasSrc()); // source contents exhausted
497  CHECK (not mutator2.acceptSrc (CHILD_T)); // ...anything beyond is NOP
498  CHECK (mutator2.completeScope()); // no pending elements left, everything resolved
499 
500  // verify reordered shape
501  contents = stringify(eachElm(target));
502  CHECK ("≺α∣1≻" == *contents);
503  ++contents;
504  CHECK ("≺γ∣3.45≻" == *contents);
505  ++contents;
506  CHECK ("≺γ∣3.45≻" == *contents);
507  ++contents;
508  CHECK ("≺β∣2≻" == *contents);
509  ++contents;
510  CHECK (contains(*contents, "∣Rec()≻"));
511  ++contents;
512  CHECK (contains(*contents, "∣78:56:34.012≻"));
513  ++contents;
514  CHECK (contains(*contents, "∣b≻"));
515  ++contents;
516  CHECK (isnil (contents));
517 
518  cout << "Content after reordering...."
519  << join(target) <<endl;
520 
521 
522  // --- third round: mutate data and sub-scopes ---
523 
524 
525  // This time we build the Mutator bindings in a way to allow mutation
526  // For one, "mutation" means to assign a changed value to a simple node / attribute.
527  // And beyond that, mutation entails to open a nested scope and delve into that recursively.
528  // Here, as this is really just a test and demonstration, we implement those nested scopes aside,
529  // managed within a map and keyed by the sub node's ID.
530  auto mutator3 =
532  .attach (collection(target)
533  .constructFrom ([&](GenNode const& spec) -> Data
534  {
535  cout << "constructor invoked on "<<spec<<endl;
536  return {spec.idi.getSym(), render(spec.data)};
537  })
538  .matchElement ([&](GenNode const& spec, Data const& elm) -> bool
539  {
540  cout << "match? "<<spec.idi.getSym()<<"=?="<<elm.key<<endl;
541  return spec.idi.getSym() == elm.key;
542  })
543  .assignElement ([&](Data& target, GenNode const& spec) -> bool
544  {
545  cout << "assign "<<target<<" <- "<<spec<<endl;
546  CHECK (target.key == spec.idi.getSym(), "assignment to target with wrong identity");
547  target.val = render(spec.data);
548  return true;
549  })
550  .buildChildMutator ([&](Data& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
551  {
552  // use our "inside knowledge" to get at the nested scope implementation
553  VecD& subScope = subScopes[subID];
554  buff.emplace (
556  .attach (collection(subScope)
557  .constructFrom ([&](GenNode const& spec) -> Data
558  {
559  cout << "SubScope| constructor invoked on "<<spec<<endl;
560  return {spec.idi.getSym(), render(spec.data)};
561  })));
562 
563  // NOTE: mutation of sub scope has not happened yet
564  // we can only document the sub scope to be opened now
565  cout << "openSub("<<subID.getSym()<<") ⟻ "<<target<<endl;
566  target.val = "Rec(--"+subID.getSym()+"--)";
567  return true;
568  }));
569 
570  mutator3.init();
571 
572  CHECK (isnil (target));
573  CHECK (mutator3.matchSrc (ATTRIB1)); // new mutator starts out anew at the beginning
574  CHECK (mutator3.accept_until (CHILD_T)); // fast forward behind the second-last child (CHILD_T) // accept_until
575  CHECK (mutator3.matchSrc (CHILD_B)); // this /would/ be the next source element, but rather...
576  CHECK (not mutator3.completeScope()); // CHILD_B is still pending, not done yet...
577  CHECK (mutator3.accept_until (Ref::END)); // fast forward, since we do not want to re-order anything // accept_until
578  CHECK ( mutator3.completeScope()); // now any pending elements where default-resolved
579 
580  CHECK (not contains(join(target), "≺γ∣3.1415927≻"));
581  CHECK (mutator3.assignElm(GAMMA_PI)); // ...we assign a new payload to the designated element // assignElm
582  CHECK ( contains(join(target), "≺γ∣3.1415927≻"));
583  CHECK ( mutator3.completeScope());
584  cout << "Content after assignment...."
585  << join(target) <<endl;
586 
587 
588  // prepare for recursion into sub scope..
589  // Since this is a demonstration, we do not actually recurse into anything,
590  // rather we invoke the operations on a nested mutator right from here.
591 
593  TreeMutator::Handle placementHandle(subMutatorBuffer);
594 
595  CHECK (mutator3.mutateChild (SUB_NODE, placementHandle)); // mutateChild
596 
597  CHECK (isnil (subScopes[SUB_NODE.idi])); // ...this is where the nested mutator is expected to work on
598  CHECK (not subMutatorBuffer->hasSrc());
599 
600  // now use the Mutator *interface* to talk to the nested mutator...
601  // This code might be confusing, because in fact we're playing two roles here!
602  // For one, above, in the definition of mutator3 and in the declaration of MapD subScopes,
603  // the test code represents what a private data structure and binding would do.
604  // But below we enact the TreeDiffAplicator, which *would* use the Mutator interface
605  // to talk to an otherwise opaque nested mutator implementation. Actually, here this
606  // nested opaque mutator is created on-the-fly, embedded within the .buildChildMutator(..lambda...)
607  // Incidentally, we "just happen to know" how large the buffer needs to be to hold that mutator,
608  // since this is a topic beyond the scope of this test. In real usage, the DiffApplicator cares
609  // to provide a stack of suitably sized buffers for the nested mutators.
610 
611  subMutatorBuffer->injectNew (TYPE_X); // >> // injectNew
612  subMutatorBuffer->injectNew (ATTRIB2); // >> // injectNew
613  subMutatorBuffer->injectNew (CHILD_B); // >> // injectNew
614  subMutatorBuffer->injectNew (CHILD_A); // >> // injectNew
615 
616  CHECK (not isnil (subScopes[SUB_NODE.idi])); // ...and "magically" these instructions happened to insert
617  cout << "Sub|" << join(subScopes[SUB_NODE.idi]) <<endl; // some new content into our implementation defined sub scope!
618 
619  // verify contents of nested scope after mutation
620  contents = stringify(eachElm(subScopes[SUB_NODE.idi]));
621  CHECK ("≺type∣ξ≻" == *contents);
622  ++contents;
623  CHECK ("≺β∣2≻" == *contents);
624  ++contents;
625  CHECK (contains(*contents, "∣b≻"));
626  ++contents;
627  CHECK (contains(*contents, "∣a≻"));
628  ++contents;
629  CHECK (isnil (contents));
630 
631 
632  // now back to parent scope....
633  // ...add a new attribute and immediately recurse into it
634  mutator3.injectNew (ATTRIB_NODE);
635  CHECK (mutator3.mutateChild (ATTRIB_NODE, placementHandle)); // NOTE: we're just recycling the buffer. InPlaceHolder handles lifecycle properly
636  subMutatorBuffer->injectNew (TYPE_Z);
637  subMutatorBuffer->injectNew (CHILD_A);
638  subMutatorBuffer->injectNew (CHILD_A);
639  subMutatorBuffer->injectNew (CHILD_A);
640  CHECK (subMutatorBuffer->completeScope()); // no pending "open ends" left in sub-scope
641  CHECK (mutator3.completeScope()); // and likewise in the enclosing main scope
642 
643  // and thus we've gotten a second nested scope, populated with new values
644  cout << "Sub|" << join(subScopes[ATTRIB_NODE.idi]) <<endl;
645 
646  // verify contents of this second nested scope
647  contents = stringify(eachElm(subScopes[ATTRIB_NODE.idi]));
648  CHECK ("≺type∣ζ≻" == *contents);
649  ++contents;
650  CHECK (contains(*contents, "∣a≻"));
651  ++contents;
652  CHECK (contains(*contents, "∣a≻"));
653  ++contents;
654  CHECK (contains(*contents, "∣a≻"));
655  ++contents;
656  CHECK (isnil (contents));
657 
658 
659  // back to parent scope....
660  // verify the marker left by our "nested sub-scope lambda"
661  CHECK (contains (join(target), "Rec(--"+SUB_NODE.idi.getSym()+"--)"));
662  CHECK (contains (join(target), "Rec(--"+ATTRIB_NODE.idi.getSym()+"--)"));
663 
664  cout << "Content after nested mutation...."
665  << join(target) <<endl;
666  }
667 
668 
669 
670 
671 
680  void
682  {
684 
685  // local data fields to be handled as "attributes"
686  int alpha = -1;
687  int64_t beta = -1;
688  double gamma = -1;
689 
690  // we'll use this as an attribute with nested scope ("object valued attribute")
691  TestMutationTarget delta;
692 
693 
694  #define LOG_SETTER(KEY) cout << STRINGIFY(KEY) " := "<<val<<endl;
695 
696 
697  // set up a binding to these opaque private structures...
698  auto mutator1 =
700  .change("α", [&](int val)
701  {
702  LOG_SETTER ("alpha")
703  alpha = val;
704  })
705  .change("γ", [&](double val)
706  {
707  LOG_SETTER ("gamma")
708  gamma = val;
709  });
710  mutator1.init();
711 
712  CHECK (sizeof(mutator1) <= sizeof(void*) // the TreeMutator VTable
713  + 2 * sizeof(void*) // one closure reference for each lambda
714  + 2 * sizeof(GenNode::ID)); // one attribute-key for each binding
715 
716 
717 
718  // --- first round: introduce new "attributes" ---
719 
720  CHECK (-1 == alpha);
721  CHECK (-1 == beta);
722  CHECK (-1 == gamma);
723 
724  CHECK (not mutator1.hasSrc()); // NOTE: the attribute binding has no "reference source sequence" and thus no dynamic state.
725  // (in fact it is predetermined, because it relies on a likewise fixed class definition)
726  CHECK (mutator1.completeScope()); // NOTE: this is always true and NOP, for the same reason: the structure of the binding is fixed
727 
728  mutator1.injectNew (ATTRIB1);
729  CHECK ( 1 == alpha);
730  CHECK (-1 == beta);
731  CHECK (-1 == gamma);
732 
733  mutator1.injectNew (ATTRIB3);
734  CHECK ( 1 == alpha);
735  CHECK (-1 == beta);
736  CHECK (3.45 == gamma);
737 
738  mutator1.injectNew (ATTRIB3);
739  CHECK ( 1 == alpha);
740  CHECK (-1 == beta);
741  CHECK (3.45 == gamma);
742 
743  CHECK (not mutator1.injectNew (ATTRIB2)); // ...because we didn't define a binding for ATTRIB2 (aka "beta")
744 
745  // any changes to something other than attributes are just delegated to the next "onion layer"
746  // since in this case here, there is only one layer (our attribute binding), these other changes will be ignored silently
747  mutator1.injectNew (CHILD_B);
748  mutator1.injectNew (CHILD_B);
749  mutator1.injectNew (CHILD_T);
750  CHECK (mutator1.completeScope()); // this invocation typically happens at this point, but is NOP (see above)
751 
752  CHECK ( 1 == alpha);
753  CHECK (-1 == beta);
754  CHECK (3.45 == gamma);
755  cout << "successfully 'injected' new attributes." <<endl;
756 
757 
758 
759  // --- second round: reordering ---
760 
761 
762  // in fact any re-ordering of "attributes" is prohibited,
763  // because "attributes" are mapped to object or data fields,
764  // which are fixed by definition and don't expose any ordering.
765  // While any mutations beyond attributes are passed on / ignored
766  auto mutator2 =
768  .change("α", [&](int val)
769  {
770  LOG_SETTER ("alpha")
771  alpha = val;
772  })
773  .change("β", [&](int64_t val)
774  {
775  LOG_SETTER ("beta")
776  beta = val;
777  })
778  .change("γ", [&](double val)
779  {
780  LOG_SETTER ("gamma")
781  gamma = val;
782  });
783  mutator2.init();
784 
785 
786  CHECK (sizeof(mutator2) <= sizeof(void*) // the TreeMutator VTable
787  + 3 * sizeof(void*) // one closure reference for each lambda
788  + 3 * sizeof(GenNode::ID)); // one attribute-key for each binding
789 
790 
791 
792  CHECK ( 1 == alpha);
793  CHECK (-1 == beta);
794  CHECK (3.45 == gamma); // values not affected by attaching a new mutator
795 
796  CHECK (mutator2.matchSrc (ATTRIB1)); // this "match" is positive, since our binding supports this attribute
797  CHECK ( 1 == alpha); // the (NOP) match didn't change anything...
798  CHECK (-1 == beta);
799  CHECK (3.45 == gamma);
800 
801  VERIFY_ERROR (LOGIC, mutator2.findSrc (ATTRIB3));
802  // search for an element and thus reordering is explicitly rejected...
803  // If we hadn't defined a binding for "γ", then the same operation
804  // would have been passed on silently to other binding layers.
805 
806  CHECK (mutator2.matchSrc (ATTRIB1)); // behaviour of the binding remains unaffected
807  CHECK (mutator2.acceptSrc (ATTRIB1)); // now pick and "accept" this src element (also a NOP) // acceptSrc
808 
809  VERIFY_ERROR (LOGIC, mutator2.skipSrc (ATTRIB3));
810  // and 'skip' likewise is just not implemented for attributes // skipSrc
811  CHECK ( 1 == alpha);
812  CHECK (-1 == beta);
813  CHECK (3.45 == gamma); // all these non-operations actually didn't change anything...
814 
815  CHECK (mutator2.accept_until(Ref::ATTRIBS)); // accept_until ATTRIBS
816  // what /is/ allowed though, for reasons of logic,
817  // is to "fast forward behind all attributes"
818  // of course this is implemented as NOP
819  CHECK (mutator2.accept_until(Ref::END)); // likewise for Ref::END // accept_until END
820 
821  mutator2.injectNew (ATTRIB2); // injectNew
822 
823  CHECK ( 1 == alpha);
824  CHECK ( 2 == beta); // the first operation actually causing a tangible effect
825  CHECK (3.45 == gamma);
826 
827 
828  // for sake of completeness, we'll be applying the same sequence of operations as in the other tests
829  // but since all those operations are not relevant for our attribute binding, they will be passed on
830  // to lower binding layers. And since, moreover, there /are no lower binding layers/ in our setup,
831  // they will just do nothing and return false
832  CHECK (not mutator2.matchSrc (CHILD_B));
833  mutator2.skipSrc (CHILD_B); // ...no setter binding, thus no effect // skipSrc
834  CHECK (not mutator2.injectNew (SUB_NODE));// ...no setter binding, thus no effect // injectNew
835  CHECK (not mutator2.matchSrc (CHILD_B));
836  CHECK (not mutator2.findSrc (CHILD_T)); // find for non-attribute is just passed down // findSrc
837  CHECK (not mutator2.acceptSrc (CHILD_B)); // acceptSrc
838  mutator2.skipSrc (CHILD_T); // skipSrc
839 
840  CHECK ( 1 == alpha);
841  CHECK ( 2 == beta);
842  CHECK (3.45 == gamma); // no further effect on our attribute fields
843 
844  cout << "ignored all 'reordering' operations (as expected)..." <<endl;
845 
846 
847 
848  // --- third round: mutate data and sub-scopes ---
849 
850 
851  // This third part of the test covers the actual purpose of attribute binding:
852  // the ability to assign values or even to open a sub-scope enabling recursion
853  // into a nested object stored within a data field.
854  auto mutator3 =
856  .change("γ", [&](double val)
857  {
858  LOG_SETTER ("gamma")
859  gamma = val;
860  })
861  .mutateAttrib("δ", [&](TreeMutator::Handle buff)
862  {
863  // NOTE: we use "implementation inside knowledge" regarding the nested scope,
864  // which is here represented as TestMutationTarget
865  buff.emplace (
867  .attachDummy (delta));
868 
869  // NOTE: when this closure is invoked, we're about to open the sub scope,
870  // while mutation has not happened yet
871  cout << "openSub()...\n"
872  << join(delta.getLog(), "\n") <<endl;
873  });
874  mutator3.init();
875 
876  CHECK (sizeof(mutator1) <= sizeof(void*) // the TreeMutator VTable
877  + 2 * sizeof(void*) // one closure reference for each lambda
878  + 2 * sizeof(GenNode::ID)); // one attribute-key for each binding
879 
880 
881 
882  VERIFY_ERROR (LOGIC, mutator3.accept_until (ATTRIB3)); // rejected; no support for ordering // accept_until
883  CHECK (not mutator3.accept_until (ATTRIB2)); // unknown binding, no one is responsible
884  CHECK (not mutator3.accept_until (ATTRIB1));
885  CHECK (mutator3.accept_until (Ref::ATTRIBS)); // only the generic end-of-scope marks supported
886  CHECK (mutator3.accept_until (Ref::END)); // (and implemented as NOP plus forwarding down)
887 
888  // explanation: due to the nature of a 'data field',
889  // this binding has no notion of 'ordering' and thus no 'current position'.
890  // Rather, the decision if some diff verb is applicable can be done statically.
891  CHECK (mutator3.completeScope()); // always true (for the same reason)
892 
893 
894  CHECK (not mutator3.acceptSrc (ATTRIB1));
895  CHECK (not mutator3.acceptSrc (ATTRIB2));
896  CHECK ( mutator3.acceptSrc (ATTRIB3)); // in this round we just have a binding for ATTRIB3 (== "γ")
897  CHECK ( mutator3.acceptSrc (ATTRIB_NODE));
898  // ...and of course a binding for a nested ATTRIB_NODE
899 
900  CHECK (3.45 == gamma);
901  CHECK (mutator3.assignElm(GAMMA_PI)); // ...we assign a new payload to the current element first // assignElm
902  CHECK (3.14159265 == gamma);
903  CHECK ( 1 == alpha); // the other fields remain unaffected
904  CHECK ( 2 == beta);
905 
906  cout << "successfully assigned a new value." <<endl;
907 
908 
909  // prepare for recursion into sub scope...
910  // In this demonstration, the nested scope is declared to live within an attribute `ATTRIB_NODE` (== "δ").
911  // It is implemented as `TestMutationTarget delta`, which allows us to verify a fully operational nested mutator.
912 
913  const size_t BUFF_SIZ = sizeof(TreeMutator::build().attachDummy (delta));
914  // use some suitable size here, not the point in focus for this test
915 
916  InPlaceBuffer<TreeMutator, BUFF_SIZ> subMutatorBuffer;
917  TreeMutator::Handle placementHandle(subMutatorBuffer);
918 
919  CHECK (mutator3.mutateChild (ATTRIB_NODE, placementHandle)); // mutateChild
920 
921  CHECK (isnil (delta)); // ...this is where the nested mutator is expected to work on
922  CHECK (not subMutatorBuffer->hasSrc());
923 
924 
925  // now use the Mutator *interface* to talk to the nested mutator...
926  subMutatorBuffer->injectNew (TYPE_X); // >> // injectNew
927  subMutatorBuffer->injectNew (ATTRIB2); // >> // injectNew
928  subMutatorBuffer->injectNew (CHILD_B); // >> // injectNew
929  subMutatorBuffer->injectNew (CHILD_A); // >> // injectNew
930 
931  CHECK (not isnil (delta)); // ...and "magically" these instructions happened to insert
932  cout << "Sub|" << delta.showContent() <<endl; // some new content into our implementation defined sub scope!
933  cout << "____Mutation-Log(nested)______\n"
934  << join(delta.getLog(), "\n")
935  << "\n───╼━━━━━━━━━╾────────────────"<<endl;
936 
937  // verify contents of nested scope after mutation
938  CHECK (delta.showContent() == "type = ξ, β = 2, b, a");
939 
940  // verify unaffected parent scope (data fields)
941  CHECK (3.14159265 == gamma);
942  CHECK ( 1 == alpha);
943  CHECK ( 2 == beta);
944  }
945 
946 
947 
948 
949 
956  void
958  {
960 
961  // private target data be mutated
962  Rec::Mutator target;
963 
964  // set up a GenNode binding to work on this root node...
965  auto mutator1 =
967  .attach (target);
968  mutator1.init();
969 
970 
971 #if false
972  using VecG = RecordSetup<GenNode>::Storage;
973 
974  CHECK (sizeof(mutator1) <= 2 * (sizeof(VecG) // we use two collection bindings...
975  +sizeof(VecG*) // with a buffer for pending elements and a reference to the original collection
976  + 2* sizeof(VecG::iterator) // and one Lumiera RangeIter (comprised of pos and end iterators)
977  +sizeof(void*) // the VTable for each layer of TreeMutator impl
978  )
979  + 1 * sizeof(void*)); // plus one unused selector, implemented as pointer to the default impl
985 #endif
986 
987 
988  // --- first round: populate the collection ---
989 
990  CHECK (isnil (target));
991  CHECK (not mutator1.hasSrc());
992 
993  mutator1.injectNew (ATTRIB1);
994  CHECK (!isnil (target));
995  CHECK (contains(renderRecord(target), "α = 1"));
996 
997  mutator1.injectNew (ATTRIB3);
998  mutator1.injectNew (ATTRIB3);
999  mutator1.injectNew (CHILD_B);
1000  mutator1.injectNew (CHILD_B);
1001  mutator1.injectNew (CHILD_T);
1002  CHECK (mutator1.completeScope());
1003 
1004  Rec& root = target;
1005 
1006  CHECK (!isnil (root)); // nonempty -- content has been added
1007  CHECK (Rec::TYPE_NIL == root.getType()); // type field was not touched
1008  CHECK (1 == root.get("α").data.get<int>()); // has gotten our int attribute "α"
1009  CHECK (3.45 == root.get("γ").data.get<double>()); // ... and double attribute "γ"
1010  auto scope = root.scope(); // look into the scope contents...
1011  CHECK ( *scope == CHILD_B); // there we find is CHILD_B
1012  CHECK (*++scope == CHILD_B); // followed by a second CHILD_B
1013  CHECK (*++scope == CHILD_T); // and another one CHILD_T
1014 
1015  cout << "injected...................."
1016  << renderRecord(target)<<endl;
1017 
1018 
1019 
1020  // --- second round: reorder the collection ---
1021 
1022 
1023  // Mutators are one-time disposable objects,
1024  // thus we'll have to build a new one for the second round...
1025  auto mutator2 =
1027  .attach (target);
1028 
1029  mutator2.init();
1030 
1031  CHECK (isnil (target)); // old content moved aside, visible new content still void
1032 
1033  CHECK (mutator2.matchSrc (ATTRIB1)); // current head element of src "matches" the given spec
1034  CHECK (isnil (target)); // the match didn't change anything
1035 
1036  CHECK (mutator2.accept_until(ATTRIB3)); // accept and fast forward behind a given value // accept_until
1037  CHECK (!isnil (target)); // the fast forward did indeed accept some entries
1038 
1039  CHECK (mutator2.matchSrc (ATTRIB3)); // we had a duplicate attribute entry (and Record<GenNode>
1040  // indeed represents duplicates), so this is waiting next
1041 
1042  CHECK (mutator2.accept_until(Ref::ATTRIBS)); // accept_until
1043 
1044  CHECK (mutator2.hasSrc()); // ...we did a "blind" fast forward, accepting all attributes
1045  CHECK (mutator2.matchSrc (CHILD_B)); // thus we're now located behind the attributes, at first child
1046  mutator2.injectNew (ATTRIB2); // ..no one prevents us from injecting another attribute... // injectNew
1047 
1048  CHECK (mutator2.matchSrc (CHILD_B)); // first child still waiting in src is CHILD_B
1049  mutator2.skipSrc (CHILD_B); // ...which will be skipped (and thus discarded) // skipSrc
1050  mutator2.injectNew (SUB_NODE); // inject a nested sub-structure (here a Record<GenNode>) // injectNew
1051  CHECK (mutator2.matchSrc (CHILD_B)); // yet another B-child is waiting
1052  CHECK (not mutator2.findSrc (CHILD_A)); // unsuccessful find operation won't do anything
1053  CHECK (mutator2.hasSrc());
1054  CHECK (mutator2.matchSrc (CHILD_B)); // child B still waiting, unaffected
1055  CHECK (not mutator2.acceptSrc (CHILD_T)); // refusing to accept/pick a non matching element
1056  CHECK (mutator2.matchSrc (CHILD_B)); // child B still patiently waiting, unaffected
1057  CHECK (mutator2.hasSrc());
1058  CHECK (mutator2.findSrc (CHILD_T)); // search for an element further down into src... // findSrc
1059  CHECK (mutator2.matchSrc (CHILD_B)); // element at head of src is still CHILD_B (as before)
1060  CHECK (mutator2.acceptSrc (CHILD_B)); // now pick and accept this src element as child // acceptSrc
1061 
1062  CHECK (mutator2.hasSrc()); // next we have to clean up waste
1063  mutator2.skipSrc (CHILD_T); // left behind by the findSrc() operation // skipSrc
1064  CHECK (not mutator2.hasSrc()); // source contents exhausted
1065  CHECK (not mutator2.acceptSrc (CHILD_T)); // ...anything beyond is NOP
1066  CHECK (mutator2.completeScope()); // no pending elements left, everything resolved
1067 
1068  // verify reordered shape
1069  CHECK (!isnil (root)); // nonempty -- content has been moved back
1070  CHECK (Rec::TYPE_NIL == root.getType()); // type field was not touched
1071  CHECK (1 == root.get("α").data.get<int>()); // all attributes accessible
1072  CHECK (2 == root.get("β").data.get<int64_t>());
1073  CHECK (3.45 == root.get("γ").data.get<double>());
1074  auto attrs = root.attribs(); // verify the sequence of attributes...
1075  CHECK ( *attrs == ATTRIB1); // first attribute "α" was left as it was
1076  CHECK (*++attrs == ATTRIB3); // same for the attribute "γ"
1077  CHECK (*++attrs == ATTRIB3); // ...and its duplicate
1078  CHECK (*++attrs == ATTRIB2); // and here is the newly inserted "β"
1079  CHECK (isnil (++attrs));
1080  scope = root.scope(); // look into the scope contents...
1081  CHECK ( *scope == SUB_NODE); // first the new empty nested child node
1082  CHECK (*++scope == CHILD_T); // but now followed immediately by CHILD_T
1083  CHECK (*++scope == CHILD_B); // while CHILD_B has be shuffled back
1084  CHECK (isnil (++scope)); // ...and that's all
1085 
1086  cout << "Content after reordering...."
1087  << renderRecord(target) <<endl;
1088 
1089 
1090  // --- third round: mutate data and sub-scopes ---
1091 
1092 
1093  // since our target data here is comprised of GenNode elements,
1094  // we're both able to assign data and to enter a nested Record<GenNode>
1095  // The default configuration is outfitted for this use as-is right away.
1096  auto mutator3 =
1098  .attach (target);
1099 
1100  mutator3.init();
1101 
1102  CHECK (isnil (target));
1103  CHECK (mutator3.matchSrc (ATTRIB1)); // new mutator starts out anew at the beginning
1104  CHECK (mutator3.accept_until (CHILD_T)); // fast forward behind the second-last child (CHILD_T) // accept_until
1105  CHECK (mutator3.matchSrc (CHILD_B)); // this /would/ be the next source element, but rather...
1106  CHECK (not mutator3.completeScope()); // CHILD_B is still pending, not done yet...
1107  CHECK (mutator3.accept_until (Ref::END)); // fast forward, since we do not want to re-order anything // accept_until
1108  CHECK ( mutator3.completeScope()); // now any pending elements where default-resolved
1109 
1110  CHECK (not contains(renderRecord(target), "γ = 3.1415927"));
1111  CHECK (mutator3.assignElm(GAMMA_PI)); // ...we assign a new payload to the designated element // assignElm
1112  CHECK ( contains(renderRecord(target), "γ = 3.1415927"));
1113  CHECK ( mutator3.completeScope());
1114  cout << "Content after assignment...."
1115  << renderRecord(target) <<endl;
1116 
1117  // Note: it is up to the implementation of the target data how to deal with duplicate attributes
1118  // Record<GenNode> represents attributes as a list of named sub GenNode elements, and the
1119  // access to attributes uses the first match found
1120  attrs = root.attribs(); // visit all attributes sequentially...
1121  CHECK ( *attrs == ATTRIB1); // first attribute "α" was left as it was
1122  CHECK (*++attrs == GAMMA_PI); // this is where the value assignment happened...
1123  CHECK ( attrs->data.get<double>() == GAMMA_PI.data.get<double>());
1124  CHECK (*++attrs == ATTRIB3); // ...while the duplicate "γ"...
1125  CHECK ( attrs->data.get<double>() == 3.45); // ...still holds the original value
1126  CHECK (*++attrs == ATTRIB2);
1127  CHECK (isnil (++attrs));
1128 
1129  // prepare for recursion into sub scope..
1130  // Since this is a demonstration, we do not actually recurse into anything,
1131  // rather we just let the binding generate a nested mutator into some buffer
1132  // and then we invoke the operations this nested mutator right from here.
1133 
1135  TreeMutator::Handle placementHandle(subMutatorBuffer);
1136 
1137  CHECK (mutator3.mutateChild (SUB_NODE, placementHandle)); // mutateChild
1138 
1139  GenNode const& subNode = *root.scope();
1140  CHECK (subNode == SUB_NODE); // ...this is the sub node
1141  CHECK (isnil (subNode.data.get<Rec>())); // where the nested mutator is expected to work on
1142 
1143  // now use the Mutator *interface* to talk to the nested mutator...
1144  // which was built and placed into the provided buffer
1145  CHECK (not subMutatorBuffer->hasSrc());
1146  subMutatorBuffer->injectNew (TYPE_X); // >> // injectNew
1147  subMutatorBuffer->injectNew (ATTRIB2); // >> // injectNew
1148  subMutatorBuffer->injectNew (CHILD_B); // >> // injectNew
1149  subMutatorBuffer->injectNew (CHILD_A); // >> // injectNew
1150 
1151  Rec const& nestedRec = subNode.data.get<Rec>();
1152  CHECK (not isnil (nestedRec)); // ...and "magically" these instructions happened to insert
1153  cout << "Sub-" << renderNode(subNode) <<endl; // some new content into our implementation defined sub scope!
1154 
1155  // verify contents of nested scope after mutation
1156  CHECK ("ξ" == nestedRec.getType()); // type of nested node has been set to Xi
1157  attrs = nestedRec.attribs(); // look into the nested node's attributes...
1158  CHECK ( *attrs == ATTRIB2);
1159  CHECK (isnil (++attrs));
1160  scope = nestedRec.scope(); // look into the nested nodes's scope contents...
1161  CHECK ( *scope == CHILD_B);
1162  CHECK (*++scope == CHILD_A);
1163  CHECK (isnil (++scope)); // ...and that's all
1164 
1165 
1166  // now back to parent scope....
1167  // ...add a new attribute and immediately recurse into it
1168  mutator3.injectNew (ATTRIB_NODE);
1169  CHECK (mutator3.mutateChild (ATTRIB_NODE, placementHandle)); // NOTE: we're just recycling the buffer. InPlaceHolder handles lifecycle properly
1170  subMutatorBuffer->injectNew (TYPE_X);
1171  subMutatorBuffer->injectNew (CHILD_A);
1172  subMutatorBuffer->injectNew (CHILD_A);
1173  subMutatorBuffer->injectNew (CHILD_A);
1174  subMutatorBuffer->assignElm (TYPE_Z); // NOTE use assignment to *change* the type field
1175  CHECK (subMutatorBuffer->completeScope()); // no pending "open ends" left in sub-scope
1176  CHECK (mutator3.completeScope()); // and likewise in the enclosing main scope
1177 
1178  // and thus we've gotten a second nested scope, populated with new values
1179  Rec const& attrRec = root.get("δ").data.get<Rec>();
1180  cout << "Att-" << renderNode(attrRec) <<endl;
1181 
1182  // verify contents of this second nested scope
1183  CHECK (not isnil (attrRec));
1184  CHECK ("ζ" == attrRec.getType());
1185  CHECK (isnil (attrRec.attribs()));
1186  scope = attrRec.scope();
1187  CHECK (not isnil (scope));
1188  CHECK ( *scope == CHILD_A);
1189  CHECK (*++scope == CHILD_A);
1190  CHECK (*++scope == CHILD_A);
1191  CHECK (isnil (++scope));
1192 
1193 
1194  // back to parent scope....
1195  // verify the parent scope indeed contains the nested elements in new shape
1196  CHECK (contains (renderRecord(target), renderRecord(attrRec)));
1197  CHECK (contains (renderRecord(target), renderRecord(nestedRec)));
1198 
1199  cout << "Content after sub mutation.."
1200  << renderRecord(target) <<endl;
1201  }
1202  };
1203 
1204 
1206  LAUNCHER (TreeMutatorBinding_test, "unit common");
1207 
1208 
1209 
1210 }}} // namespace lib::diff::test
string showContent() const
render payload content for diagnostics
Automatically use custom string conversion in C++ stream output.
Definition: run.hpp:49
Test adapter to watch and verify how the TreeMutator binds to custom tree data structures.
auto stringify(IT &&src)
convert to string as transforming step in a pipeline
bool operator==(PtrDerefIter< I1 > const &il, PtrDerefIter< I2 > const &ir)
Supporting equality comparisons...
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify a statement indeed raises an exception.
EventMatch & after(string match)
find a match (substring match) of the given text in an EventLog entry before the current position...
Definition: event-log.cpp:463
A front-end for using printf-style formatting.
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:305
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
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:113
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:862
#define MARK_TEST_FUN
Macro to mark the current test function in STDOUT.
EventMatch & beforeEvent(string match)
find a match for an "event" after the current point of reference
Definition: event-log.cpp:426
Simple test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
A collection of frequently used helper functions to support unit testing.
_SeqT< CON >::Range eachElm(CON &coll)
Lumiera error handling (C++ interface).
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:865
Customisable intermediary to abstract generic tree mutation operations.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Buffer to place and maintain an object instance privately within another object.
auto collection(COLL &coll)
Entry point to a nested DSL for setup and configuration of a collection binding.
Preconfigured adapters for some STL container standard usage situations.
a family of time value like entities and their relationships.
object-like record of data.
Definition: record.hpp:150
string contents(Rec const &object)
render the child elements as string data for test/verification
Diagnostic helper for unit tests regarding mutation of custom data.
EventMatch & before(string match)
find a match (substring match) of the given text in an EventLog entry after the current position ...
Definition: event-log.cpp:393
generic data element node within a tree
Definition: gen-node.hpp:231
bool contains(SEQ const &cont, typename SEQ::const_reference val)
shortcut for brute-force containment test in any sequential container
Definition: util.hpp:230