From e81b0592d3e97eec96ff72ad26ad53e843594db0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 12 Oct 2018 02:05:11 +0200 Subject: [PATCH] TreeMutator: combine no-op layer with selective other diff binding ...and complete unit test coverage. This is complex stuff and we'd better be careful it actually works --- src/lib/diff/tree-mutator-noop-binding.hpp | 22 +++--- .../library/diff/diff-ignore-changes-test.cpp | 72 ++++++++++++++++++- wiki/thinkPad.ichthyo.mm | 44 +++++++++--- 3 files changed, 119 insertions(+), 19 deletions(-) diff --git a/src/lib/diff/tree-mutator-noop-binding.hpp b/src/lib/diff/tree-mutator-noop-binding.hpp index c8fea45fb..51762e2e7 100644 --- a/src/lib/diff/tree-mutator-noop-binding.hpp +++ b/src/lib/diff/tree-mutator-noop-binding.hpp @@ -51,14 +51,10 @@ #include "lib/error.hpp" -#include "lib/symbol.hpp" #include "lib/diff/gen-node.hpp" #include "lib/diff/tree-mutator.hpp" -#include "lib/format-string.hpp" -#include "lib/idi/entry-id.hpp" - -#include +#include namespace lib { namespace diff{ @@ -85,7 +81,7 @@ namespace diff{ bool hasSrc() override { return true; } ///< always keen to do yet more - bool injectNew (Elm) override { return true; } ///< pretend to inject a new element + bool injectNew (Elm) override { return true; } ///< pretend to inject something new bool matchSrc (Elm) override { return true; } ///< purport suitable element is waiting bool acceptSrc (Elm) override { return true; } ///< claim to handle any diff task bool accept_until (Elm) override { return true; } ///< profess to forward anywhere @@ -93,7 +89,7 @@ namespace diff{ bool assignElm (Elm) override { return true; } ///< accept any assignment bool - mutateChild (Elm, Buff buff) override ///< bluff to care for children, while just reproducing ourselves + mutateChild (Elm, Buff buff) override ///< ignore inferiors, yet reproduce yourself { buff.create (BlackHoleMutation()); return true; @@ -102,11 +98,21 @@ namespace diff{ - /** Entry point for DSL builder */ + /** Entry point for DSL builder: create a binding which consumes any diff without effect. + * @warning must be used as bottom most layer in a custom TreeMutator, since it would + * otherwise shadow and disable any binding layer below. + * @note however it is possible to add a (typically selective) binding layer on top; + * any diff verb _not handled_ by such a top layer will fall through and + * be silently ignored. Such a setup can be used to "fish" for some specific + * attributes within a diff stream. + */ template inline auto Builder::ignoreAllChanges() { + static_assert (std::is_same() + ,"ignoreAllChanges() must be used as bottom layer."); + return Builder (BlackHoleMutation{}); } diff --git a/tests/library/diff/diff-ignore-changes-test.cpp b/tests/library/diff/diff-ignore-changes-test.cpp index 6252d715a..95c4f3a16 100644 --- a/tests/library/diff/diff-ignore-changes-test.cpp +++ b/tests/library/diff/diff-ignore-changes-test.cpp @@ -145,15 +145,35 @@ namespace test{ virtual void run (Arg) + { + fail_or_ignore(); + fish_for_content(); + } + + + /** @test fail or ignore, depending on toggle. + * - the TreeMutator default implementation produces a failure, + * when it actually has to handle some diff verb + * - yet a custom TreeMutator can be configured to `ignoreAllChanges()`, + * in which case it will consume any diff without effect. + * This test also demonstrates that the actual TreeMutator is built anew + * for each diff application (TreeMutator is meant to be disposable). + * Thus we may alter the behaviour of the diff binding dynamically. + * @remark the actual use case for this is the TimelineGui, which + * either forwards changes to a TimelineWidget, or silently + * ignores them when the corresponding timeline is not opened. + */ + void + fail_or_ignore() { struct HappyBlackHole { - bool grumpy = true; + bool diligent = true; void buildMutator (TreeMutator::Handle buff) { - if (grumpy) + if (diligent) buff.create( TreeMutator()); else @@ -170,12 +190,58 @@ namespace test{ VERIFY_ERROR(DIFF_CONFLICT, application.consume(reorderingDiff()) ); VERIFY_ERROR(DIFF_CONFLICT, application.consume(mutationDiff()) ); - subject.grumpy = false; + subject.diligent = false; application.consume(populationDiff()); application.consume(reorderingDiff()); application.consume(mutationDiff()); } + + + /** @test fish some content and ignore everything else. + * While the BlackHoleMutation binding generated by `ignoreAllChanges()` + * must be used as bottom layer of a custom TreeMutator, it is possible + * to layer a partial diff binding on top. This test demonstrates this + * with a single attribute binding, which just "fishes" any value + * mentioned in the diff for that specific attribute. Any other + * changes are silently ignored non the less... + */ + void + fish_for_content() + { + struct Scrounger + { + double loot = 0.0; + + void + buildMutator (TreeMutator::Handle buff) + { + buff.create( + TreeMutator::build() + .ignoreAllChanges() + .change("γ", [&](double val) { loot = val; })); + } + }; + + Scrounger subject; + DiffApplicator application(subject); + + const auto NOTHING = 0.0; + const auto VAL_1 = ATTRIB3.data.get(); + const auto VAL_2 = GAMMA_PI.data.get(); + + CHECK (subject.loot == NOTHING); + + application.consume(populationDiff()); + CHECK (subject.loot == VAL_1); + + application.consume(reorderingDiff()); + CHECK (subject.loot == VAL_1); + + application.consume(mutationDiff()); + CHECK (subject.loot == VAL_2); + } + }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 9ca2edb50..72a320157 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -16549,7 +16549,7 @@ - + @@ -16579,12 +16579,14 @@ - - + + - + + + @@ -16600,20 +16602,46 @@ + + + + + + + + + +

+ ...damit es nicht versehentlich über einen anderen Layer gelegt wird, +

+

+ welchen es dann überdecken und außer Gefecht setzen würde. +

+

+ +

+

+ Habe verifiziert, daß diese Assertion tatsächlich greift +

+ + +
+ +
- - + + - - + +