From 4571d3fb0fbc5c610727be4ad26f80ac5c3ca993 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 24 May 2016 22:23:06 +0200 Subject: [PATCH] introduce new mutation primitive as pointed out by preceding analysis to summarise, it turned out that it is impossible to provide an airtight 'emptySrc' implementation when binding to object fields -- so we distinguish into positive and negative tests, allowing to loosen the sanity check only for the latter ones when binding to object fields. --- src/lib/diff/test-mutation-target.hpp | 17 +++++++++++++++++ .../diff/tree-mutator-collection-binding.hpp | 9 +++++++++ src/lib/diff/tree-mutator.hpp | 11 +++++++++++ .../diff/tree-manipulation-binding-test.cpp | 14 ++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/src/lib/diff/test-mutation-target.hpp b/src/lib/diff/test-mutation-target.hpp index afa23e23f..42cf8c6b9 100644 --- a/src/lib/diff/test-mutation-target.hpp +++ b/src/lib/diff/test-mutation-target.hpp @@ -282,6 +282,14 @@ namespace diff{ % render(target.data)); } + void + logScopeCompletion (iterator processingPos) + { + log_.event ("completeScope", _Fmt{"⤴ scope%s completed / %d waste elm(s) dropped"} + % (processingPos? " NOT":"") + % prev_content_.size()); + } + /* === Diagnostic / Verification === */ @@ -510,6 +518,15 @@ namespace diff{ } } + /** verify all our pending (old) source elements where mentioned. + * @note allows chained "onion-layers" to clean-up and verify.*/ + virtual bool + completeScope() + { + target_.logScopeCompletion (pos_); + return PAR::completeScope() + and isnil(this->pos_); + } }; diff --git a/src/lib/diff/tree-mutator-collection-binding.hpp b/src/lib/diff/tree-mutator-collection-binding.hpp index 99fb71567..8ec83145c 100644 --- a/src/lib/diff/tree-mutator-collection-binding.hpp +++ b/src/lib/diff/tree-mutator-collection-binding.hpp @@ -310,6 +310,15 @@ return target_found? binding_.openSub (*target_found, spec.idi, targetBuff) : false; } + + /** verify all our pending (old) source elements where mentioned. + * @note allows chained "onion-layers" to clean-up and verify.*/ + virtual bool + completeScope() + { + return PAR::completeScope() + and isnil(this->pos_); + } }; diff --git a/src/lib/diff/tree-mutator.hpp b/src/lib/diff/tree-mutator.hpp index 4db462a78..316327843 100644 --- a/src/lib/diff/tree-mutator.hpp +++ b/src/lib/diff/tree-mutator.hpp @@ -280,6 +280,17 @@ namespace diff{ return false; } + /** ensure the scope addressed by this TreeMutator + * was processed and exhausted without mismatch + * @return `true` when all "open ends" are closed + * and no pending work remains to be done. */ + virtual bool + completeScope() + { + // nothing to clean-up or verify by default + return true; + } + virtual void setAttribute (ID, Attribute&) { /* do nothing by default */ } /** diff --git a/tests/library/diff/tree-manipulation-binding-test.cpp b/tests/library/diff/tree-manipulation-binding-test.cpp index 9fa91a08b..ac3573cc1 100644 --- a/tests/library/diff/tree-manipulation-binding-test.cpp +++ b/tests/library/diff/tree-manipulation-binding-test.cpp @@ -136,6 +136,7 @@ namespace test{ mutator.injectNew (CHILD_B); mutator.injectNew (CHILD_B); mutator.injectNew (CHILD_T); + CHECK (mutator.completeScope()); CHECK (target.verify("attachMutator") .beforeEvent("injectNew","α = 1") .beforeEvent("injectNew","γ = 3.45") @@ -143,6 +144,7 @@ namespace test{ .beforeEvent("injectNew","b") .beforeEvent("injectNew","b") .beforeEvent("injectNew","78:56:34.012") + .beforeEvent("completeScope","scope completed") ); CHECK (target.showContent() == "α = 1, γ = 3.45, γ = 3.45, b, b, 78:56:34.012"); cout << "Content after population; " @@ -200,6 +202,7 @@ namespace test{ CHECK (mutator2.acceptSrc (CHILD_T)); // acceptSrc CHECK (not mutator2.hasSrc()); // source contents exhausted CHECK (not mutator2.acceptSrc (CHILD_T)); + CHECK (mutator2.completeScope()); // no pending elements left, everything resolved CHECK (target.verify("attachMutator") .beforeEvent("injectNew","78:56:34.012") .before("attachMutator") @@ -212,6 +215,7 @@ namespace test{ .beforeEvent("injectNew","Rec()") .beforeEvent("acceptSrc","b") .beforeEvent("acceptSrc","78:56:34.012") + .beforeEvent("completeScope","scope completed") ); CHECK (target.showContent() == "γ = 3.45, α = 1, β = 2, γ = 3.45, Rec(), b, 78:56:34.012"); cout << "Content after reordering; " @@ -233,7 +237,9 @@ namespace test{ CHECK (not contains(target.showContent(), "γ = 3.1415927")); CHECK (mutator3.assignElm(GAMMA_PI)); // ...we assign a new payload to the current element first CHECK ( contains(target.showContent(), "γ = 3.1415927")); + CHECK (not mutator3.completeScope()); // not done yet... CHECK (mutator3.accept_until (Ref::END)); // fast forward, since we do not want to re-order anything + CHECK ( mutator3.completeScope()); // now any pending elements where default-resolved cout << "Content after assignment; " << target.showContent() <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]) <