Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
mutation-message-test.cpp
Go to the documentation of this file.
1/*
2 MutationMessage(Test) - verify diff mutation message container
3
4 Copyright (C)
5 2017, 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
19#include "lib/test/run.hpp"
25#include "lib/format-util.hpp"
26#include "lib/util.hpp"
27
28#include <string>
29#include <vector>
30
31using LERR_(ITER_EXHAUST);
34using lib::time::Time;
35using util::contains;
36using util::isnil;
37using util::join;
38using std::string;
39using std::vector;
40
41
42namespace lib {
43namespace diff{
44namespace test{
45
46 namespace {//Test fixture....
47
48 int instances = 0;
49
50
51 // define some GenNode elements
52 // to act as templates within the concrete diff
53 // NOTE: everything in this diff language is by-value
54 const GenNode ATTRIB1("α", 1), // attribute α = 1
55 ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t)
56 ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
57 TYPE_X("type", "X"), // a "magic" type attribute "X"
58 TYPE_Y("type", "Y"), //
59 CHILD_A("a"), // unnamed string child node
60 CHILD_B('b'), // unnamed char child node
61 CHILD_T(Time(12,34,56,78)), // unnamed time value child
62 SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope
63 ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ
64 CHILD_NODE = SUB_NODE; // yet another child node, same ID as SUB_NODE (!)
65
66 }//(End)Test fixture
67
68
69
70
71
72
73
74
75
76 /***********************************************************************/
110 : public Test
112 {
113
114 virtual void
122
123
130 void
132 {
134
135 /* opaque generation context */
136 struct Generator
138 , Source
139 {
140 Generator()
141 : Source{snapshot({ins(TYPE_X)
142 ,set(ATTRIB1)
143 ,del(CHILD_T)})}
144 {
145 ++instances;
146 }
147
148 ~Generator()
149 {
150 --instances;
151 }
152 };
153
154
155 CHECK (0 == instances);
156 {
157 MutationMessage diffMsg{new Generator};
158 CHECK (!isnil (diffMsg));
159 CHECK (1 == instances);
160
161 CHECK (diffMsg);
162 CHECK (ins(TYPE_X) == *diffMsg);
163
164 // and this effectively means....
165 CHECK ("ins" == string(diffMsg->verb()));
166 CHECK ("type" == diffMsg->elm().idi.getSym());
167 CHECK ("X" == diffMsg->elm().data.get<string>());
168
169 // now iterate one step
170 ++diffMsg;
171 CHECK (diffMsg);
172 CHECK (set(ATTRIB1) == *diffMsg);
173 CHECK ("set" == string(diffMsg->verb()));
174 CHECK ("α" == diffMsg->elm().idi.getSym());
175 CHECK ( 1 == diffMsg->elm().data.get<int>());
176
177 // cloning is allowed, yet implementation defined
178 // in the actual case the underlying generator is based on a vector + a pointer
179 // and thus the full state can be cloned into an independent instance
180 MutationMessage clone{diffMsg};
181 CHECK (clone == diffMsg);
182 CHECK (set(ATTRIB1) == *clone);
183
184 CHECK (1 == instances); // the iterator front-end was cloned, not the generator
185
186 ++clone;
187 CHECK (del(CHILD_T) == *clone);
188 CHECK (set(ATTRIB1) == *diffMsg);
189 CHECK (clone != diffMsg);
190
191 ++clone;
192 CHECK (not clone);
193 CHECK (isnil (clone));
194 VERIFY_ERROR(ITER_EXHAUST, *clone);
195
196 // note the weird behaviour:
197 // both instances share a common backend and thus state get mixed up.
198 // The diffMsg front-End still points at a state already obsoleted
199 CHECK (set(ATTRIB1) == *diffMsg);
200 ++diffMsg;
201 // So better don't do this at home...
202 VERIFY_ERROR(ITER_EXHAUST, *diffMsg);
203
204 clone = MutationMessage{new Generator};
205 CHECK (2 == instances); // now we got two independent generator instances
206 CHECK (clone);
207 CHECK (ins(TYPE_X) == *clone);
208 ++clone;
209 CHECK (set(ATTRIB1) == *clone);
210
211 // first instance unaffected as before
212 CHECK (isnil (diffMsg));
213
214 }// NOTE: automatic clean-up when leaving the scope
215 CHECK (0 == instances);
216 }
217
218
219 void
221 {
222 MutationMessage diffMsg{ins(TYPE_X)
223 ,set(ATTRIB1)
224 ,del(CHILD_T)};
225
226 CHECK (!isnil (diffMsg));
227 CHECK (ins(TYPE_X) == *diffMsg);
228 CHECK (set(ATTRIB1) == *++diffMsg);
229 CHECK (del(CHILD_T) == *++diffMsg);
230 CHECK (isnil (++diffMsg));
231 VERIFY_ERROR(ITER_EXHAUST, *diffMsg);
232
233
234
235 // likewise works with a std::initializer_list
236 diffMsg = MutationMessage{{ins(TYPE_X)
237 ,set(ATTRIB1)
238 ,del(CHILD_T)}
239 };
240
241 CHECK (!isnil (diffMsg));
242 CHECK (ins(TYPE_X) == *diffMsg);
243 CHECK (set(ATTRIB1) == *++diffMsg);
244 CHECK (del(CHILD_T) == *++diffMsg);
245 CHECK (isnil (++diffMsg));
246
247
248
249 // even passing any suitable iterable works
250 diffMsg = MutationMessage{snapshot({ins(TYPE_X)
251 ,set(ATTRIB1)
252 ,del(CHILD_T)})
253 };
254
255 CHECK (!isnil (diffMsg));
256 CHECK (ins(TYPE_X) == *diffMsg);
257 CHECK (set(ATTRIB1) == *++diffMsg);
258 CHECK (del(CHILD_T) == *++diffMsg);
259 CHECK (isnil (++diffMsg));
260
261
262
263 // really anything iterable...
264 std::vector<DiffStep> steps;
265 CHECK (isnil (steps));
266 append_all(snapshot({ins(TYPE_X)
267 ,set(ATTRIB1)
268 ,del(CHILD_T)}), steps);
269
270 diffMsg = MutationMessage{steps};
271
272 CHECK (!isnil (diffMsg));
273 CHECK (ins(TYPE_X) == *diffMsg);
274 CHECK (set(ATTRIB1) == *++diffMsg);
275 CHECK (del(CHILD_T) == *++diffMsg);
276 CHECK (isnil (++diffMsg));
277 }
278
279
280 void
282 {
283 MutationMessage diffMsg{ins(TYPE_X)
284 ,set(ATTRIB1)
285 ,del(CHILD_T)};
286
287 // initially only the default diagnostics of IterSource is shown (rendering the element type)
288 CHECK (string(diffMsg) == "IterSource<DiffLanguage<TreeDiffInterpreter, GenNode>::DiffStep>");
289
290 // transparently take a snapshot
291 diffMsg.updateDiagnostics();
292
293 // now the whole sequence is rendered explicitly
294 string expectedRendering = join ({ins(TYPE_X), set(ATTRIB1), del(CHILD_T)});
295 CHECK (contains (string(diffMsg), expectedRendering));
296
297 CHECK (string(set(ATTRIB1)) == "set(GenNode-ID(\"α\")-DataCap|«int»|1)");
298
299 // and we can still iterate...
300 CHECK (!isnil (diffMsg));
301 CHECK (ins(TYPE_X) == *diffMsg);
302 CHECK (set(ATTRIB1) == *++diffMsg);
303
304 // NOTE: in fact only the remainder of the sequence is captured...
305 diffMsg.updateDiagnostics();
306 CHECK (not contains (string(diffMsg), string(ins(TYPE_X))));
307 CHECK ( contains (string(diffMsg), string(set(ATTRIB1))));
308 CHECK ( contains (string(diffMsg), string(del(CHILD_T))));
309
310 // and we can still continue to iterate...
311 CHECK (del(CHILD_T) == *++diffMsg);
312 CHECK (isnil (++diffMsg));
313
314 diffMsg.updateDiagnostics();
315 CHECK (string(diffMsg) == "Diff--{}");
316 }
317
318
319
320
321
324 {
325 return { ins(TYPE_X)
326 , ins(ATTRIB1)
327 , ins(ATTRIB2)
328 , ins(ATTRIB3)
329 , ins(CHILD_A)
330 , ins(CHILD_T)
331 , ins(CHILD_T)
332 , ins(SUB_NODE)
333 , mut(SUB_NODE)
334 , ins(CHILD_B)
335 , ins(CHILD_A)
336 , emu(SUB_NODE)
337 };
338 }
339
340
343 {
344 // prepare for direct assignment of new value
345 // NOTE: the target ID will be reconstructed, including hash
346 GenNode childA_upper(CHILD_A.idi.getSym(), "A");
347
348 return { after(Ref::ATTRIBS) // fast forward to the first child
349 , find(CHILD_T)
350 , pick(CHILD_A)
351 , skip(CHILD_T)
352 , del(CHILD_T)
353 , after(Ref::END) // accept anything beyond as-is
354 , mut(SUB_NODE)
355 , ins(ATTRIB3)
356 , ins(ATTRIB_NODE) // attributes can also be nested objects
357 , find(CHILD_A)
358 , del(CHILD_B)
359 , ins(CHILD_NODE)
360 , ins(CHILD_T)
361 , skip(CHILD_A)
362 , mut(CHILD_NODE)
363 , ins(TYPE_Y)
364 , ins(ATTRIB2)
365 , emu(CHILD_NODE)
366 , set(childA_upper) // direct assignment, target found by ID (out of order)
367 , mut(ATTRIB_NODE) // mutation can be out-of order, target found by ID
368 , ins(CHILD_A)
369 , ins(CHILD_A)
370 , ins(CHILD_A)
371 , emu(ATTRIB_NODE)
372 , emu(SUB_NODE)
373 };
374 }
375
378 void
380 {
381 Rec::Mutator target;
382 Rec& subject = target;
383 DiffApplicator<Rec::Mutator> application(target);
384
385 // Part I : apply diff to populate
386 application.consume (populationDiff());
387
388 CHECK (!isnil (subject)); // nonempty -- content has been added
389 CHECK ("X" == subject.getType()); // type was set to "X"
390 CHECK (1 == subject.get("α").data.get<int>()); // has gotten our int attribute "α"
391 CHECK (2L == subject.get("β").data.get<int64_t>()); // ... the long attribute "β"
392 CHECK (3.45 == subject.get("γ").data.get<double>()); // ... and double attribute "γ"
393 auto scope = subject.scope(); // look into the scope contents...
394 CHECK ( *scope == CHILD_A); // there is CHILD_A
395 CHECK (*++scope == CHILD_T); // followed by a copy of CHILD_T
396 CHECK (*++scope == CHILD_T); // and another copy of CHILD_T
397 CHECK (*++scope == MakeRec().appendChild(CHILD_B) // and there is a nested Record
398 .appendChild(CHILD_A) // with CHILD_B
399 .genNode(SUB_NODE.idi.getSym())); // and CHILD_A
400 CHECK (isnil(++scope)); // thats all -- no more children
401
402 // Part II : apply the second diff
403 application.consume (mutationDiff());
404 CHECK (join (subject.keys()) == "α, β, γ"); // the attributes weren't altered
405 scope = subject.scope(); // but the scope was reordered
406 CHECK ( *scope == CHILD_T); // CHILD_T
407 CHECK (*++scope == CHILD_A); // CHILD_A
408 Rec nested = (++scope)->data.get<Rec>(); // and our nested Record, which too has been altered:
409 CHECK (nested.get("γ").data.get<double>() == 3.45); // it carries now an attribute "δ", which is again
410 CHECK (nested.get("δ") == MakeRec().appendChild(CHILD_A) // a nested Record with three children CHILD_A
411 .appendChild(CHILD_A) //
412 .appendChild(CHILD_A) //
413 .genNode("δ")); //
414 auto subScope = nested.scope(); // and within the nested sub-scope we find
415 CHECK ( *subScope != CHILD_A); // CHILD_A has been altered by assignment
416 CHECK (CHILD_A.idi == subScope->idi); // ...: same ID as CHILD_A
417 CHECK ("A" == subScope->data.get<string>()); // ...: but mutated payload
418 CHECK (*++subScope == MakeRec().type("Y") // a yet-again nested sub-Record of type "Y"
419 .set("β", int64_t(2)) // with just an attribute "β" == 2L
420 .genNode(CHILD_NODE.idi.getSym())); // (and an empty child scope)
421 CHECK (*++subScope == CHILD_T); // followed by another copy of CHILD_T
422 CHECK (isnil (++subScope)); //
423 CHECK (isnil (++scope)); // and nothing beyond that.
424 }
425 };
426
427
430
431
432
433}}} // namespace lib::diff::test
Standard implementation of the IterSource interface: a wrapped "Lumiera Forward Iterator".
generic builder to apply a diff description to a given target data structure.
object-like record of data.
Definition record.hpp:142
Access get(string key) const
Definition record.hpp:245
scopeIter scope() const
Definition record.hpp:319
keyIter keys() const
Definition record.hpp:321
string getType() const
Definition record.hpp:227
string const & getSym() const
Definition entry-id.hpp:169
materialised iterator contents.
Lumiera's internal time value datatype.
#define LERR_(_NAME_)
Definition error.hpp:45
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Preconfigured adapters for some STL container standard usage situations.
Generic Message with an embedded diff, to describe changes to model elements.
Rec::Mutator MakeRec
Definition gen-node.hpp:135
ContentSnapshot< CON > snapshot(CON const &con)
Take a snapshot of the given STL compliant container.
Implementation namespace for support and library code.
void append_all(IT iter, CON &container)
Test runner and basic definitions for tests.
bool contains(MAP &map, typename MAP::key_type const &key)
shortcut for containment test on a map
Definition util.hpp:230
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
Opaque message to effect a structural change on a target, which is likewise only known in an abstract...
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
A collection of frequently used helper functions to support unit testing.
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
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...