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) Lumiera.org
5  2017, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
29 #include "lib/test/test-helper.hpp"
32 #include "lib/iter-adapter-stl.hpp"
33 #include "lib/time/timevalue.hpp"
34 #include "lib/format-util.hpp"
35 #include "lib/util.hpp"
36 
37 #include <string>
38 #include <vector>
39 
40 using LERR_(ITER_EXHAUST);
43 using lib::time::Time;
44 using util::contains;
45 using util::isnil;
46 using util::join;
47 using std::string;
48 using std::vector;
49 
50 
51 namespace lib {
52 namespace diff{
53 namespace test{
54 
55  namespace {//Test fixture....
56 
57  int instances = 0;
58 
59 
60  // define some GenNode elements
61  // to act as templates within the concrete diff
62  // NOTE: everything in this diff language is by-value
63  const GenNode ATTRIB1("α", 1), // attribute α = 1
64  ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t)
65  ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
66  TYPE_X("type", "X"), // a "magic" type attribute "X"
67  TYPE_Y("type", "Y"), //
68  CHILD_A("a"), // unnamed string child node
69  CHILD_B('b'), // unnamed char child node
70  CHILD_T(Time(12,34,56,78)), // unnamed time value child
71  SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope
72  ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ
73  CHILD_NODE = SUB_NODE; // yet another child node, same ID as SUB_NODE (!)
74 
75  }//(End)Test fixture
76 
77 
78 
79 
80 
81 
82 
83 
84 
85  /***********************************************************************/
119  : public Test
121  {
122 
123  virtual void
124  run (Arg)
125  {
127  verify_builder();
128  verify_diagnostics();
130  }
131 
132 
139  void
141  {
143 
144  /* opaque generation context */
145  struct Generator
147  , Source
148  {
149  Generator()
150  : Source{snapshot({ins(TYPE_X)
151  ,set(ATTRIB1)
152  ,del(CHILD_T)})}
153  {
154  ++instances;
155  }
156 
157  ~Generator()
158  {
159  --instances;
160  }
161  };
162 
163 
164  CHECK (0 == instances);
165  {
166  MutationMessage diffMsg{new Generator};
167  CHECK (!isnil (diffMsg));
168  CHECK (1 == instances);
169 
170  CHECK (diffMsg);
171  CHECK (ins(TYPE_X) == *diffMsg);
172 
173  // and this effectively means....
174  CHECK ("ins" == string(diffMsg->verb()));
175  CHECK ("type" == diffMsg->elm().idi.getSym());
176  CHECK ("X" == diffMsg->elm().data.get<string>());
177 
178  // now iterate one step
179  ++diffMsg;
180  CHECK (diffMsg);
181  CHECK (set(ATTRIB1) == *diffMsg);
182  CHECK ("set" == string(diffMsg->verb()));
183  CHECK ("α" == diffMsg->elm().idi.getSym());
184  CHECK ( 1 == diffMsg->elm().data.get<int>());
185 
186  // cloning is allowed, yet implementation defined
187  // in the actual case the underlying generator is based on a vector + a pointer
188  // and thus the full state can be cloned into an independent instance
189  MutationMessage clone{diffMsg};
190  CHECK (clone == diffMsg);
191  CHECK (set(ATTRIB1) == *clone);
192 
193  CHECK (1 == instances); // the iterator front-end was cloned, not the generator
194 
195  ++clone;
196  CHECK (del(CHILD_T) == *clone);
197  CHECK (set(ATTRIB1) == *diffMsg);
198  CHECK (clone != diffMsg);
199 
200  ++clone;
201  CHECK (not clone);
202  CHECK (isnil (clone));
203  VERIFY_ERROR(ITER_EXHAUST, *clone);
204 
205  // note the weird behaviour:
206  // both instances share a common backend and thus state get mixed up.
207  // The diffMsg front-End still points at a state already obsoleted
208  CHECK (set(ATTRIB1) == *diffMsg);
209  ++diffMsg;
210  // So better don't do this at home...
211  VERIFY_ERROR(ITER_EXHAUST, *diffMsg);
212 
213  clone = MutationMessage{new Generator};
214  CHECK (2 == instances); // now we got two independent generator instances
215  CHECK (clone);
216  CHECK (ins(TYPE_X) == *clone);
217  ++clone;
218  CHECK (set(ATTRIB1) == *clone);
219 
220  // first instance unaffected as before
221  CHECK (isnil (diffMsg));
222 
223  }// NOTE: automatic clean-up when leaving the scope
224  CHECK (0 == instances);
225  }
226 
227 
228  void
229  verify_builder()
230  {
231  MutationMessage diffMsg{ins(TYPE_X)
232  ,set(ATTRIB1)
233  ,del(CHILD_T)};
234 
235  CHECK (!isnil (diffMsg));
236  CHECK (ins(TYPE_X) == *diffMsg);
237  CHECK (set(ATTRIB1) == *++diffMsg);
238  CHECK (del(CHILD_T) == *++diffMsg);
239  CHECK (isnil (++diffMsg));
240  VERIFY_ERROR(ITER_EXHAUST, *diffMsg);
241 
242 
243 
244  // likewise works with a std::initializer_list
245  diffMsg = MutationMessage{{ins(TYPE_X)
246  ,set(ATTRIB1)
247  ,del(CHILD_T)}
248  };
249 
250  CHECK (!isnil (diffMsg));
251  CHECK (ins(TYPE_X) == *diffMsg);
252  CHECK (set(ATTRIB1) == *++diffMsg);
253  CHECK (del(CHILD_T) == *++diffMsg);
254  CHECK (isnil (++diffMsg));
255 
256 
257 
258  // even passing any suitable iterable works
259  diffMsg = MutationMessage{snapshot({ins(TYPE_X)
260  ,set(ATTRIB1)
261  ,del(CHILD_T)})
262  };
263 
264  CHECK (!isnil (diffMsg));
265  CHECK (ins(TYPE_X) == *diffMsg);
266  CHECK (set(ATTRIB1) == *++diffMsg);
267  CHECK (del(CHILD_T) == *++diffMsg);
268  CHECK (isnil (++diffMsg));
269 
270 
271 
272  // really anything iterable...
273  std::vector<DiffStep> steps;
274  CHECK (isnil (steps));
275  append_all(snapshot({ins(TYPE_X)
276  ,set(ATTRIB1)
277  ,del(CHILD_T)}), steps);
278 
279  diffMsg = MutationMessage{steps};
280 
281  CHECK (!isnil (diffMsg));
282  CHECK (ins(TYPE_X) == *diffMsg);
283  CHECK (set(ATTRIB1) == *++diffMsg);
284  CHECK (del(CHILD_T) == *++diffMsg);
285  CHECK (isnil (++diffMsg));
286  }
287 
288 
289  void
290  verify_diagnostics()
291  {
292  MutationMessage diffMsg{ins(TYPE_X)
293  ,set(ATTRIB1)
294  ,del(CHILD_T)};
295 
296  // initially only the default diagnostics of IterSource is shown (rendering the element type)
297  CHECK (string(diffMsg) == "IterSource<DiffLanguage<TreeDiffInterpreter, GenNode>::DiffStep>");
298 
299  // transparently take a snapshot
300  diffMsg.updateDiagnostics();
301 
302  // now the whole sequence is rendered explicitly
303  string expectedRendering = join ({ins(TYPE_X), set(ATTRIB1), del(CHILD_T)});
304  CHECK (contains (string(diffMsg), expectedRendering));
305 
306  CHECK (string(set(ATTRIB1)) == "set(GenNode-ID(\"α\")-DataCap|«int»|1)");
307 
308  // and we can still iterate...
309  CHECK (!isnil (diffMsg));
310  CHECK (ins(TYPE_X) == *diffMsg);
311  CHECK (set(ATTRIB1) == *++diffMsg);
312 
313  // NOTE: in fact only the remainder of the sequence is captured...
314  diffMsg.updateDiagnostics();
315  CHECK (not contains (string(diffMsg), string(ins(TYPE_X))));
316  CHECK ( contains (string(diffMsg), string(set(ATTRIB1))));
317  CHECK ( contains (string(diffMsg), string(del(CHILD_T))));
318 
319  // and we can still continue to iterate...
320  CHECK (del(CHILD_T) == *++diffMsg);
321  CHECK (isnil (++diffMsg));
322 
323  diffMsg.updateDiagnostics();
324  CHECK (string(diffMsg) == "Diff--{}");
325  }
326 
327 
328 
329 
330 
332  populationDiff()
333  {
334  return { ins(TYPE_X)
335  , ins(ATTRIB1)
336  , ins(ATTRIB2)
337  , ins(ATTRIB3)
338  , ins(CHILD_A)
339  , ins(CHILD_T)
340  , ins(CHILD_T)
341  , ins(SUB_NODE)
342  , mut(SUB_NODE)
343  , ins(CHILD_B)
344  , ins(CHILD_A)
345  , emu(SUB_NODE)
346  };
347  }
348 
349 
351  mutationDiff()
352  {
353  // prepare for direct assignment of new value
354  // NOTE: the target ID will be reconstructed, including hash
355  GenNode childA_upper(CHILD_A.idi.getSym(), "A");
356 
357  return { after(Ref::ATTRIBS) // fast forward to the first child
358  , find(CHILD_T)
359  , pick(CHILD_A)
360  , skip(CHILD_T)
361  , del(CHILD_T)
362  , after(Ref::END) // accept anything beyond as-is
363  , mut(SUB_NODE)
364  , ins(ATTRIB3)
365  , ins(ATTRIB_NODE) // attributes can also be nested objects
366  , find(CHILD_A)
367  , del(CHILD_B)
368  , ins(CHILD_NODE)
369  , ins(CHILD_T)
370  , skip(CHILD_A)
371  , mut(CHILD_NODE)
372  , ins(TYPE_Y)
373  , ins(ATTRIB2)
374  , emu(CHILD_NODE)
375  , set(childA_upper) // direct assignment, target found by ID (out of order)
376  , mut(ATTRIB_NODE) // mutation can be out-of order, target found by ID
377  , ins(CHILD_A)
378  , ins(CHILD_A)
379  , ins(CHILD_A)
380  , emu(ATTRIB_NODE)
381  , emu(SUB_NODE)
382  };
383  }
384 
387  void
389  {
390  Rec::Mutator target;
391  Rec& subject = target;
392  DiffApplicator<Rec::Mutator> application(target);
393 
394  // Part I : apply diff to populate
395  application.consume (populationDiff());
396 
397  CHECK (!isnil (subject)); // nonempty -- content has been added
398  CHECK ("X" == subject.getType()); // type was set to "X"
399  CHECK (1 == subject.get("α").data.get<int>()); // has gotten our int attribute "α"
400  CHECK (2L == subject.get("β").data.get<int64_t>()); // ... the long attribute "β"
401  CHECK (3.45 == subject.get("γ").data.get<double>()); // ... and double attribute "γ"
402  auto scope = subject.scope(); // look into the scope contents...
403  CHECK ( *scope == CHILD_A); // there is CHILD_A
404  CHECK (*++scope == CHILD_T); // followed by a copy of CHILD_T
405  CHECK (*++scope == CHILD_T); // and another copy of CHILD_T
406  CHECK (*++scope == MakeRec().appendChild(CHILD_B) // and there is a nested Record
407  .appendChild(CHILD_A) // with CHILD_B
408  .genNode(SUB_NODE.idi.getSym())); // and CHILD_A
409  CHECK (isnil(++scope)); // thats all -- no more children
410 
411  // Part II : apply the second diff
412  application.consume (mutationDiff());
413  CHECK (join (subject.keys()) == "α, β, γ"); // the attributes weren't altered
414  scope = subject.scope(); // but the scope was reordered
415  CHECK ( *scope == CHILD_T); // CHILD_T
416  CHECK (*++scope == CHILD_A); // CHILD_A
417  Rec nested = (++scope)->data.get<Rec>(); // and our nested Record, which too has been altered:
418  CHECK (nested.get("γ").data.get<double>() == 3.45); // it carries now an attribute "δ", which is again
419  CHECK (nested.get("δ") == MakeRec().appendChild(CHILD_A) // a nested Record with three children CHILD_A
420  .appendChild(CHILD_A) //
421  .appendChild(CHILD_A) //
422  .genNode("δ")); //
423  auto subScope = nested.scope(); // and within the nested sub-scope we find
424  CHECK ( *subScope != CHILD_A); // CHILD_A has been altered by assignment
425  CHECK (CHILD_A.idi == subScope->idi); // ...: same ID as CHILD_A
426  CHECK ("A" == subScope->data.get<string>()); // ...: but mutated payload
427  CHECK (*++subScope == MakeRec().type("Y") // a yet-again nested sub-Record of type "Y"
428  .set("β", int64_t(2)) // with just an attribute "β" == 2L
429  .genNode(CHILD_NODE.idi.getSym())); // (and an empty child scope)
430  CHECK (*++subScope == CHILD_T); // followed by another copy of CHILD_T
431  CHECK (isnil (++subScope)); //
432  CHECK (isnil (++scope)); // and nothing beyond that.
433  }
434  };
435 
436 
438  LAUNCHER (MutationMessage_test, "unit common");
439 
440 
441 
442 }}} // 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:49
#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:308
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:871
Simple 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:874
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:150
Standard implementation of the IterSource interface: a wrapped "Lumiera Forward Iterator".
generic data element node within a tree
Definition: gen-node.hpp:231
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.