Lumiera  0.pre.03
»edit your freedom«
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"
20 #include "lib/test/test-helper.hpp"
23 #include "lib/iter-adapter-stl.hpp"
24 #include "lib/time/timevalue.hpp"
25 #include "lib/format-util.hpp"
26 #include "lib/util.hpp"
27 
28 #include <string>
29 #include <vector>
30 
31 using LERR_(ITER_EXHAUST);
34 using lib::time::Time;
35 using util::contains;
36 using util::isnil;
37 using util::join;
38 using std::string;
39 using std::vector;
40 
41 
42 namespace lib {
43 namespace diff{
44 namespace 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
115  run (Arg)
116  {
118  verify_builder();
119  verify_diagnostics();
121  }
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
220  verify_builder()
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
281  verify_diagnostics()
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 
323  populationDiff()
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 
342  mutationDiff()
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 
429  LAUNCHER (MutationMessage_test, "unit common");
430 
431 
432 
433 }}} // namespace lib::diff::test
Concrete implementation to apply structural changes to hierarchical data structures.
Generic Message with an embedded diff, to describe changes to model elements.
Definition: run.hpp:40
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
iter_stl::IterSnapshot< VAL > snapshot(std::initializer_list< VAL > const &&ili)
Take a snapshot of the given std::initializer_list.
Opaque message to effect a structural change on a target, which is likewise only known in an abstract...
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:299
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:862
Simplistic test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
A collection of frequently used helper functions to support unit testing.
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:865
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
materialised iterator contents.
Preconfigured adapters for some STL container standard usage situations.
a family of time value like entities and their relationships.
object-like record of data.
Definition: record.hpp:141
Standard implementation of the IterSource interface: a wrapped "Lumiera Forward Iterator".
generic data element node within a tree
Definition: gen-node.hpp:222
bool contains(SEQ const &cont, typename SEQ::const_reference val)
shortcut for brute-force containment test in any sequential container
Definition: util.hpp:255
generic builder to apply a diff description to a given target data structure.