Lumiera  0.pre.03
»edit your freedom«
linked-elements-test.cpp
Go to the documentation of this file.
1 /*
2  LinkedElements(Test) - verify the intrusive single linked list template
3 
4  Copyright (C)
5  2012, 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 
20 #include "lib/test/run.hpp"
21 #include "lib/test/test-helper.hpp"
22 #include "lib/util.hpp"
23 
25 #include "lib/linked-elements.hpp"
27 #include "lib/iter-source.hpp"
28 
29 #include <memory>
30 
31 
32 
33 namespace lib {
34 namespace test{
35 
36  namespace error = lumiera::error;
37 
38  using util::isnil;
39  using util::isSameObject;
40  using LERR_(ITER_EXHAUST);
41 
42 
43  namespace { // test data...
44 
45  LUMIERA_ERROR_DEFINE(PROVOKED_FAILURE, "provoked failure");
46 
47  const uint NUM_ELEMENTS = 500;
48  int exception_trigger = -1;
49 
50  inline void __triggerErrorAt(int i) { exception_trigger = i; }
51  inline void __triggerError_reset() { exception_trigger =-1; }
52 
53 
58  struct Nummy
59  : Dummy
60  {
61  Nummy* next;
62 
63  Nummy()
64  : Dummy()
65  , next{0}
66  { }
67 
68  explicit
69  Nummy (int i)
70  : Dummy{i}
71  , next{0}
72  {
73  if (i == exception_trigger)
74  throw error::Fatal("simulated error", LUMIERA_ERROR_PROVOKED_FAILURE);
75  }
76  };
77 
78 
82  template<uint I>
83  struct Num
84  : Nummy
85  {
86  void* storage[I]; // note size depends on template parameter
87 
88  Num (int i=0, int j=0, int k=0)
89  : Nummy(I+i+j+k)
90  { }
91  };
92 
93 
94 
104  : public IterSource<Nummy>
105  {
106  uint maxNum_;
107 
108  virtual Pos
110  {
111  return new Nummy(1);
112  }
113 
114  virtual void
115  nextResult(Pos& num)
116  {
117  uint current = num->getVal();
118  if (maxNum_ <= current)
119  num = 0;
120  else
121  num = new Nummy(current+1);
122  }
123 
124  public:
125  NummyGenerator (uint maxElms)
126  : maxNum_(maxElms)
127  { }
128  };
129 
131  class Populator
132  : public NummyGenerator::iterator
133  {
134  public:
135  explicit
136  Populator (uint numElms)
137  : NummyGenerator::iterator (
138  NummyGenerator::build (new NummyGenerator(numElms)))
139  { }
140  };
141 
142 
143 
144 
145  inline uint
146  sum (uint n)
147  {
148  return n*(n+1) / 2;
149  }
150 
151  }//(End) test data and helpers
152 
153 
154 
155 
156 
159 
162 
163 
164 
165  /****************************************************************/
170  class LinkedElements_test : public Test
171  {
172 
173  virtual void
174  run (Arg)
175  {
176  simpleUsage();
177  iterating();
178  reverseList();
179 
180  verify_nonOwnership();
181  verify_ExceptionSafety();
182  populate_by_iterator();
183  verify_RAII_safety();
184  verify_customAllocator();
185  }
186 
187 
188  void
189  simpleUsage()
190  {
191  CHECK (0 == Dummy::checksum());
192  {
193  List elements;
194  CHECK (isnil (elements));
195  CHECK (0 == elements.size());
196  CHECK (0 == Dummy::checksum());
197 
198  elements.emplace<Nummy>(1);
199  elements.emplace<Nummy>(2);
200  elements.emplace<Nummy>(3);
201  elements.emplace<Nummy>(4);
202  elements.emplace<Nummy>(5);
203  CHECK (!isnil (elements));
204  CHECK (5 == elements.size());
205  CHECK (0 != Dummy::checksum());
206 
207  CHECK (Dummy::checksum() == elements[0].getVal()
208  + elements[1].getVal()
209  + elements[2].getVal()
210  + elements[3].getVal()
211  + elements[4].getVal());
212 
213  elements.clear();
214  CHECK (isnil (elements));
215  CHECK (0 == elements.size());
216  CHECK (0 == Dummy::checksum());
217 
218  elements.emplace<Nummy>();
219  elements.emplace<Nummy>();
220  elements.emplace<Nummy>();
221 
222  CHECK (3 == elements.size());
223  CHECK (0 != Dummy::checksum());
224  }
225  CHECK (0 == Dummy::checksum());
226  }
227 
228 
229  void
230  iterating()
231  {
232  CHECK (0 == Dummy::checksum());
233  {
234  List elements;
235  for (uint i=1; i<=NUM_ELEMENTS; ++i)
236  elements.emplace<Nummy>(i);
237 
238  // since elements where pushed,
239  // they should appear in reversed order
240  int check=NUM_ELEMENTS;
241  List::iterator ii = elements.begin();
242  while (ii)
243  {
244  CHECK (check == ii->getVal());
245  CHECK (check == ii->calc(+5) - 5);
246  --check;
247  ++ii;
248  }
249  CHECK (0 == check);
250 
251 
252  // Test the const iterator
253  List const& const_elm (elements);
254  check = NUM_ELEMENTS;
255  List::const_iterator cii = const_elm.begin();
256  while (cii)
257  {
258  CHECK (check == cii->getVal());
259  --check;
260  ++cii;
261  }
262  CHECK (0 == check);
263 
264 
265  // Verify correct behaviour of iteration end
266  CHECK (! (elements.end()));
267  CHECK (isnil (elements.end()));
268 
269  VERIFY_ERROR (ITER_EXHAUST, *elements.end() );
270  VERIFY_ERROR (ITER_EXHAUST, ++elements.end() );
271 
272  CHECK (ii == elements.end());
273  CHECK (ii == List::iterator());
274  CHECK (cii == elements.end());
275  CHECK (cii == List::const_iterator());
276  VERIFY_ERROR (ITER_EXHAUST, ++ii );
277  VERIFY_ERROR (ITER_EXHAUST, ++cii );
278 
279  }
280  CHECK (0 == Dummy::checksum());
281  }
282 
283 
284  void
285  reverseList()
286  {
287  CHECK (0 == Dummy::checksum());
288  {
289  List list;
290  CHECK (isnil (list));
291  list.reverse();
292  CHECK (isnil (list));
293  CHECK (0 == Dummy::checksum());
294 
295  list.emplace<Nummy>(1);
296  CHECK (not isnil (list));
297  CHECK (1 == list[0].getVal());
298  CHECK (1 == Dummy::checksum());
299  list.reverse();
300  CHECK (1 == Dummy::checksum());
301  CHECK (1 == list[0].getVal());
302  CHECK (not isnil (list));
303 
304  list.emplace<Nummy>(2);
305  CHECK (not isnil (list));
306  CHECK (2 == list.size());
307  CHECK (2 == list[0].getVal());
308  CHECK (2+1 == Dummy::checksum());
309  list.reverse();
310  CHECK (1+2 == Dummy::checksum());
311  CHECK (1 == list[0].getVal());
312  CHECK (2 == list.size());
313 
314  list.emplace<Nummy>(3);
315  CHECK (3 == list.size());
316  CHECK (3 == list.top().getVal());
317  CHECK (3+1+2 == Dummy::checksum());
318  list.reverse();
319  CHECK (2 == list[0].getVal());
320  CHECK (1 == list[1].getVal());
321  CHECK (3 == list[2].getVal());
322  List::iterator ii = list.begin();
323  CHECK (2 == ii->getVal());
324  ++ii;
325  CHECK (1 == ii->getVal());
326  ++ii;
327  CHECK (3 == ii->getVal());
328  ++ii;
329  CHECK (isnil (ii));
330  CHECK (2+1+3 == Dummy::checksum());
331 
332  list.emplace<Nummy>(4);
333  CHECK (4 == list.top().getVal());
334  CHECK (3 == list[3].getVal());
335  list.reverse();
336  CHECK (3 == list[0].getVal());
337  CHECK (1 == list[1].getVal());
338  CHECK (2 == list[2].getVal());
339  CHECK (4 == list[3].getVal());
340  CHECK (3+1+2+4 == Dummy::checksum());
341  }
342  CHECK (0 == Dummy::checksum());
343  }
344 
345 
354  void
356  {
357  CHECK (0 == Dummy::checksum());
358  {
360  CHECK (isnil (elements));
361 
362  Num<22> n2;
363  Num<44> n4;
364  Num<66> n6;
365  CHECK (22+44+66 == Dummy::checksum());
366 
367  elements.push(n2);
368  elements.push(n4);
369  elements.push(n6);
370  CHECK (!isnil (elements));
371  CHECK (3 == elements.size());
372  CHECK (22+44+66 == Dummy::checksum()); // not altered: we're referring the originals
373 
374  CHECK (66 == elements[0].getVal());
375  CHECK (44 == elements[1].getVal());
376  CHECK (22 == elements[2].getVal());
377  CHECK (isSameObject(n2, elements[2]));
378  CHECK (isSameObject(n4, elements[1]));
379  CHECK (isSameObject(n6, elements[0]));
380 
381  elements.clear();
382  CHECK (isnil (elements));
383  CHECK (22+44+66 == Dummy::checksum()); // referred elements unaffected
384  }
385  CHECK (0 == Dummy::checksum());
386  }
387 
388 
389  void
390  verify_ExceptionSafety()
391  {
392  CHECK (0 == Dummy::checksum());
393  {
394  List elements;
395  CHECK (isnil (elements));
396 
397  __triggerErrorAt(3);
398 
399  elements.emplace<Nummy>(1);
400  elements.emplace<Nummy>(2);
401  CHECK (1+2 == Dummy::checksum());
402 
403  VERIFY_ERROR (PROVOKED_FAILURE, elements.emplace<Nummy>(3) );
404  CHECK (1+2 == Dummy::checksum());
405  CHECK (2 == elements.size());
406 
407  CHECK (2 == elements[0].getVal());
408  CHECK (1 == elements[1].getVal());
409 
410  elements.clear();
411  CHECK (0 == Dummy::checksum());
412  __triggerError_reset();
413  }
414  CHECK (0 == Dummy::checksum());
415  }
416 
417 
418  void
419  populate_by_iterator()
420  {
421  CHECK (0 == Dummy::checksum());
422  {
423  Populator yieldSomeElements(NUM_ELEMENTS);
424  List elements (yieldSomeElements);
425 
426  CHECK (!isnil (elements));
427  CHECK (NUM_ELEMENTS == elements.size());
428  CHECK (sum(NUM_ELEMENTS) == Dummy::checksum());
429 
430  int check=NUM_ELEMENTS;
431  List::iterator ii = elements.begin();
432  while (ii)
433  {
434  CHECK (check == ii->getVal());
435  --check;
436  ++ii;
437  }
438  CHECK (0 == check);
439  }
440  CHECK (0 == Dummy::checksum());
441  }
442 
443 
450  void
452  {
453  CHECK (0 == Dummy::checksum());
454 
455  __triggerErrorAt(3);
456  Populator yieldSomeElements(NUM_ELEMENTS);
457  VERIFY_ERROR (PROVOKED_FAILURE, List elements(yieldSomeElements) );
458 
459  CHECK (0 == Dummy::checksum());
460  __triggerError_reset();
461  }
462 
463 
464 
468  {
470 
471  CustomAllocator cluster_;
472 
473  UseAllocationCluster (CustomAllocator clu)
474  : cluster_(clu)
475  { }
476 
477  template<class TY, typename...ARGS>
478  TY*
479  create (ARGS&& ...args)
480  {
481  return & cluster_.create<TY> (std::forward<ARGS> (args)...);
482  }
483 
484  void dispose (void*) { /* does nothing */ }
485  };
486 
498  void
500  {
501  CHECK (0 == Dummy::checksum());
502  {
503  AllocationCluster cluster;
504 
506 
507  elements.emplace<Num<1>> (2);
508  elements.emplace<Num<3>> (4,5);
509  elements.emplace<Num<6>> (7,8,9);
510 
511  const size_t EXPECT = sizeof(Num<1>) + sizeof(Num<3>) + sizeof(Num<6>)
512  + 3*2*sizeof(void*); // ◁┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄overhead for storing 3 dtor-invokers
513  CHECK (EXPECT == cluster.numBytes());
514  CHECK (sum(9) == Dummy::checksum());
515 
516  CHECK (3 == elements.size());
517  CHECK (1+2 == elements[2].getVal());
518  CHECK (3+4+5 == elements[1].getVal());
519  CHECK (6+7+8+9 == elements[0].getVal());
520 
521  elements.clear();
522  CHECK (EXPECT == cluster.numBytes());
523  CHECK (sum(9) == Dummy::checksum());
524  // note: elements won't be discarded unless
525  // the AllocationCluster goes out of scope
526  }
527  CHECK (0 == Dummy::checksum());
528  {
529  // now use AllocationCluster through the default allocator adapter...
530  AllocationCluster cluster;
533 
534  Elms elements{cluster.getAllocator<Nummy>()};
535 
536  elements.emplace<Num<1>> (2);
537  elements.emplace<Num<3>> (4,5);
538 
539  const size_t EXPECT = sizeof(Num<1>) + sizeof(Num<3>);
540  CHECK (EXPECT == cluster.numBytes());
541  CHECK (sum(5) == Dummy::checksum());
542 
543  CHECK (2 == elements.size());
544  CHECK (1+2 == elements[1].getVal());
545  CHECK (3+4+5 == elements[0].getVal());
546  // note: this time the destructors will be invoked
547  // from LinkedElements::clear(), but not from
548  // the destructor of AllocationCluster
549  }
550  CHECK (0 == Dummy::checksum());
551  }
552  };
553 
554 
555 
556  LAUNCHER (LinkedElements_test, "unit common");
557 
558 
559 }} // namespace lib::test
LinkedElements & reverse()
Mutate the complete list to change the order of elements.
IterQueue< T > elements(T const &elm)
convenience free function to build an iterable sequence
Definition: iter-stack.hpp:292
TY & emplace(ARGS &&...args)
prepend object of type TY, forwarding ctor args
Policy to use an Allocation cluster, but also to invoke all object destructors.
Definition: run.hpp:40
Memory management for the low-level model (render nodes network).
Intrusive single linked list, possibly taking ownership of node elements.
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Iterator-Frontend to generate this series of objects.
Implementation namespace for support and library code.
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
Iteration source interface to abstract a data source, which then can be accessed through IterAdapter ...
Definition: iter-source.hpp:79
Simplistic test class runner.
Another Lumiera Forward Iterator building block, based on incorporating a state type as »*State Core*...
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
unittest helper code: test dummy objects to track instances.
Helper to produce a pre-determined series of objects to populate a LinkedElements list...
A collection of frequently used helper functions to support unit testing.
Intrusive single linked list with optional ownership.
A Dummy object for tests.
void clear() noexcept
A pile of objects sharing common allocation and lifecycle.
TY & push(TY &elm) noexcept
accept the given element and prepend it to the list of elements; depending on the allocation policy...
virtual Pos firstResult()
iteration start: prepare the first element.
virtual void nextResult(Pos &num)
iteration step: switch on to the next element.
Extension module to build an opaque data source, accessible as Lumiera Forward Iterator.
Test-Element, supporting intrusive linked list storage.
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition: error.h:71
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee&#39;s memory identities. ...
Definition: util.hpp:421