Lumiera  0.pre.03
»edit your freedom«
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"
28 #include "lib/iter-adapter-stl.hpp"
29 #include "lib/time/timevalue.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 
38 using util::isnil;
39 using util::join;
40 using util::_Fmt;
41 using util::BOTTOM_INDICATOR;
43 using lib::time::Time;
44 using std::unique_ptr;
45 using std::string;
46 using std::vector;
47 
48 
49 namespace lib {
50 namespace diff{
51 namespace 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  {
79  idi::BareEntryID key_;
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:
92  Opaque()
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 (
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
304  populationDiff()
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
316  reorderingDiff()
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
329  mutationDiff()
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 
423  LAUNCHER (DiffComplexApplication_test, "unit common");
424 
425 
426 
427 }}} // namespace lib::diff::test
Concrete implementation to apply structural changes to hierarchical data structures.
type erased baseclass for building a combined hash and symbolic ID.
Definition: entry-id.hpp:133
Automatically use custom string conversion in C++ stream output.
bool isNested() const
determine if payload constitutes a nested scope ("object")
Definition: gen-node.hpp:768
void buildMutator(TreeMutator::Handle buff)
the only way this opaque object exposes itself for mutation through diff messages.
Definition: run.hpp:40
Front-end for printf-style string template interpolation.
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:126
iter_stl::IterSnapshot< VAL > snapshot(std::initializer_list< VAL > const &&ili)
Take a snapshot of the given std::initializer_list.
A front-end for using printf-style formatting.
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:299
static Builder< TreeMutator > build()
DSL: start building a custom adapted tree mutator, where the operations are tied by closures or wrapp...
SUB & emplace(SUB &&implementation)
move-construct an instance of a subclass into the opaque buffer
A handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBu...
Definition: record.hpp:104
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...
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.
auto collection(COLL &coll)
Entry point to a nested DSL for setup and configuration of a collection binding.
Preconfigured adapters for some STL container standard usage situations.
a family of time value like entities and their relationships.
friend constexpr size_t treeMutatorSize(const Opaque *)
override default size traits to allow for sufficient buffer, able to hold the mutator defined above...
Diagnostic helper for unit tests regarding mutation of custom data.
generic data element node within a tree
Definition: gen-node.hpp:222
generic builder to apply a diff description to a given target data structure.