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