Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
diff-complex-application-test.cpp
Go to the documentation of this file.
1/*
2 DiffComplexApplication(Test) - apply structural changes to unspecific private data structures
3
4 Copyright (C)
5 2016, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
24#include "lib/test/run.hpp"
25#include "lib/format-util.hpp"
30#include "lib/format-string.hpp"
31#include "lib/format-cout.hpp"
32#include "lib/util.hpp"
33
34#include <string>
35#include <vector>
36#include <memory>
37
38using util::isnil;
39using util::join;
40using util::_Fmt;
41using util::BOTTOM_INDICATOR;
43using lib::time::Time;
44using std::unique_ptr;
45using std::string;
46using std::vector;
47
48
49namespace lib {
50namespace diff{
51namespace test{
52
53 namespace {//Test fixture....
54
55 // define some GenNode elements
56 // to act as templates within the concrete diff
57 // NOTE: everything in this diff language is by-value
58 const GenNode ATTRIB1("α", 1), // attribute α = 1
59 ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t)
60 ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
61 TYPE_X("type", "ξ"), // a "magic" type attribute "Xi"
62 TYPE_Z("type", "ζ"), //
63 CHILD_A("a"), // unnamed string child node
64 CHILD_B('b'), // unnamed char child node
65 CHILD_T(Time(12,34,56,78)), // unnamed time value child
66 SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope
67 ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ
68 GAMMA_PI("γ", 3.14159265); // happens to have the same identity (ID) as ATTRIB3
69
70
77 class Opaque
78 {
80 string type_ = Rec::TYPE_NIL;
81
82 int alpha_ = -1;
83 int64_t beta_ = -1;
84 double gamma_ = -1;
85
86 unique_ptr<Opaque> delta_;
87
88 vector<Opaque> nestedObj_;
89 vector<string> nestedData_;
90
91 public:
93 : key_(idi::EntryID<Opaque>())
94 { }
95
96 explicit
97 Opaque (string keyID)
98 : key_(idi::EntryID<Opaque>(keyID))
99 { }
100
101 explicit
103 : key_(id)
104 { }
105
106 Opaque (Opaque const& o)
107 : key_(o.key_)
108 , type_(o.type_)
109 , alpha_(o.alpha_)
110 , beta_(o.beta_)
111 , gamma_(o.gamma_)
112 , delta_()
113 , nestedObj_(o.nestedObj_)
114 , nestedData_(o.nestedData_)
115 {
116 if (o.delta_)
117 delta_.reset(new Opaque(*o.delta_));
118 }
119
120 Opaque&
121 operator= (Opaque const& o)
122 {
123 if (&o != this)
124 {
125 Opaque tmp(o);
126 swap (*this, tmp);
127 }
128 return *this;
129 }
130
131 bool verifyType(string x) const { return x == type_; }
132 bool verifyAlpha(int x) const { return x == alpha_;}
133 bool verifyBeta(int64_t x) const { return x == beta_; }
134 bool verifyGamma(double x) const { return x == gamma_;}
135 bool verifyData(string desc) const { return desc == join(nestedData_); }
136 const Opaque* nestedDelta() const { return not delta_? NULL : delta_.get(); }
137 const Opaque* nestedObj_1() const { return isnil(nestedObj_)? NULL : &nestedObj_[0]; }
138
139
140 operator string() const
141 {
142 return _Fmt{"%s__(α:%d β:%s γ:%7.5f δ:%s\n......|nested:%s\n......|data:%s\n )__END_%s"}
143 % identity()
144 % alpha_
145 % beta_
146 % gamma_
147 % delta_
148 % join (nestedObj_, "\n......|")
149 % join (nestedData_)
150 % identity()
151 ;
152 }
153
154 string
155 identity() const
156 {
157 string symbol = key_.getSym() + (isTyped()? "≺"+type_+"≻" : "");
158 return lib::idi::format::instance_hex_format(symbol, key_.getHash());
159 }
160
161 bool
162 isTyped() const
163 {
164 return Rec::TYPE_NIL != type_;
165 }
166
167
192 void
194 {
195 buff.emplace (
196 TreeMutator::build()
197 .attach (collection(nestedData_)
198 .isApplicableIf ([&](GenNode const& spec) -> bool
199 {
200 return not spec.isNamed(); // »Selector« : accept anything unnamed value-like
201 })
202 .matchElement ([&](GenNode const& spec, string const& elm) -> bool
203 {
204 return elm == render(spec.data); // »Matcher« : does the diff verb #spec apply to this object?
205 })
206 .constructFrom ([&](GenNode const& spec) -> string
207 {
208 return render (spec.data); // »Constructor« : build a new child entity to reflect the given diff #spec
209 })
210 .assignElement ([&](string& target, GenNode const& spec) -> bool
211 {
212 target = render (spec.data); // »Assigner« : treat this object as value and assign data from the #spec payload
213 return true;
214 }))
215 .attach (collection(nestedObj_)
216 .isApplicableIf ([&](GenNode const& spec) -> bool
217 {
218 return spec.data.isNested(); // »Selector« : require object-like sub scope
219 })
220 .matchElement ([&](GenNode const& spec, Opaque const& elm) -> bool
221 {
222 return spec.idi == elm.key_;
223 })
224 .constructFrom ([&](GenNode const& spec) -> Opaque
225 {
226 return Opaque{spec.idi};
227 })
228 .buildChildMutator ([&](Opaque& target, GenNode::ID const&, TreeMutator::Handle buff) -> bool
229 {
230 target.buildMutator (buff); // »Recursive Mutator« : delegate to child for building a nested TreeMutator
231 return true;
232 }))
233 .change("type", [&](string typeID)
234 {
235 type_ = typeID;
236 })
237 .change("α", [&](int val)
238 {
239 alpha_ = val;
240 })
241 .change("β", [&](int64_t val)
242 {
243 beta_ = val;
244 })
245 .change("γ", [&](double val)
246 {
247 gamma_ = val;
248 })
249 .mutateAttrib("δ", [&](TreeMutator::Handle buff)
250 {
251 if (not delta_) // note: object is managed automatically,
252 delta_.reset (new Opaque("δ")); // thus no INS-implementation necessary
253 REQUIRE (delta_);
254
255 delta_->buildMutator(buff);
256 }));
257 }
258
263 friend constexpr size_t
265 {
266 return 430;
267 }
268 };
269
270 }//(End)Test fixture
271
272
273
274
275
276
277
278 /***********************************************************************/
298 : public Test
300 {
302
303 DiffSeq
305 {
306 return snapshot({ins(ATTRIB1)
307 , ins(ATTRIB3)
308 , ins(ATTRIB3)
309 , ins(CHILD_B)
310 , ins(CHILD_B)
311 , ins(CHILD_T)
312 });
313 } // ==> ATTRIB1, ATTRIB3, (ATTRIB3), CHILD_B, CHILD_B, CHILD_T
314
315 DiffSeq
317 {
318 return snapshot({after(Ref::ATTRIBS)
319 , ins(ATTRIB2)
320 , del(CHILD_B)
321 , ins(SUB_NODE)
322 , find(CHILD_T)
323 , pick(CHILD_B)
324 , skip(CHILD_T)
325 });
326 } // ==> ATTRIB1, ATTRIB3, (ATTRIB3), ATTRIB2, SUB_NODE, CHILD_T, CHILD_B
327
328 DiffSeq
330 {
331 return snapshot({after(CHILD_B)
332 , after(Ref::END)
333 , set(GAMMA_PI)
334 , mut(SUB_NODE)
335 , ins(TYPE_X)
336 , ins(ATTRIB2)
337 , ins(CHILD_B)
338 , ins(CHILD_A)
339 , emu(SUB_NODE)
340 , ins(ATTRIB_NODE)
341 , mut(ATTRIB_NODE)
342 , ins(TYPE_Z)
343 , ins(CHILD_A)
344 , ins(CHILD_A)
345 , ins(CHILD_A)
346 , emu(ATTRIB_NODE)
347 });
348 } // ==> ATTRIB1, ATTRIB3 := π, (ATTRIB3), ATTRIB2,
349 // ATTRIB_NODE{ type ζ, CHILD_A, CHILD_A, CHILD_A }
350 // SUB_NODE{ type ξ, ATTRIB2, CHILD_B, CHILD_A },
351 // CHILD_T, CHILD_B
352
353
354
355
356 virtual void
357 run (Arg)
358 {
359 Opaque subject;
360 DiffApplicator<Opaque> application(subject);
361 //
362 cout << "before..."<<endl << subject<<endl;
363 CHECK (subject.verifyAlpha(-1));
364 CHECK (subject.verifyBeta(-1));
365 CHECK (subject.verifyGamma(-1));
366 CHECK (not subject.nestedDelta());
367 CHECK (not subject.nestedObj_1());
368 CHECK (subject.verifyData(""));
369
370
371 // Part I : apply attribute changes
372 application.consume(populationDiff());
373 //
374 cout << "after...I"<<endl << subject<<endl;
375 // ==> ATTRIB1, ATTRIB3, (ATTRIB3), CHILD_B, CHILD_B, CHILD_T
376 CHECK (subject.verifyAlpha(1));
377 CHECK (subject.verifyGamma(ATTRIB3.data.get<double>()));
378 CHECK (subject.verifyData("b, b, 78:56:34.012"));
379 // unchanged...
380 CHECK (subject.verifyBeta(-1));
381 CHECK (not subject.nestedDelta());
382 CHECK (not subject.nestedObj_1());
383
384
385 // Part II : apply child population
386 application.consume(reorderingDiff());
387 //
388 cout << "after...II"<<endl << subject<<endl;
389 // ==> ATTRIB1, ATTRIB3, (ATTRIB3), ATTRIB2, SUB_NODE, CHILD_T, CHILD_B
390 CHECK (subject.verifyAlpha(1));
391 CHECK (subject.verifyBeta (2)); // attribute β has been set
392 CHECK (subject.verifyGamma(3.45));
393 CHECK (subject.verifyData("78:56:34.012, b")); // one child deleted, the other ones re-ordered
394 CHECK (subject.nestedObj_1()); // plus inserted a nested child object
395 CHECK (subject.nestedObj_1()->verifyType(Rec::TYPE_NIL));
396 CHECK (subject.nestedObj_1()->verifyBeta(-1)); // ...which is empty (default constructed)
397 CHECK (subject.nestedObj_1()->verifyData(""));
398
399
400 // Part III : apply child mutations
401 application.consume(mutationDiff());
402 //
403 cout << "after...III"<<endl << subject<<endl;
404 // ==> ATTRIB1, ATTRIB3 := π, (ATTRIB3), ATTRIB2,
405 // ATTRIB_NODE{ type ζ, CHILD_A, CHILD_A, CHILD_A }
406 // SUB_NODE{ type ξ, ATTRIB2, CHILD_B, CHILD_A },
407 // CHILD_T, CHILD_B
408 CHECK (subject.verifyAlpha(1));
409 CHECK (subject.verifyBeta (2));
410 CHECK (subject.verifyGamma(GAMMA_PI.data.get<double>())); // new value assigned to attribute γ
411 CHECK (subject.nestedDelta()); // attribute δ (object valued) is now present
412 CHECK (subject.nestedDelta()->verifyType("ζ")); // ...and has an explicitly defined type field
413 CHECK (subject.nestedDelta()->verifyData("a, a, a"));//...plus three similar child values
414 CHECK (subject.verifyData("78:56:34.012, b")); // the child values weren't altered
415 CHECK (subject.nestedObj_1()->verifyType("ξ")); // but the nested child object's type has been set
416 CHECK (subject.nestedObj_1()->verifyBeta(2)); // ...and the attribute β has been set on the nested object
417 CHECK (subject.nestedObj_1()->verifyData("b, a")); // ...plus some child values where added here
418 }
419 };
420
421
424
425
426
427}}} // namespace lib::diff::test
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
bool isNested() const
determine if payload constitutes a nested scope ("object")
Definition gen-node.hpp:769
generic builder to apply a diff description to a given target data structure.
static const string TYPE_NIL
Definition record.hpp:153
void buildMutator(TreeMutator::Handle buff)
the only way this opaque object exposes itself for mutation through diff messages.
friend constexpr size_t treeMutatorSize(const Opaque *)
override default size traits to allow for sufficient buffer, able to hold the mutator defined above.
type erased baseclass for building a combined hash and symbolic ID.
Definition entry-id.hpp:134
LuidH const & getHash() const
Definition entry-id.hpp:175
string const & getSym() const
Definition entry-id.hpp:169
materialised iterator contents.
Lumiera's internal time value datatype.
A front-end for using printf-style formatting.
Automatically use custom string conversion in C++ stream output.
Front-end for printf-style string template interpolation.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Preconfigured adapters for some STL container standard usage situations.
return NULL
Definition llist.h:586
Rec::Mutator MakeRec
Definition gen-node.hpp:135
string instance_hex_format(string const &prefix, size_t instanceNr)
Definition genfunc.cpp:47
ContentSnapshot< CON > snapshot(CON const &con)
Take a snapshot of the given STL compliant container.
Implementation namespace for support and library code.
Test runner and basic definitions for tests.
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
generic data element node within a tree
Definition gen-node.hpp:224
bool isNamed() const
Definition gen-node.hpp:337
static const Ref END
symbolic ID ref "_END_"
Definition gen-node.hpp:863
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition gen-node.hpp:866
Diagnostic helper for unit tests regarding mutation of custom data.
a family of time value like entities and their relationships.
Concrete implementation to apply structural changes to hierarchical data structures.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...