2017-08-11 15:23:33 +02:00
|
|
|
|
/*
|
|
|
|
|
|
DiffMessage(Test) - demonstrate the basics of tree diff representation
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
|
2017, 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.
|
|
|
|
|
|
|
|
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/** @file diff-message-test.cpp
|
|
|
|
|
|
** unit test \ref DiffMessage_test
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
2017-08-11 19:11:14 +02:00
|
|
|
|
#include "lib/test/test-helper.hpp"
|
2017-08-11 18:34:23 +02:00
|
|
|
|
#include "lib/diff/diff-message.hpp"
|
2017-08-11 15:23:33 +02:00
|
|
|
|
#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>
|
|
|
|
|
|
|
2017-08-11 19:11:14 +02:00
|
|
|
|
using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST;
|
2017-08-11 18:34:23 +02:00
|
|
|
|
using lib::iter_stl::IterSnapshot;
|
2017-08-11 15:23:33 +02:00
|
|
|
|
using lib::iter_stl::snapshot;
|
2017-08-12 14:48:35 +02:00
|
|
|
|
using lib::time::Time;
|
2017-08-12 14:33:26 +02:00
|
|
|
|
using util::contains;
|
2017-08-11 15:23:33 +02:00
|
|
|
|
using util::isnil;
|
|
|
|
|
|
using util::join;
|
|
|
|
|
|
using std::string;
|
|
|
|
|
|
using std::vector;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
namespace test{
|
|
|
|
|
|
|
|
|
|
|
|
namespace {//Test fixture....
|
|
|
|
|
|
|
2017-08-12 14:48:35 +02:00
|
|
|
|
int instances = 0; ///< verify instance management
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-11 15:23:33 +02:00
|
|
|
|
// 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 properties of a container to transport a diff from an abstracted
|
|
|
|
|
|
* source generator to an abstracted consumer.
|
|
|
|
|
|
* - this covers a standard usage scenario within Lumiera, where some
|
|
|
|
|
|
* producer in the Session core detects changes in session structure
|
|
|
|
|
|
* and sends a message to make the UI conform to the new structure
|
2017-08-11 15:48:28 +02:00
|
|
|
|
* - diff messages are hard to debug and test, since they are designed
|
|
|
|
|
|
* as opaque sequences to be consumed only once. Thus for we provide
|
|
|
|
|
|
* a snapshot decorator to offer diagnostic facilities
|
|
|
|
|
|
* - moreover we provide a simplified builder function to create
|
|
|
|
|
|
* hard wired diff messages in a concise way
|
|
|
|
|
|
* - and finally this test repeats the scenario of DiffTreeApplication_test,
|
|
|
|
|
|
* but this time the diff sequences are encapsulated as DiffMessage.
|
|
|
|
|
|
* @remarks like all the other _diff related_ tests, this code might be hard
|
|
|
|
|
|
* to follow, unless you're familiar with the underlying concepts. Basically,
|
|
|
|
|
|
* a _Diff_ is represented as _a linearised sequence of verb tokens_. Together
|
|
|
|
|
|
* these tokens form a _diff language_. The semantics of that language are
|
|
|
|
|
|
* oriented towards application of this diff onto a target structure. The
|
|
|
|
|
|
* goal is to represent structure changes without being bound to a specific
|
|
|
|
|
|
* data structure implementation. Yet there is one _meta_ data representation
|
|
|
|
|
|
* used within the diff itself, as well as for various test and demonstration
|
|
|
|
|
|
* examples: the [generic data record](\ref diff::Record) together with its
|
|
|
|
|
|
* [variant node element](\ref diff::GenNode). The key point to note is the
|
|
|
|
|
|
* (recursive) usage of Record elements as payload within GenNode, which
|
|
|
|
|
|
* allows to represent tree shaped object like data structures.
|
|
|
|
|
|
*
|
2017-08-11 15:23:33 +02:00
|
|
|
|
* @see AbstractTangible_test::mutate() concrete usage scenario for UI-elements
|
|
|
|
|
|
* @see DiffTreeApplication_test change a tree-like data structure by diff
|
|
|
|
|
|
* @see DiffComplexApplication_test handling arbitrary data structures
|
|
|
|
|
|
* @see DiffListApplication_test
|
|
|
|
|
|
* @see DiffMessage
|
|
|
|
|
|
* @see ui-bus.hpp
|
|
|
|
|
|
*/
|
|
|
|
|
|
class DiffMessage_test
|
|
|
|
|
|
: public Test
|
|
|
|
|
|
, TreeDiffLanguage
|
|
|
|
|
|
{
|
2017-08-11 15:48:28 +02:00
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
|
run (Arg)
|
|
|
|
|
|
{
|
2017-08-11 19:11:14 +02:00
|
|
|
|
demonstrate_standardUsage();
|
2017-08-11 15:48:28 +02:00
|
|
|
|
verify_builder();
|
2017-08-11 19:28:16 +02:00
|
|
|
|
verify_diagnostics();
|
2017-08-11 15:48:28 +02:00
|
|
|
|
demonstrate_treeApplication();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-11 18:34:23 +02:00
|
|
|
|
|
|
|
|
|
|
/** @test demonstrate the intended usage pattern
|
|
|
|
|
|
* - a diff generation context is allocated
|
|
|
|
|
|
* - the MutationMessage takes ownership
|
|
|
|
|
|
* - and exposes the generated diff sequence
|
|
|
|
|
|
* - which is pulled during iteration
|
|
|
|
|
|
*/
|
2017-08-11 15:48:28 +02:00
|
|
|
|
void
|
2017-08-11 19:11:14 +02:00
|
|
|
|
demonstrate_standardUsage()
|
2017-08-11 15:48:28 +02:00
|
|
|
|
{
|
2017-08-11 18:34:23 +02:00
|
|
|
|
using Source = WrappedLumieraIter<IterSnapshot<DiffStep>>;
|
|
|
|
|
|
|
|
|
|
|
|
/* opaque generation context */
|
|
|
|
|
|
struct Generator
|
|
|
|
|
|
: TreeDiffLanguage
|
|
|
|
|
|
, Source
|
|
|
|
|
|
{
|
|
|
|
|
|
Generator()
|
|
|
|
|
|
: Source{snapshot({ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)})}
|
|
|
|
|
|
{
|
|
|
|
|
|
++instances;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
~Generator()
|
|
|
|
|
|
{
|
|
|
|
|
|
--instances;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (0 == instances);
|
|
|
|
|
|
{
|
|
|
|
|
|
DiffMessage diffMsg{new Generator};
|
|
|
|
|
|
CHECK (!isnil (diffMsg));
|
|
|
|
|
|
CHECK (1 == instances);
|
|
|
|
|
|
|
2017-08-11 19:11:14 +02:00
|
|
|
|
CHECK (diffMsg);
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *diffMsg);
|
|
|
|
|
|
|
|
|
|
|
|
// and this effectively means....
|
|
|
|
|
|
CHECK ("ins" == string(diffMsg->verb()));
|
|
|
|
|
|
CHECK ("type" == diffMsg->elm().idi.getSym());
|
|
|
|
|
|
CHECK ("X" == diffMsg->elm().data.get<string>());
|
|
|
|
|
|
|
|
|
|
|
|
// now iterate one step
|
|
|
|
|
|
++diffMsg;
|
|
|
|
|
|
CHECK (diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *diffMsg);
|
|
|
|
|
|
CHECK ("set" == string(diffMsg->verb()));
|
|
|
|
|
|
CHECK ("α" == diffMsg->elm().idi.getSym());
|
|
|
|
|
|
CHECK ( 1 == diffMsg->elm().data.get<int>());
|
|
|
|
|
|
|
|
|
|
|
|
// cloning is allowed, yet implementation defined
|
|
|
|
|
|
// in the actual case the underlying generator is based on a vector + a pointer
|
|
|
|
|
|
// and thus the full state can be cloned into an independent instance
|
|
|
|
|
|
DiffMessage clone{diffMsg};
|
|
|
|
|
|
CHECK (clone == diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *clone);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (1 == instances); // the iterator front-end was cloned, not the generator
|
|
|
|
|
|
|
|
|
|
|
|
++clone;
|
|
|
|
|
|
CHECK (del(CHILD_T) == *clone);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *diffMsg);
|
|
|
|
|
|
CHECK (clone != diffMsg);
|
|
|
|
|
|
|
|
|
|
|
|
++clone;
|
|
|
|
|
|
CHECK (not clone);
|
|
|
|
|
|
CHECK (isnil (clone));
|
|
|
|
|
|
VERIFY_ERROR(ITER_EXHAUST, *clone);
|
|
|
|
|
|
|
|
|
|
|
|
// note the weird behaviour:
|
|
|
|
|
|
// both instances share a common backend and thus state get mixed up.
|
|
|
|
|
|
// The diffMsg front-End still points at a state already obsoleted
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *diffMsg);
|
|
|
|
|
|
++diffMsg;
|
|
|
|
|
|
// So better don't do this at home...
|
|
|
|
|
|
VERIFY_ERROR(ITER_EXHAUST, *diffMsg);
|
|
|
|
|
|
|
|
|
|
|
|
clone = DiffMessage{new Generator};
|
|
|
|
|
|
CHECK (2 == instances); // now we got two independent generator instances
|
|
|
|
|
|
CHECK (clone);
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *clone);
|
|
|
|
|
|
++clone;
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *clone);
|
|
|
|
|
|
|
|
|
|
|
|
// first instance unaffected as before
|
|
|
|
|
|
CHECK (isnil (diffMsg));
|
|
|
|
|
|
|
|
|
|
|
|
}// NOTE: automatic clean-up when leaving the scope
|
2017-08-11 18:34:23 +02:00
|
|
|
|
CHECK (0 == instances);
|
2017-08-11 15:48:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2017-08-11 19:28:16 +02:00
|
|
|
|
verify_builder()
|
2017-08-11 15:48:28 +02:00
|
|
|
|
{
|
2017-08-11 19:28:16 +02:00
|
|
|
|
DiffMessage diffMsg{ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)};
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (!isnil (diffMsg));
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *++diffMsg);
|
|
|
|
|
|
CHECK (del(CHILD_T) == *++diffMsg);
|
|
|
|
|
|
CHECK (isnil (++diffMsg));
|
|
|
|
|
|
VERIFY_ERROR(ITER_EXHAUST, *diffMsg);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// likewise works with a std::initializer_list
|
|
|
|
|
|
diffMsg = DiffMessage{{ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (!isnil (diffMsg));
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *++diffMsg);
|
|
|
|
|
|
CHECK (del(CHILD_T) == *++diffMsg);
|
|
|
|
|
|
CHECK (isnil (++diffMsg));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// even passing any suitable iterable works
|
|
|
|
|
|
diffMsg = DiffMessage{snapshot({ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)})
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (!isnil (diffMsg));
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *++diffMsg);
|
|
|
|
|
|
CHECK (del(CHILD_T) == *++diffMsg);
|
|
|
|
|
|
CHECK (isnil (++diffMsg));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// really anything iterable...
|
|
|
|
|
|
std::vector<DiffStep> steps;
|
|
|
|
|
|
CHECK (isnil (steps));
|
|
|
|
|
|
append_all(snapshot({ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)}), steps);
|
|
|
|
|
|
|
|
|
|
|
|
diffMsg = DiffMessage{steps};
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (!isnil (diffMsg));
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *++diffMsg);
|
|
|
|
|
|
CHECK (del(CHILD_T) == *++diffMsg);
|
|
|
|
|
|
CHECK (isnil (++diffMsg));
|
2017-08-11 15:48:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2017-08-11 19:28:16 +02:00
|
|
|
|
verify_diagnostics()
|
2017-08-11 15:48:28 +02:00
|
|
|
|
{
|
2017-08-12 04:04:22 +02:00
|
|
|
|
DiffMessage diffMsg{ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)};
|
|
|
|
|
|
|
2017-08-12 14:33:26 +02:00
|
|
|
|
// initially only the default diagnostics of IterSource is shown (rendering the element type)
|
|
|
|
|
|
CHECK (string(diffMsg) == "IterSource<DiffLanguage<TreeDiffInterpreter, GenNode>::DiffStep>");
|
2017-08-12 04:04:22 +02:00
|
|
|
|
|
2017-08-12 14:33:26 +02:00
|
|
|
|
// transparently take a snapshot
|
|
|
|
|
|
diffMsg.updateDiagnostics();
|
2017-08-12 04:04:22 +02:00
|
|
|
|
|
2017-08-12 14:33:26 +02:00
|
|
|
|
// now the whole sequence is rendered explicitly
|
|
|
|
|
|
string expectedRendering = join ({ins(TYPE_X), set(ATTRIB1), del(CHILD_T)});
|
|
|
|
|
|
CHECK (contains (string(diffMsg), expectedRendering));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (string(set(ATTRIB1)) == "set(GenNode-ID(\"α\")-DataCap|«int»|1)");
|
|
|
|
|
|
|
|
|
|
|
|
// and we can still iterate...
|
2017-08-12 04:04:22 +02:00
|
|
|
|
CHECK (!isnil (diffMsg));
|
|
|
|
|
|
CHECK (ins(TYPE_X) == *diffMsg);
|
|
|
|
|
|
CHECK (set(ATTRIB1) == *++diffMsg);
|
2017-08-12 14:33:26 +02:00
|
|
|
|
|
|
|
|
|
|
// NOTE: in fact only the remainder of the sequence is captured...
|
|
|
|
|
|
diffMsg.updateDiagnostics();
|
|
|
|
|
|
CHECK (not contains (string(diffMsg), string(ins(TYPE_X))));
|
|
|
|
|
|
CHECK ( contains (string(diffMsg), string(set(ATTRIB1))));
|
|
|
|
|
|
CHECK ( contains (string(diffMsg), string(del(CHILD_T))));
|
|
|
|
|
|
|
|
|
|
|
|
// and we can still continue to iterate...
|
2017-08-12 04:04:22 +02:00
|
|
|
|
CHECK (del(CHILD_T) == *++diffMsg);
|
|
|
|
|
|
CHECK (isnil (++diffMsg));
|
2017-08-12 14:33:26 +02:00
|
|
|
|
|
|
|
|
|
|
diffMsg.updateDiagnostics();
|
|
|
|
|
|
CHECK (string(diffMsg) == "Diff--{}");
|
2017-08-11 15:48:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
2017-08-12 14:48:35 +02:00
|
|
|
|
DiffMessage
|
2017-08-11 15:23:33 +02:00
|
|
|
|
populationDiff()
|
|
|
|
|
|
{
|
2017-08-12 14:48:35 +02:00
|
|
|
|
return { 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)
|
|
|
|
|
|
};
|
2017-08-11 15:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-12 14:48:35 +02:00
|
|
|
|
DiffMessage
|
2017-08-11 15:23:33 +02:00
|
|
|
|
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");
|
|
|
|
|
|
|
2017-08-12 14:48:35 +02:00
|
|
|
|
return { 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)
|
|
|
|
|
|
};
|
2017-08-11 15:23:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-08-12 14:48:35 +02:00
|
|
|
|
/** @test use DiffMessage to transport and apply changes to target data
|
|
|
|
|
|
* @remarks this almost literally repeats the DiffTreeApplication_test */
|
2017-08-11 15:48:28 +02:00
|
|
|
|
void
|
|
|
|
|
|
demonstrate_treeApplication()
|
2017-08-11 15:23:33 +02:00
|
|
|
|
{
|
|
|
|
|
|
Rec::Mutator target;
|
|
|
|
|
|
Rec& subject = target;
|
|
|
|
|
|
DiffApplicator<Rec::Mutator> application(target);
|
|
|
|
|
|
|
|
|
|
|
|
// Part I : apply diff to populate
|
2017-08-12 14:48:35 +02:00
|
|
|
|
application.consume (populationDiff());
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
|
|
|
|
|
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
|
2017-08-12 14:48:35 +02:00
|
|
|
|
application.consume (mutationDiff());
|
2017-08-11 15:23:33 +02:00
|
|
|
|
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
|
2017-08-12 14:48:35 +02:00
|
|
|
|
CHECK ( *subScope != CHILD_A); // CHILD_A has been altered by assignment
|
2017-08-11 15:23:33 +02:00
|
|
|
|
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 (DiffMessage_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}} // namespace lib::diff::test
|