Lumiera  0.pre.03
»edityourfreedom«
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) Lumiera.org
5  2016, 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 
32 #include "lib/test/run.hpp"
33 #include "lib/format-util.hpp"
36 #include "lib/iter-adapter-stl.hpp"
37 #include "lib/time/timevalue.hpp"
38 #include "lib/format-string.hpp"
39 #include "lib/format-cout.hpp"
40 #include "lib/util.hpp"
41 
42 #include <string>
43 #include <vector>
44 #include <memory>
45 
46 using util::isnil;
47 using util::join;
48 using util::_Fmt;
51 using lib::time::Time;
52 using std::unique_ptr;
53 using std::string;
54 using std::vector;
55 
56 
57 namespace lib {
58 namespace diff{
59 namespace test{
60 
61  namespace {//Test fixture....
62 
63  // define some GenNode elements
64  // to act as templates within the concrete diff
65  // NOTE: everything in this diff language is by-value
66  const GenNode ATTRIB1("α", 1), // attribute α = 1
67  ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t)
68  ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
69  TYPE_X("type", "ξ"), // a "magic" type attribute "Xi"
70  TYPE_Z("type", "ζ"), //
71  CHILD_A("a"), // unnamed string child node
72  CHILD_B('b'), // unnamed char child node
73  CHILD_T(Time(12,34,56,78)), // unnamed time value child
74  SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope
75  ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ
76  GAMMA_PI("γ", 3.14159265); // happens to have the same identity (ID) as ATTRIB3
77 
78 
85  class Opaque
86  {
87  idi::BareEntryID key_;
88  string type_ = Rec::TYPE_NIL;
89 
90  int alpha_ = -1;
91  int64_t beta_ = -1;
92  double gamma_ = -1;
93 
94  unique_ptr<Opaque> delta_;
95 
96  vector<Opaque> nestedObj_;
97  vector<string> nestedData_;
98 
99  public:
100  Opaque()
101  : key_(idi::EntryID<Opaque>())
102  { }
103 
104  explicit
105  Opaque (string keyID)
106  : key_(idi::EntryID<Opaque>(keyID))
107  { }
108 
109  explicit
111  : key_(id)
112  { }
113 
114  Opaque (Opaque const& o)
115  : key_(o.key_)
116  , type_(o.type_)
117  , alpha_(o.alpha_)
118  , beta_(o.beta_)
119  , gamma_(o.gamma_)
120  , delta_()
121  , nestedObj_(o.nestedObj_)
122  , nestedData_(o.nestedData_)
123  {
124  if (o.delta_)
125  delta_.reset(new Opaque(*o.delta_));
126  }
127 
128  Opaque&
129  operator= (Opaque const& o)
130  {
131  if (&o != this)
132  {
133  Opaque tmp(o);
134  swap (*this, tmp);
135  }
136  return *this;
137  }
138 
139  bool verifyType(string x) const { return x == type_; }
140  bool verifyAlpha(int x) const { return x == alpha_;}
141  bool verifyBeta(int64_t x) const { return x == beta_; }
142  bool verifyGamma(double x) const { return x == gamma_;}
143  bool verifyData(string desc) const { return desc == join(nestedData_); }
144  const Opaque* nestedDelta() const { return not delta_? NULL : delta_.get(); }
145  const Opaque* nestedObj_1() const { return isnil(nestedObj_)? NULL : &nestedObj_[0]; }
146 
147 
148  operator string() const
149  {
150  return _Fmt{"%s__(α:%d β:%s γ:%7.5f δ:%s\n......|nested:%s\n......|data:%s\n )__END_%s"}
151  % identity()
152  % alpha_
153  % beta_
154  % gamma_
155  % delta_
156  % join (nestedObj_, "\n......|")
157  % join (nestedData_)
158  % identity()
159  ;
160  }
161 
162  string
163  identity() const
164  {
165  string symbol = key_.getSym() + (isTyped()? "≺"+type_+"≻" : "");
166  return lib::idi::format::instance_hex_format(symbol, key_.getHash());
167  }
168 
169  bool
170  isTyped() const
171  {
172  return Rec::TYPE_NIL != type_;
173  }
174 
175 
200  void
201  buildMutator (TreeMutator::Handle buff)
202  {
203  buff.create (
205  .attach (collection(nestedData_)
206  .isApplicableIf ([&](GenNode const& spec) -> bool
207  {
208  return not spec.isNamed(); // »Selector« : accept anything unnamed value-like
209  })
210  .matchElement ([&](GenNode const& spec, string const& elm) -> bool
211  {
212  return elm == render(spec.data);
213  })
214  .constructFrom ([&](GenNode const& spec) -> string
215  {
216  return render (spec.data);
217  })
218  .assignElement ([&](string& target, GenNode const& spec) -> bool
219  {
220  target = render (spec.data);
221  return true;
222  }))
223  .attach (collection(nestedObj_)
224  .isApplicableIf ([&](GenNode const& spec) -> bool
225  {
226  return spec.data.isNested(); // »Selector« : require object-like sub scope
227  })
228  .matchElement ([&](GenNode const& spec, Opaque const& elm) -> bool
229  {
230  return spec.idi == elm.key_;
231  })
232  .constructFrom ([&](GenNode const& spec) -> Opaque
233  {
234  return Opaque{spec.idi};
235  })
236  .buildChildMutator ([&](Opaque& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
237  {
238  if (target.key_ != subID) return false; // require match on already existing child object
239  target.buildMutator (buff); // delegate to child to build nested TreeMutator
240  return true;
241  }))
242  .change("type", [&](string typeID)
243  {
244  type_ = typeID;
245  })
246  .change("α", [&](int val)
247  {
248  alpha_ = val;
249  })
250  .change("β", [&](int64_t val)
251  {
252  beta_ = val;
253  })
254  .change("γ", [&](double val)
255  {
256  gamma_ = val;
257  })
258  .mutateAttrib("δ", [&](TreeMutator::Handle buff)
259  {
260  if (not delta_) // note: object is managed automatically,
261  delta_.reset (new Opaque("δ")); // thus no INS-implementation necessary
262  REQUIRE (delta_);
263 
264  delta_->buildMutator(buff);
265  }));
266  }
267 
272  friend constexpr size_t
273  treeMutatorSize (const Opaque*)
274  {
275  return 430;
276  }
277  };
278 
279  }//(End)Test fixture
280 
281 
282 
283 
284 
285 
286 
287  /***********************************************************************/
307  : public Test
309  {
311 
312  DiffSeq
314  {
315  return snapshot({ins(ATTRIB1)
316  , ins(ATTRIB3)
317  , ins(ATTRIB3)
318  , ins(CHILD_B)
319  , ins(CHILD_B)
320  , ins(CHILD_T)
321  });
322  } // ==> ATTRIB1, ATTRIB3, (ATTRIB3), CHILD_B, CHILD_B, CHILD_T
323 
324  DiffSeq
326  {
327  return snapshot({after(Ref::ATTRIBS)
328  , ins(ATTRIB2)
329  , del(CHILD_B)
330  , ins(SUB_NODE)
331  , find(CHILD_T)
332  , pick(CHILD_B)
333  , skip(CHILD_T)
334  });
335  } // ==> ATTRIB1, ATTRIB3, (ATTRIB3), ATTRIB2, SUB_NODE, CHILD_T, CHILD_B
336 
337  DiffSeq
339  {
340  return snapshot({after(CHILD_B)
341  , after(Ref::END)
342  , set(GAMMA_PI)
343  , mut(SUB_NODE)
344  , ins(TYPE_X)
345  , ins(ATTRIB2)
346  , ins(CHILD_B)
347  , ins(CHILD_A)
348  , emu(SUB_NODE)
349  , ins(ATTRIB_NODE)
350  , mut(ATTRIB_NODE)
351  , ins(TYPE_Z)
352  , ins(CHILD_A)
353  , ins(CHILD_A)
354  , ins(CHILD_A)
355  , emu(ATTRIB_NODE)
356  });
357  } // ==> ATTRIB1, ATTRIB3 := π, (ATTRIB3), ATTRIB2,
358  // ATTRIB_NODE{ type ζ, CHILD_A, CHILD_A, CHILD_A }
359  // SUB_NODE{ type ξ, ATTRIB2, CHILD_B, CHILD_A },
360  // CHILD_T, CHILD_B
361 
362 
363 
364 
365  virtual void
367  {
368  Opaque subject;
369  DiffApplicator<Opaque> application(subject);
370  //
371  cout << "before..."<<endl << subject<<endl;
372  CHECK (subject.verifyAlpha(-1));
373  CHECK (subject.verifyBeta(-1));
374  CHECK (subject.verifyGamma(-1));
375  CHECK (not subject.nestedDelta());
376  CHECK (not subject.nestedObj_1());
377  CHECK (subject.verifyData(""));
378 
379 
380  // Part I : apply attribute changes
381  application.consume(populationDiff());
382  //
383  cout << "after...I"<<endl << subject<<endl;
384  // ==> ATTRIB1, ATTRIB3, (ATTRIB3), CHILD_B, CHILD_B, CHILD_T
385  CHECK (subject.verifyAlpha(1));
386  CHECK (subject.verifyGamma(ATTRIB3.data.get<double>()));
387  CHECK (subject.verifyData("b, b, 78:56:34.012"));
388  // unchanged...
389  CHECK (subject.verifyBeta(-1));
390  CHECK (not subject.nestedDelta());
391  CHECK (not subject.nestedObj_1());
392 
393 
394  // Part II : apply child population
395  application.consume(reorderingDiff());
396  //
397  cout << "after...II"<<endl << subject<<endl;
398  // ==> ATTRIB1, ATTRIB3, (ATTRIB3), ATTRIB2, SUB_NODE, CHILD_T, CHILD_B
399  CHECK (subject.verifyAlpha(1));
400  CHECK (subject.verifyBeta (2)); // attribute β has been set
401  CHECK (subject.verifyGamma(3.45));
402  CHECK (subject.verifyData("78:56:34.012, b")); // one child deleted, the other ones re-ordered
403  CHECK (subject.nestedObj_1()); // plus inserted a nested child object
404  CHECK (subject.nestedObj_1()->verifyType(Rec::TYPE_NIL));
405  CHECK (subject.nestedObj_1()->verifyBeta(-1)); // ...which is empty (default constructed)
406  CHECK (subject.nestedObj_1()->verifyData(""));
407 
408 
409  // Part III : apply child mutations
410  application.consume(mutationDiff());
411  //
412  cout << "after...III"<<endl << subject<<endl;
413  // ==> ATTRIB1, ATTRIB3 := π, (ATTRIB3), ATTRIB2,
414  // ATTRIB_NODE{ type ζ, CHILD_A, CHILD_A, CHILD_A }
415  // SUB_NODE{ type ξ, ATTRIB2, CHILD_B, CHILD_A },
416  // CHILD_T, CHILD_B
417  CHECK (subject.verifyAlpha(1));
418  CHECK (subject.verifyBeta (2));
419  CHECK (subject.verifyGamma(GAMMA_PI.data.get<double>())); // new value assigned to attribute γ
420  CHECK (subject.nestedDelta()); // attribute δ (object valued) is now present
421  CHECK (subject.nestedDelta()->verifyType("ζ")); // ...and has an explicitly defined type field
422  CHECK (subject.nestedDelta()->verifyData("a, a, a"));//...plus three similar child values
423  CHECK (subject.verifyData("78:56:34.012, b")); // the child values weren't altered
424  CHECK (subject.nestedObj_1()->verifyType("ξ")); // but the nested child object's type has been set
425  CHECK (subject.nestedObj_1()->verifyBeta(2)); // ...and the attribute β has been set on the nested object
426  CHECK (subject.nestedObj_1()->verifyData("b, a")); // ...plus some child values where added here
427  }
428  };
429 
430 
432  LAUNCHER (DiffComplexApplication_test, "unit common");
433 
434 
435 
436 }}} // 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:135
Rec::Mutator MakeRec
Definition: gen-node.hpp:135
Automatically use custom string conversion in C++ stream output.
bool isNested() const
determine if payload constitutes a nested scope ("object")
Definition: gen-node.hpp:719
void consume(DIFF &&diff)
string instance_hex_format(string const &prefix, size_t instanceNr)
Definition: genfunc.cpp:56
Definition: run.hpp:49
Front-end for printf-style string template interpolation.
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:128
string join(CON &&coll, string const &delim=", ")
enumerate a collection&#39;s contents, separated by delimiter.
A front-end for using printf-style formatting.
ContentSnapshot< CON > snapshot(CON const &con)
Take a snapshot of the given STL compliant container.
Implementation namespace for support and library code.
bool find(Query< Pipe > &q)
shortcut: run just a query without creating new instances
bool isNamed() const
Definition: gen-node.hpp:323
string const & getSym() const
Definition: entry-id.hpp:165
static Builder< TreeMutator > build()
DSL: start building a custom adapted tree mutator, where the operations are tied by closures or wrapp...
handle to allow for safe »remote implantation« of an unknown subclass into a given opaque InPlaceBuff...
Definition: record.hpp:108
std::vector< string > & Arg
Definition: run.hpp:54
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:755
return NULL
Definition: llist.h:596
Simple test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
BA & create(SUB &&implementation)
bool isnil(lib::time::Duration const &dur)
Definition: timevalue.hpp:642
static const Ref ATTRIBS
symbolic ID ref "_ATTRIBS_"
Definition: gen-node.hpp:758
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
OpaqueHolder< Base > Opaque
const string BOTTOM_INDICATOR
Definition: meta/util.hpp:244
static const string TYPE_NIL
Definition: record.hpp:155
LuidH const & getHash() const
Definition: entry-id.hpp:171
materialised iterator contents.
Preconfigured adapters for some STL container standard usage situations.
a family of time value like entities and their relationships.
constexpr size_t treeMutatorSize(...)
Diagnostic helper for unit tests regarding mutation of custom data.
generic data element node within a tree
Definition: gen-node.hpp:213
LAUNCHER(DiffComplexApplication_test, "unit common")
Register this test class...
generic builder to apply a diff description to a given target data structure.