LUMIERA.clone/tests/library/diff/diff-tree-application-test.cpp
Ichthyostega d68b881fab fix test failure due to compilation order (see #973)
some tests rely on additional diagnostics code being linked in,
which happens, when lib/format-util.hpp is included prior to
the instantiation of lib::diff::Record rsp. lib::Variant.

The reason why i opended this can of worms was to avoid includion
of this formatting and diagnostics code into such basic headers
as lib/variant.hpp or lib/diff/gen-node.hpp

Now it turns out, that on some platforms the linker will use
a later instantiation of lib::Variant::Buff<GenNode>::operator string
in spite of a complete instantiation of this virtual function
being available already in liblumierasupport.so

But the real reason is that -- with this trickery -- we're violating
the single definition rule, so we get what we deserved.

TODO (Ticket #973): at a later point in development we have to re-assess,
the precise impact of including lib/format-util.hpp into
lib/diff/gen-node.hpp
Right now I expect GenNode to be used pervasively, so I am
reluctant to make that header too heavyweight.
2015-11-15 02:11:08 +01:00

208 lines
9.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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/format-util.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("β", int64_t(2)), // 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 tree differences.
* The representation is given as a linearised sequence of verb tokens.
* In addition to the verbs used for list diffing, here we additionally
* have to deal with nested scopes, which can be entered thorough a
* bracketing construct \c mut(ID)...emu(ID).
* This test demonstrates the application of such diff sequences
* - in the first step, an empty root #Record<GenNode> is populated
* with a type-ID, three named attribute values, three child values
* and a nested child-Record.
* - the second step demonstrates various diff language constructs
* to alter, reshape and mutate this data structure
* After applying those two diff sequences, we verify the data
* is indeed in the expected shape.
* @remarks to follow this test, you should be familiar both with our
* \link diff::Record generic data record \endlink, as well as with
* the \link diff::GenNode variant data node \endlink. The key point
* to note is the usage of Record elements as payload within GenNode,
* which allows to represent tree shaped object like data structures.
* @see GenericRecordRepresentation_test
* @see GenNodeBasic_test
* @see DiffListApplication_test
* @see diff-tree-application.hpp
* @see tree-diff.hpp
*/
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) // attributes can also be nested objects
, 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) // mutation can be out-of order, target found by ID
, 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);
// Part I : apply diff to populate
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
// Part II : apply the second diff
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("δ") == 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("β", int64_t(2)) // 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