while implementing this, I've discovered a conceptual error: we allow to accept attributes, even when we've already entered the child scope. This means that we can not predictable get back at the "last" (i.e. the currently touched) element, because this might be such an attribute. So a really correct implementation would have to memorise the "current" element, which is really tricky, given the various ways of touching elements in our diff language. In the end I've decided to ignore this problem (maybe a better solution would have been to disallow those "late" attributes?) My reasoning is that attributes are unlikely to be full records, rather just values, and values are never mutated. (but note that it is definitively possible to have an record as attribute!)
188 lines
9 KiB
C++
188 lines
9 KiB
C++
/*
|
||
DiffTreeApplication(Test) - demonstrate the basics of tree diff representation
|
||
|
||
Copyright (C) Lumiera.org
|
||
2015, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
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.
|
||
|
||
* *****************************************************/
|
||
|
||
|
||
#include "lib/test/run.hpp"
|
||
#include "lib/diff/tree-diff-application.hpp"
|
||
#include "lib/iter-adapter-stl.hpp"
|
||
#include "lib/time/timevalue.hpp"
|
||
#include "lib/format-util.hpp"
|
||
#include "lib/util.hpp"
|
||
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
using lib::iter_stl::snapshot;
|
||
using util::isnil;
|
||
using util::join;
|
||
using std::string;
|
||
using std::vector;
|
||
using lib::time::Time;
|
||
|
||
|
||
namespace lib {
|
||
namespace diff{
|
||
namespace test{
|
||
|
||
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("β", 2L), // attribute α = 2L (int64_t)
|
||
ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
|
||
TYPE_X("type", "X"), // a "magic" type attribute "X"
|
||
TYPE_Y("type", "Y"), //
|
||
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 δ
|
||
CHILD_NODE = SUB_NODE; // yet another child node, same ID as SUB_NODE (!)
|
||
|
||
}//(End)Test fixture
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/***********************************************************************//**
|
||
* @test Demonstration/Concept: a description language for list differences.
|
||
* The representation is given as a linearised sequence of verb tokens.
|
||
* This test demonstrates the application of such a diff representation
|
||
* to a given source list, transforming this list to hold the intended
|
||
* target list contents.
|
||
*
|
||
* @see session-structure-mapping-test.cpp
|
||
*/
|
||
class DiffTreeApplication_test
|
||
: public Test
|
||
, TreeDiffLanguage
|
||
{
|
||
using DiffSeq = iter_stl::IterSnapshot<DiffStep>;
|
||
|
||
DiffSeq
|
||
populationDiff()
|
||
{
|
||
return snapshot({ins(TYPE_X)
|
||
, ins(ATTRIB1)
|
||
, ins(ATTRIB2)
|
||
, ins(ATTRIB3)
|
||
, ins(CHILD_A)
|
||
, ins(CHILD_T)
|
||
, ins(CHILD_T)
|
||
, ins(SUB_NODE)
|
||
, mut(SUB_NODE)
|
||
, ins(CHILD_B)
|
||
, ins(CHILD_A)
|
||
, emu(SUB_NODE)
|
||
});
|
||
}
|
||
|
||
|
||
DiffSeq
|
||
mutationDiff()
|
||
{
|
||
return snapshot({after(Ref::ATTRIBS) // fast forward to the first child
|
||
, find(CHILD_T)
|
||
, pick(CHILD_A)
|
||
, skip(CHILD_T)
|
||
, del(CHILD_T)
|
||
, pick(Ref::CHILD) // pick a child anonymously
|
||
, mut(Ref::THIS) // mutate the current element (the one just picked)
|
||
, ins(ATTRIB3)
|
||
, ins(ATTRIB_NODE)
|
||
, find(CHILD_A)
|
||
, del(CHILD_B)
|
||
, ins(CHILD_NODE)
|
||
, ins(CHILD_T)
|
||
, skip(CHILD_A)
|
||
, mut(CHILD_NODE)
|
||
, ins(TYPE_Y)
|
||
, ins(ATTRIB2)
|
||
, emu(CHILD_NODE)
|
||
, mut(ATTRIB_NODE)
|
||
, ins(CHILD_A)
|
||
, ins(CHILD_A)
|
||
, ins(CHILD_A)
|
||
, emu(ATTRIB_NODE)
|
||
, emu(Ref::THIS)
|
||
});
|
||
}
|
||
|
||
|
||
virtual void
|
||
run (Arg)
|
||
{
|
||
Rec::Mutator target;
|
||
Rec& subject = target;
|
||
DiffApplicator<Rec::Mutator> application(target);
|
||
application.consume(populationDiff());
|
||
|
||
CHECK (!isnil (subject)); // nonempty -- content has been added
|
||
CHECK ("X" == subject.getType()); // type was set to "X"
|
||
CHECK (1 == subject.get("α").data.get<int>()); // has gotten our int attribute "α"
|
||
CHECK (2L == subject.get("β").data.get<int64_t>()); // ... the long attribute "β"
|
||
CHECK (3.45 == subject.get("γ").data.get<double>()); // ... and double attribute "γ"
|
||
auto scope = subject.scope(); // look into the scope contents...
|
||
CHECK ( *scope == CHILD_A); // there is CHILD_A
|
||
CHECK (*++scope == CHILD_T); // followed by a copy of CHILD_T
|
||
CHECK (*++scope == CHILD_T); // and another copy of CHILD_T
|
||
CHECK (*++scope == MakeRec().appendChild(CHILD_B) // and there is a nested Record
|
||
.appendChild(CHILD_A) // with CHILD_B
|
||
.genNode(SUB_NODE.idi.getSym())); // and CHILD_A
|
||
CHECK (isnil(++scope)); // thats all -- no more children
|
||
|
||
application.consume(mutationDiff());
|
||
CHECK (join (subject.keys()) == "α, β, γ"); // the attributes weren't altered
|
||
scope = subject.scope(); // but the scope was reordered
|
||
CHECK ( *scope == CHILD_T); // CHILD_T
|
||
CHECK (*++scope == CHILD_A); // CHILD_A
|
||
Rec nested = (++scope)->data.get<Rec>(); // and our nested Record, which too has been altered:
|
||
CHECK (nested.get("γ").data.get<double>() == 3.45); // it carries now an attribute "δ", which is again
|
||
CHECK (nested.get("δ").data.get<Rec>() == MakeRec().appendChild(CHILD_A) // a nested Record with three children CHILD_A
|
||
.appendChild(CHILD_A) //
|
||
.appendChild(CHILD_A) //
|
||
.genNode("δ")); //
|
||
auto subScope = nested.scope(); // and within the nested sub-scope we find
|
||
CHECK ( *subScope == CHILD_A); // CHILD_A
|
||
CHECK (*++subScope == MakeRec().type("Y") // a yet-again nested sub-Record of type "Y"
|
||
.set("β", 2L ) // with just an attribute "β" == 2L
|
||
.genNode(CHILD_NODE.idi.getSym())); // (and an empty child scope)
|
||
CHECK (*++subScope == CHILD_T); // followed by another copy of CHILD_T
|
||
CHECK (isnil (++subScope)); //
|
||
CHECK (isnil (++scope)); // and nothing beyond that.
|
||
}
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (DiffTreeApplication_test, "unit common");
|
||
|
||
|
||
|
||
}}} // namespace lib::diff::test
|