2017-08-11 15:23:33 +02:00
|
|
|
|
/*
|
2018-10-15 00:54:10 +02:00
|
|
|
|
MutationMessage(Test) - verify diff mutation message container
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
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
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2017, Hermann Vosseler <Ichthyostega@web.de>
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
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
|
|
|
|
**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.
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
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
|
|
|
|
* *****************************************************************/
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
2017-08-13 07:09:06 +02:00
|
|
|
|
/** @file mutation-message-test.cpp
|
|
|
|
|
|
** unit test \ref MutationMessage_test
|
2017-08-11 15:23:33 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
2017-08-11 19:11:14 +02:00
|
|
|
|
#include "lib/test/test-helper.hpp"
|
2017-08-13 07:09:06 +02:00
|
|
|
|
#include "lib/diff/mutation-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>
|
|
|
|
|
|
|
2024-03-16 02:04:47 +01:00
|
|
|
|
using LERR_(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,
|
2017-08-13 07:09:06 +02:00
|
|
|
|
* but this time the diff sequences are encapsulated as MutationMessage.
|
2017-08-11 15:48:28 +02:00
|
|
|
|
* @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
|
2017-08-13 07:09:06 +02:00
|
|
|
|
* @see MutationMessage
|
2017-08-11 15:23:33 +02:00
|
|
|
|
* @see ui-bus.hpp
|
|
|
|
|
|
*/
|
2017-08-13 07:09:06 +02:00
|
|
|
|
class MutationMessage_test
|
2017-08-11 15:23:33 +02:00
|
|
|
|
: 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);
|
|
|
|
|
|
{
|
2017-08-13 07:09:06 +02:00
|
|
|
|
MutationMessage diffMsg{new Generator};
|
2017-08-11 18:34:23 +02:00
|
|
|
|
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
|
2017-08-13 07:09:06 +02:00
|
|
|
|
MutationMessage clone{diffMsg};
|
2017-08-11 19:11:14 +02:00
|
|
|
|
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);
|
|
|
|
|
|
|
2017-08-13 07:09:06 +02:00
|
|
|
|
clone = MutationMessage{new Generator};
|
2017-08-11 19:11:14 +02:00
|
|
|
|
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-13 07:09:06 +02:00
|
|
|
|
MutationMessage diffMsg{ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)};
|
2017-08-11 19:28:16 +02:00
|
|
|
|
|
|
|
|
|
|
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
|
2017-08-13 07:09:06 +02:00
|
|
|
|
diffMsg = MutationMessage{{ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)}
|
|
|
|
|
|
};
|
2017-08-11 19:28:16 +02:00
|
|
|
|
|
|
|
|
|
|
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
|
2017-08-13 07:09:06 +02:00
|
|
|
|
diffMsg = MutationMessage{snapshot({ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)})
|
|
|
|
|
|
};
|
2017-08-11 19:28:16 +02:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
2017-08-13 07:09:06 +02:00
|
|
|
|
diffMsg = MutationMessage{steps};
|
2017-08-11 19:28:16 +02:00
|
|
|
|
|
|
|
|
|
|
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-13 07:09:06 +02:00
|
|
|
|
MutationMessage diffMsg{ins(TYPE_X)
|
|
|
|
|
|
,set(ATTRIB1)
|
|
|
|
|
|
,del(CHILD_T)};
|
2017-08-12 04:04:22 +02:00
|
|
|
|
|
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-13 07:09:06 +02:00
|
|
|
|
MutationMessage
|
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-13 07:09:06 +02:00
|
|
|
|
MutationMessage
|
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-13 07:09:06 +02:00
|
|
|
|
/** @test use MutationMessage to transport and apply changes to target data
|
2017-08-12 14:48:35 +02:00
|
|
|
|
* @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
|
2025-06-07 23:59:57 +02:00
|
|
|
|
CHECK (*++scope == MakeRec().appendChild(CHILD_B) // and there is a nested Record
|
2017-08-11 15:23:33 +02:00
|
|
|
|
.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());
|
2025-06-07 23:59:57 +02:00
|
|
|
|
CHECK (join (subject.keys()) == "α, β, γ"); // the attributes weren't altered
|
2017-08-11 15:23:33 +02:00
|
|
|
|
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
|
2025-06-07 23:59:57 +02:00
|
|
|
|
.appendChild(CHILD_A) //
|
|
|
|
|
|
.appendChild(CHILD_A) //
|
|
|
|
|
|
.genNode("δ")); //
|
2017-08-11 15:23:33 +02:00
|
|
|
|
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
|
2025-06-07 23:59:57 +02:00
|
|
|
|
CHECK (isnil (++subScope)); //
|
2017-08-11 15:23:33 +02:00
|
|
|
|
CHECK (isnil (++scope)); // and nothing beyond that.
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
2017-08-13 07:09:06 +02:00
|
|
|
|
LAUNCHER (MutationMessage_test, "unit common");
|
2017-08-11 15:23:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}} // namespace lib::diff::test
|