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) 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 
33 #include "lib/test/run.hpp"
34 #include "lib/format-util.hpp"
37 #include "lib/iter-adapter-stl.hpp"
38 #include "lib/time/timevalue.hpp"
39 #include "lib/format-string.hpp"
40 #include "lib/format-cout.hpp"
41 #include "lib/util.hpp"
42 
43 #include <string>
44 #include <vector>
45 #include <memory>
46 
47 using util::isnil;
48 using util::join;
49 using util::_Fmt;
50 using util::BOTTOM_INDICATOR;
52 using lib::time::Time;
53 using std::unique_ptr;
54 using std::string;
55 using std::vector;
56 
57 
58 namespace lib {
59 namespace diff{
60 namespace test{
61 
62  namespace {//Test fixture....
63 
64  // define some GenNode elements
65  // to act as templates within the concrete diff
66  // NOTE: everything in this diff language is by-value
67  const GenNode ATTRIB1("α", 1), // attribute α = 1
68  ATTRIB2("β", int64_t(2)), // attribute α = 2L (int64_t)
69  ATTRIB3("γ", 3.45), // attribute γ = 3.45 (double)
70  TYPE_X("type", "ξ"), // a "magic" type attribute "Xi"
71  TYPE_Z("type", "ζ"), //
72  CHILD_A("a"), // unnamed string child node
73  CHILD_B('b'), // unnamed char child node
74  CHILD_T(Time(12,34,56,78)), // unnamed time value child
75  SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope
76  ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ
77  GAMMA_PI("γ", 3.14159265); // happens to have the same identity (ID) as ATTRIB3
78 
79 
86  class Opaque
87  {
88  idi::BareEntryID key_;
89  string type_ = Rec::TYPE_NIL;
90 
91  int alpha_ = -1;
92  int64_t beta_ = -1;
93  double gamma_ = -1;
94 
95  unique_ptr<Opaque> delta_;
96 
97  vector<Opaque> nestedObj_;
98  vector<string> nestedData_;
99 
100  public:
101  Opaque()
102  : key_(idi::EntryID<Opaque>())
103  { }
104 
105  explicit
106  Opaque (string keyID)
107  : key_(idi::EntryID<Opaque>(keyID))
108  { }
109 
110  explicit
112  : key_(id)
113  { }
114 
115  Opaque (Opaque const& o)
116  : key_(o.key_)
117  , type_(o.type_)
118  , alpha_(o.alpha_)
119  , beta_(o.beta_)
120  , gamma_(o.gamma_)
121  , delta_()
122  , nestedObj_(o.nestedObj_)
123  , nestedData_(o.nestedData_)
124  {
125  if (o.delta_)
126  delta_.reset(new Opaque(*o.delta_));
127  }
128 
129  Opaque&
130  operator= (Opaque const& o)
131  {
132  if (&o != this)
133  {
134  Opaque tmp(o);
135  swap (*this, tmp);
136  }
137  return *this;
138  }
139 
140  bool verifyType(string x) const { return x == type_; }
141  bool verifyAlpha(int x) const { return x == alpha_;}
142  bool verifyBeta(int64_t x) const { return x == beta_; }
143  bool verifyGamma(double x) const { return x == gamma_;}
144  bool verifyData(string desc) const { return desc == join(nestedData_); }
145  const Opaque* nestedDelta() const { return not delta_? NULL : delta_.get(); }
146  const Opaque* nestedObj_1() const { return isnil(nestedObj_)? NULL : &nestedObj_[0]; }
147 
148 
149  operator string() const
150  {
151  return _Fmt{"%s__(α:%d β:%s γ:%7.5f δ:%s\n......|nested:%s\n......|data:%s\n )__END_%s"}
152  % identity()
153  % alpha_
154  % beta_
155  % gamma_
156  % delta_
157  % join (nestedObj_, "\n......|")
158  % join (nestedData_)
159  % identity()
160  ;
161  }
162 
163  string
164  identity() const
165  {
166  string symbol = key_.getSym() + (isTyped()? "≺"+type_+"≻" : "");
167  return lib::idi::format::instance_hex_format(symbol, key_.getHash());
168  }
169 
170  bool
171  isTyped() const
172  {
173  return Rec::TYPE_NIL != type_;
174  }
175 
176 
201  void
203  {
204  buff.emplace (
206  .attach (collection(nestedData_)
207  .isApplicableIf ([&](GenNode const& spec) -> bool
208  {
209  return not spec.isNamed(); // »Selector« : accept anything unnamed value-like
210  })
211  .matchElement ([&](GenNode const& spec, string const& elm) -> bool
212  {
213  return elm == render(spec.data); // »Matcher« : does the diff verb #spec apply to this object?
214  })
215  .constructFrom ([&](GenNode const& spec) -> string
216  {
217  return render (spec.data); // »Constructor« : build a new child entity to reflect the given diff #spec
218  })
219  .assignElement ([&](string& target, GenNode const& spec) -> bool
220  {
221  target = render (spec.data); // »Assigner« : treat this object as value and assign data from the #spec payload
222  return true;
223  }))
224  .attach (collection(nestedObj_)
225  .isApplicableIf ([&](GenNode const& spec) -> bool
226  {
227  return spec.data.isNested(); // »Selector« : require object-like sub scope
228  })
229  .matchElement ([&](GenNode const& spec, Opaque const& elm) -> bool
230  {
231  return spec.idi == elm.key_;
232  })
233  .constructFrom ([&](GenNode const& spec) -> Opaque
234  {
235  return Opaque{spec.idi};
236  })
237  .buildChildMutator ([&](Opaque& target, GenNode::ID const&, TreeMutator::Handle buff) -> bool
238  {
239  target.buildMutator (buff); // »Recursive Mutator« : delegate to child for building a 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
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
313  populationDiff()
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
325  reorderingDiff()
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
338  mutationDiff()
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
366  run (Arg)
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:142
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:49
Front-end for printf-style string template interpolation.
typed symbolic and hash ID for asset-like position accounting.
Definition: entry-id.hpp:135
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:305
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:113
static const Ref END
symbolic ID ref "_END_"
Definition: gen-node.hpp:862
Simple 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:231
generic builder to apply a diff description to a given target data structure.