lumiera_/tests/library/diff/diff-tree-application-test.cpp
Ichthyostega 806db414dd Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
 * there is no entity "Lumiera.org" which holds any copyrights
 * Lumiera source code is provided under the GPL Version 2+

== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''

The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!

The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00

220 lines
11 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 main features of tree diff representation
Copyright (C)
2015, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** 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. See the file COPYING for further details.
* *****************************************************************/
/** @file diff-tree-application-test.cpp
** unit test \ref DiffTreeApplication_test.
** Demonstrates the most relevant operations for reshaping structured data
** through a tree-diff sequence. Here, we use a lib::diff:Record<GenNode>
** as target data, and the test focuses on demonstrating the possible operations.
** @see [Introductory demonstration](\ref diff-tree-application-simple-test.cpp)
** @see [More complex setup with opaque data](\ref diff-complex-application-test.cpp)
*/
#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 that
* the mutated data is indeed in the reordered shape.
* @remarks to follow this test, you should be familiar both with our
* [generic data record](\ref diff::Record), as well as with the
* [variant data node](\ref diff::GenNode). 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.
* @note literally the same test case is repeated in MutationMessage_test,
* just there the diff is transported in a MutationMessage capsule,
* as would be the case in the real application.
* @see DiffComplexApplication_test handling arbitrary data structures
* @see DiffTreeApplicationSimple_test introductory example demonstration
* @see GenericRecord_test
* @see GenNode_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()
{
// prepare for direct assignment of new value
// NOTE: the target ID will be reconstructed, including hash
GenNode childA_upper(CHILD_A.idi.getSym(), "A");
return snapshot({after(Ref::ATTRIBS) // fast forward to the first child
, find(CHILD_T)
, pick(CHILD_A)
, skip(CHILD_T)
, del(CHILD_T)
, after(Ref::END) // accept anything beyond as-is
, mut(SUB_NODE)
, 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)
, set(childA_upper) // direct assignment, target found by ID (out of order)
, 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(SUB_NODE)
});
}
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 has been altered by assignment
CHECK (CHILD_A.idi == subScope->idi); // ...: same ID as CHILD_A
CHECK ("A" == subScope->data.get<string>()); // ...: but mutated payload
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