/* TreeMutatorBinding(Test) - techniques to map generic changes onto concrete tree shaped data Copyright (C) Lumiera.org 2016, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************/ /** @file tree-mutator-binding-test.cpp ** unit test \ref TreeMutatorBinding_test */ #include "lib/test/run.hpp" #include "lib/format-util.hpp" #include "lib/test/test-helper.hpp" #include "lib/diff/tree-mutator.hpp" #include "lib/diff/test-mutation-target.hpp" #include "lib/iter-adapter-stl.hpp" #include "lib/time/timevalue.hpp" #include "lib/format-cout.hpp" #include "lib/format-util.hpp" #include "lib/error.hpp" #include "lib/util.hpp" #include #include using util::join; using util::isnil; using util::contains; using util::stringify; using lib::iter_stl::eachElm; using lib::time::Time; using std::string; using util::typeStr; namespace lib { namespace diff{ namespace test{ using lumiera::error::LERR_(LOGIC); namespace {//Test fixture.... // define some GenNode elements // to act as templates within the concrete diff // NOTE: everything in this diff language is by-value const GenNode ATTRIB1("α", 1), // attribute α = 1 ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t) ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double) TYPE_X("type", "ξ"), // a "magic" type attribute "Xi" TYPE_Z("type", "ζ"), // CHILD_A("a"), // unnamed string child node CHILD_B('b'), // unnamed char child node CHILD_T(Time(12,34,56,78)), // unnamed time value child SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ GAMMA_PI("γ", 3.14159265); // happens to have the same identity (ID) as ATTRIB3 }//(End)Test fixture /********************************************************************************//** * @test Building blocks to map generic changes to arbitrary private data structures. * - use a dummy diagnostic implementation to verify the interface * - verify an adapter to apply structure modification to a generic collection * - use closures to translate mutation into manipulation of private attributes * - integrate the standard case of tree diff application to `Rec` * * @remark even while this is a very long and detail oriented test, it barely * scratches the surface of what is possible with _layering multiple bindings_ * on top of each other. In fact, what follows are several self contained tests, * each performing roughly the same scenario, yet targeted at different local * data structures through appropriate special bindings given as lambda. * @remark _you should note_ that the scenario executed in each of these tests * precisely corresponds to the application of the test diff used in * (\ref DiffComplexApplication_test) * @remark _to help with understanding this,_ please consider how diff application is * actually implemented on top of a set of "primitives". The TreeMutator interface * on the other hand offers precisely these building blocks necessary to implement * diff application to an arbitrary hierarchical data structure. In this way, the * following test cases demonstrate the intermediary steps executed when applying * this test diff through the concrete binding exemplified in each case * @remark the **test diff** implied here reads as follows * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ins(ATTRIB1) * ins(ATTRIB3) * ins(ATTRIB3) * ins(CHILD_B) * ins(CHILD_B) * ins(CHILD_T) * // ==> ATTRIB1, ATTRIB3, ATTRIB3, CHILD_B, CHILD_B, CHILD_T * after(Ref::ATTRIBS) * ins(ATTRIB2) * del(CHILD_B) * ins(SUB_NODE) * find(CHILD_T) * pick(CHILD_B) * skip(CHILD_T) * // ==> ATTRIB1, ATTRIB3, (ATTRIB3), ATTRIB2, SUB_NODE, CHILD_T, CHILD_B * after(CHILD_B) * after(Ref::END) * set(GAMMA_PI) * mut(SUB_NODE) * ins(TYPE_X) * ins(ATTRIB2) * ins(CHILD_B) * ins(CHILD_A) * emu(SUB_NODE) * ins(ATTRIB_NODE) * mut(ATTRIB_NODE) * ins(TYPE_Z) * ins(CHILD_A) * ins(CHILD_A) * ins(CHILD_A) * emu(ATTRIB_NODE) * // ==> ATTRIB1, ATTRIB3 := π, (ATTRIB3), ATTRIB2, * // ATTRIB_NODE{ type ζ, CHILD_A, CHILD_A, CHILD_A } * // SUB_NODE{ type ξ, ATTRIB2, CHILD_B, CHILD_A }, * // CHILD_T, CHILD_B * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * @see TreeMutator * @see TreeMutator_test * @see DiffTreeApplication_test * @see GenNodeBasic_test * @see AbstractTangible_test::mutate() */ class TreeMutatorBinding_test : public Test { virtual void run (Arg) { mutateDummy(); mutateCollection(); mutateAttribute(); mutateGenNode(); } /** @test diagnostic binding: how to monitor and verify the mutations applied */ void mutateDummy() { MARK_TEST_FUN; TestMutationTarget target; auto mutator = TreeMutator::build() .attachDummy (target); mutator.init(); CHECK (isnil (target)); CHECK (not mutator.hasSrc()); mutator.injectNew (ATTRIB1); CHECK (!isnil (target)); CHECK (contains(target.showContent(), "α = 1")); CHECK (target.verifyEvent("injectNew","α = 1") .after("attachMutator")); mutator.injectNew (ATTRIB3); mutator.injectNew (ATTRIB3); 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") .beforeEvent("injectNew","γ = 3.45") .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; " << target.showContent() <