From 05768e4ac5be4efe2c9229e4c494dcbbc67563ed Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 2 Sep 2016 03:10:27 +0200 Subject: [PATCH] first part of unit-test for GenNode TreeMutator-binding PASS needed to use a forward function declaration within the lambda for recursive scope mutator building, since otherwise everything is inline and thus the compilation fails when it comes to deducing the auto return type of the builder. Other than that, the whole mechanics seem to work out of the box! --- .../diff/tree-mutator-collection-binding.hpp | 9 +- .../diff/tree-mutator-gen-node-binding.hpp | 21 +- .../diff/tree-mutator-binding-test.cpp | 285 +++++++++++++++++- 3 files changed, 304 insertions(+), 11 deletions(-) diff --git a/src/lib/diff/tree-mutator-collection-binding.hpp b/src/lib/diff/tree-mutator-collection-binding.hpp index 8f98e8eb1..6812d6508 100644 --- a/src/lib/diff/tree-mutator-collection-binding.hpp +++ b/src/lib/diff/tree-mutator-collection-binding.hpp @@ -545,6 +545,10 @@ namespace diff{ } }; + + /** @internal forward declaration for recursive mutator builder call */ + void buildNestedMutator(Rec& nestedScope, TreeMutator::Handle buff); + template<> struct _DefaultBinding { @@ -571,10 +575,7 @@ namespace diff{ if (target.idi == subID // require match on already existing child object and target.data.isNested()) { - Rec& nestedScope = target.data.get(); - buff.create ( - TreeMutator::build() - .attach (mutateInPlace (nestedScope))); + buildNestedMutator(target.data.get(), buff); return true; } else diff --git a/src/lib/diff/tree-mutator-gen-node-binding.hpp b/src/lib/diff/tree-mutator-gen-node-binding.hpp index e55c3ca5b..64d40df29 100644 --- a/src/lib/diff/tree-mutator-gen-node-binding.hpp +++ b/src/lib/diff/tree-mutator-gen-node-binding.hpp @@ -92,14 +92,23 @@ namespace diff{ inline auto Builder::attach (Rec::Mutator& targetTree) { - return this-> attach (collection (accessChildren(targetTree))) - .attach (collection (accessAttribs(targetTree))) - .isApplicableIf ([&](GenNode const& spec) - { - return spec.isNamed(); // »Selector« : treat key-value elements here - }); + return this->attach (collection (accessChildren(targetTree))) + .attach (collection (accessAttribs(targetTree)) + .isApplicableIf ([](GenNode const& spec) -> bool + { + return spec.isNamed(); // »Selector« : treat key-value elements here + })); } + inline void + buildNestedMutator(Rec& nestedScope, TreeMutator::Handle buff) + { + buff.create ( + TreeMutator::build() + .attach (mutateInPlace (nestedScope))); + } + + }//(END)Mutator-Builder decorator components... diff --git a/tests/library/diff/tree-mutator-binding-test.cpp b/tests/library/diff/tree-mutator-binding-test.cpp index 564272f7e..c136f65cf 100644 --- a/tests/library/diff/tree-mutator-binding-test.cpp +++ b/tests/library/diff/tree-mutator-binding-test.cpp @@ -935,10 +935,293 @@ namespace test{ + /** @test apply mutation primitives to a GenNode tree. + * - again we perform _literally_ the same diff steps as before + * - but we use the pre-configured binding for Record + * - internally this is comprised of two collection binding layers + * - we start with an empty root node, to be populated and transformed + */ void mutateGenNode() { - TODO ("define how to fit GenNode tree mutation into the framework"); + MARK_TEST_FUN; + + // private target data be mutated + Rec::Mutator target; + + // set up a GenNode binding to work on this root node... + auto mutator1 = + TreeMutator::build() + .attach (target); + +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #992 + CHECK (sizeof(mutator1) <= sizeof(VecD) // the buffer for pending elements + + sizeof(VecD*) // the reference to the original collection + + sizeof(void*) // the reference from the ChildCollectionMutator to the CollectionBinding + + 2 * sizeof(VecD::iterator) // one Lumiera RangeIter (comprised of pos and end iterators) + + 4 * sizeof(void*) // the four unused default configured binding functions + + 1 * sizeof(void*)); // one back reference from the closure to this scope +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #992 + + + // --- first round: populate the collection --- + + CHECK (isnil (target)); + CHECK (not mutator1.hasSrc()); + + mutator1.injectNew (ATTRIB1); + CHECK (!isnil (target)); + CHECK (contains(renderRecord(target), "α = 1")); + + mutator1.injectNew (ATTRIB3); + mutator1.injectNew (ATTRIB3); + mutator1.injectNew (CHILD_B); + mutator1.injectNew (CHILD_B); + mutator1.injectNew (CHILD_T); + CHECK (mutator1.completeScope()); + + Rec& root = target; + + CHECK (!isnil (root)); // nonempty -- content has been added + CHECK (Rec::TYPE_NIL == root.getType()); // type field was not touched + CHECK (1 == root.get("α").data.get()); // has gotten our int attribute "α" + CHECK (3.45 == root.get("γ").data.get()); // ... and double attribute "γ" + auto scope = root.scope(); // look into the scope contents... + CHECK ( *scope == CHILD_B); // there we find is CHILD_B + CHECK (*++scope == CHILD_B); // followed by a second CHILD_B + CHECK (*++scope == CHILD_T); // and another one CHILD_T + + cout << "injected......"< Data + { + cout << "constructor invoked on "< Data + { + cout << "constructor invoked on "< bool + { + cout << "match? "< bool + { + cout << "assign "< bool + { + // use our "inside knowledge" to get at the nested scope implementation + VecD& subScope = subScopes[subID]; + buff.create ( + TreeMutator::build() + .attach (collection(subScope) + .constructFrom ([&](GenNode const& spec) -> Data + { + cout << "SubScope| constructor invoked on "< subMutatorBuffer; + TreeMutator::Handle placementHandle(subMutatorBuffer); + + CHECK (mutator3.mutateChild (SUB_NODE, placementHandle)); // mutateChild + + CHECK (isnil (subScopes[SUB_NODE.idi])); // ...this is where the nested mutator is expected to work on + CHECK (not subMutatorBuffer->hasSrc()); + + // now use the Mutator *interface* to talk to the nested mutator... + // This code might be confusing, because in fact we're playing two roles here! + // For one, above, in the definition of mutator3 and in the declaration of MapD subScopes, + // the test code represents what a private data structure and binding would do. + // But below we enact the TreeDiffAplicator, which *would* use the Mutator interface + // to talk to an otherwise opaque nested mutator implementation. Actually, here this + // nested opaque mutator is created on-the-fly, embedded within the .buildChildMutator(..lambda...) + // Incidentally, we "just happen to know" how large the buffer needs to be to hold that mutator, + // since this is a topic beyond the scope of this test. In real usage, the DiffApplicator cares + // to provide a stack of suitably sized buffers for the nested mutators. + + subMutatorBuffer->injectNew (TYPE_X); // >> // injectNew + subMutatorBuffer->injectNew (ATTRIB2); // >> // injectNew + subMutatorBuffer->injectNew (CHILD_B); // >> // injectNew + subMutatorBuffer->injectNew (CHILD_A); // >> // injectNew + + CHECK (not isnil (subScopes[SUB_NODE.idi])); // ...and "magically" these instructions happened to insert + cout << "Sub|" << join(subScopes[SUB_NODE.idi]) <injectNew (TYPE_Z); + subMutatorBuffer->injectNew (CHILD_A); + subMutatorBuffer->injectNew (CHILD_A); + subMutatorBuffer->injectNew (CHILD_A); + CHECK (subMutatorBuffer->completeScope()); // no pending "open ends" left in sub-scope + CHECK (mutator3.completeScope()); // and likewise in the enclosing main scope + + // and thus we've gotten a second nested scope, populated with new values + cout << "Sub|" << join(subScopes[ATTRIB_NODE.idi]) <