Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
several-builder-test.cpp
Go to the documentation of this file.
1/*
2 SeveralBuilder(Test) - building a limited fixed collection of elements
3
4 Copyright (C)
5 2008, Hermann Vosseler <Ichthyostega@web.de>
6 2024, Hermann Vosseler <Ichthyostega@web.de>
7
8  **Lumiera** is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License as published by the
10  Free Software Foundation; either version 2 of the License, or (at your
11  option) any later version. See the file COPYING for further details.
12
13* *****************************************************************/
14
20#include "lib/test/run.hpp"
26#include "lib/iter-explorer.hpp"
27#include "lib/format-util.hpp"
28#include "lib/util.hpp"
29
31
32#include <array>
33
34using ::test::Test;
35using std::array;
36
37using lib::explore;
38using util::isLimited;
39using util::toString;
40using util::isnil;
41using util::join;
42
43
44namespace lib {
45namespace test{
46
47 using LERR_(INDEX_BOUNDS);
48
49 namespace { // invocation tracking diagnostic subclass...
50
59 template<uint i>
60 class Num
61 : public test::Dummy
62 {
63 std::array<int,i> ext_;
64
65 public:
66 Num (uint seed=i)
67 : Dummy(seed)
68 {
69 ext_.fill(seed);
70 setVal ((i+1)*seed);
71 }
73 {
74 setVal (getVal() - explore(ext_).resultSum());
75 }
76
77 long
78 calc (int ii) override
79 {
80 return i+ii + explore(ext_).resultSum();
81 }
82
84 Num (Num && oNum) noexcept
85 : Num(0)
86 {
87 swap (*this, oNum);
88 }
89 Num&
90 operator= (Num && oNum)
91 {
92 if (&oNum != this)
93 swap (*this, oNum);
94 return *this;
95 }
96
97 friend void
98 swap (Num& num1, Num& num2)
99 {
100 std::swap (static_cast<Dummy&> (num1)
101 ,static_cast<Dummy&> (num2));
102 std::swap (num1.ext_, num2.ext_);
103 }
104 };
105
106
114 {
115 int16_t val;
116
117 ShortBlocker (short r = 1 + rani(1'000))
118 : val(r)
119 { };
120 };
121
122 } // (END) test types
123
124
125
126
127
128
129
130 /***************************************************************/
138 class SeveralBuilder_test : public Test
139 {
140
141 virtual void
142 run (Arg)
143 {
144 seedRand();
145
146 simpleUsage();
151 }
152
153
154
157 void
159 {
160 auto elms = makeSeveral({1,1,2,3,5,8,13}).build();
161 CHECK (elms.size() == 7);
162 CHECK (elms.back() == 13);
163 CHECK (elms[3] == 3);
164 CHECK (join (elms,"-") == "1-1-2-3-5-8-13"_expect);
165 CHECK (toString(elms) == "[1, 1, 2, 3, 5, 8, 13]"_expect);
166 }
167
168
178 void
180 {
181 // prepare to verify proper invocation of all constructors / destructors
182 Dummy::checksum() = 0;
183
184 { // Scenario-1 : Baseclass and arbitrary subclass elements
185 SeveralBuilder<Dummy> builder;
186 CHECK (isnil (builder));
187
188 builder.emplace<Num<3>>()
189 .emplace<Num<2>>(1);
190 CHECK (2 == builder.size()); // use information functions...
191 CHECK (3 == builder[1].getVal()); // to peek into contents assembled thus far...
192 VERIFY_ERROR (INDEX_BOUNDS, builder[2] ); // runtime bounds check on the builder (but not on the product!)
193 builder.fillElm(2);
194 CHECK (4 == builder.size());
195 builder.fillElm(3, 5);
196 CHECK (7 == builder.size());
197
198 Several<Dummy> elms = builder.build();
199 CHECK ( isnil(builder));
200 CHECK (not isnil(elms));
201 CHECK (7 == elms.size());
202 CHECK (elms[0].getVal() == (3+1)*3); // indeed a Num<3> with default-seed ≡ 3
203 CHECK (elms[0].calc(1) == 3 + 1 + (3+3+3)); // indeed called the overridden calc() operation
204 CHECK (elms[1].getVal() == (2+1)*1); // indeed a Num<2> with seed ≡ 1
205 CHECK (elms[1].calc(1) == 2 + 1 + (1+1)); // indeed the overridden calc() picking from the Array(1,1)
206 CHECK (isLimited (1, elms[2].getVal(), 100'000'000)); // indeed a Dummy with default random seed
207 CHECK (isLimited (1, elms[3].getVal(), 100'000'000)); // and this one too, since we filled in two instances
208 CHECK (elms[4].getVal() == 5); // followed by tree instances Dummy(5)
209 CHECK (elms[5].getVal() == 5);
210 CHECK (elms[6].getVal() == 5);
211 CHECK (elms[6].calc(1) == 5+1); // indeed invoking the base implementation of calc()
212 }
213
214 { // Scenario-2 : unrelated element types
216
217 auto urgh = array<char,5>{"Urgh"};
218 auto phi = (1+sqrtf(5))/2;
219
220 builder.append (urgh, phi, -1); // can emplace arbitrary data
221 CHECK (3 == builder.size());
222
223 Several<uint32_t> elms = builder.build(); // WARNING: data accessed by wild cast to interface type
224 CHECK (3 == elms.size());
225 CHECK (elms[0] == * reinterpret_cast<const uint32_t*> ("Urgh"));
226 CHECK (elms[1] == * reinterpret_cast<uint32_t*> (&phi));
227 CHECK (elms[2] == uint32_t(-1));
228 }
229
230 { // Scenario-3 : copy values from iterator
231 SeveralBuilder<int> builder;
232
233 VecI seq = getTestSeq_int<VecI> (10);
234 builder.appendAll (seq);
235 CHECK (10 == builder.size());
236
237 auto elms = builder.build();
238 CHECK (10 == elms.size());
239 CHECK (join (elms,"-") == "0-1-2-3-4-5-6-7-8-9"_expect);
240 }
241
242 CHECK (0 == Dummy::checksum());
243 }
244
245
269 void
271 {
272 CHECK (0 == Dummy::checksum());
273
274 { // Scenario-1 : Baseclass and arbitrary subclass elements
275 SeveralBuilder<Dummy> builder;
276
277 // The first element will _prime_ the container for a suitable usage pattern
278 builder.emplace<Num<1>>();
279 CHECK (1 == builder.size());
280
281 // Notably the first element established the _spread_ between index positions,
282 // which effectively limits the size of objects to be added. Moreover, since
283 // the element type was detected to be non-trivial, we can not correct this
284 // element spacing by shifting existing allocations (`memmove()` not possible)
285 CHECK (sizeof(Num<1>) < sizeof(Num<5>));
286 VERIFY_FAIL ("Unable to place element of type Num<5u> (size="
287 , builder.emplace<Num<5>>() );
288 CHECK (1 == builder.size());
289
290 // Furthermore, the first element was detected to be a subclass,
291 // and the interface type `Dummy` has a virtual destructor;
292 // all added elements must comply to this scheme, once established
293 VERIFY_FAIL ("Unable to handle (trivial-)destructor for element type long, "
294 "since this container has been primed to use virtual-baseclass-destructors."
295 , builder.emplace<long>(55) );
296 CHECK (1 == builder.size());
297
298 // the initial allocation added some reserve buffer space (for 10 elements)
299 // and we can fill that space with arbitrary subclass instances
300 builder.fillElm (5);
301 CHECK (6 == builder.size());
302
303 // But the initial allocation can not be increased, since that would require
304 // a re-allocation of a larger buffer, followed by copying the elements;
305 // but since the established scheme allows for _arbitrary_ subclasses,
306 // the builder does not know the exact type for safe element relocation.
307 VERIFY_FAIL ("Several-container is unable to accommodate further element of type Dummy"
308 , builder.fillElm (20) );
309 CHECK (10 == builder.size());
310 }
311 // in spite of all the provoked failures,
312 // all element destructors were invoked
313 CHECK (0 == Dummy::checksum());
314
315
316 { // Scenario-2 : Baseclass and elements of a single fixed subclass
318
319 builder.fillElm(5);
320 CHECK (5 == builder.size());
321
322 // trigger re-alloc by moving into larger memory block
323 builder.fillElm(14);
324 CHECK (19 == builder.size());
325 CHECK (builder.size() > INITIAL_ELM_CNT);
326
327 // with the elements added thus far, this instance has been primed to
328 // rely on a fixed well known element type for move-growth and to use
329 // the virtual base class destructor for clean-up. It is thus not possible
330 // to add another element that is not related to this baseclass...
331 VERIFY_FAIL ("Unable to handle (trivial-)destructor for element type ShortBlocker, "
332 "since this container has been primed to use virtual-baseclass-destructors."
333 , builder.emplace<ShortBlocker>() );
334 CHECK (19 == builder.size());
335
336 CHECK (sizeof(ShortBlocker) < sizeof(Num<5>)); // it was not rejected due to size...
337
338 // However, a subclass /different than the defined element type/ is acceptable,
339 // but only to the condition to lock any further container growth by move-reallocation.
340 // The rationale is that we can still destroy through the virtual base destructor,
341 // but we aren't able to move elements safely any more, since we don't capture the type.
342 builder.emplace<Num<1>>();
343 CHECK (20 == builder.size());
344 CHECK (20 == builder.capacity());
345 CHECK ( 0 == builder.capReserve());
346
347 // But here comes the catch: since we choose to accept arbitrary sub-types not identified in detail,
348 // the container has lost its ability of move-reallocation; with 20 elements the current reserve
349 // is exhausted and we are now unable to add any further elements beyond that point.
350 VERIFY_FAIL ("unable to move elements of mixed unknown detail type, which are not trivially movable"
351 , builder.fillElm(5); );
352
353 // the container is still sound however
354 auto elms = builder.build();
355 CHECK (20 == elms.size());
356 // verify that member fields were not corrupted
357 for (uint i=0; i<=18; ++i)
358 CHECK (elms[i].calc(i) == 5 + i + (5+5+5+5+5));
359 CHECK (elms.back().calc(0) == 1 + 0 + (1));
360 }
361 CHECK (0 == Dummy::checksum());
362
363
364 { // Scenario-3 : arbitrary elements of trivial type
366
367 builder.reserve(16);
368 CHECK ( 0 == builder.size());
369 CHECK (16 == builder.capacity());
370 CHECK (16 == builder.capReserve());
371
372 string BFR{"starship is"};
373 builder.appendAll (BFR);
374 CHECK (11 == builder.size());
375 CHECK (16 == builder.capacity());
376 CHECK ( 5 == builder.capReserve());
377
378 // append element that is much larger than a char
379 // => since elements are trivial, they can be moved to accommodate
380 builder.append (int64_t(32));
381 CHECK (12 == builder.size());
382 CHECK (16 == builder.capacity()); // note: capacity remained nominally the same
383 CHECK ( 4 == builder.capReserve()); // while in fact the spread and thus the buffer were increased
384
385 // emplace a completely unrelated object type,
386 // which is also trivially destructible, but non-copyable
387 builder.emplace<ShortBlocker> ('c');
388
389 // can emplace further trivial objects, since there is still capacity left
390 builder.append (int('o'), long('o'));
391 CHECK (15 == builder.size());
392 CHECK ( 1 == builder.capReserve());
393
394 VERIFY_FAIL ("Unable to place element of type Num<5u>"
395 , builder.append (Num<5>{}) );
396 CHECK (sizeof(Num<5>) > sizeof(int64_t));
397 // not surprising: this one was too large,
398 // and due to the non-copyable element we can not adapt anymore
399
400 class NonTrivial
401 {
402 public:
403 ~NonTrivial() { }
404 };
405
406 // adding data of a non-trivial type is rejected,
407 // since the container does not capture individual element types
408 // and thus does not know how to delete it
409 CHECK (sizeof(NonTrivial) <= sizeof(int64_t));
410 VERIFY_FAIL ("Unsupported kind of destructor for element type SeveralBuilder_test::check_ErrorHandling()::NonTrivial"
411 , builder.append (NonTrivial{}) );
412 CHECK ( 1 == builder.capReserve());
413
414 // space for a single one left...
415 builder.append ('l');
416 CHECK (16 == builder.size());
417 CHECK ( 0 == builder.capReserve());
418
419 // and now we've run out of space, and due to the non-copyable object, move-relocation is rejected
420 VERIFY_FAIL ("Several-container is unable to accommodate further element of type char; "
421 "storage reserve (128 bytes ≙ 16 elms) exhausted and unable to move "
422 "elements of mixed unknown detail type, which are not trivially movable."
423 , builder.append ('!') );
424
425 // yet the container is still fine....
426 auto elms = builder.build();
427 CHECK (16 == elms.size());
428 CHECK (join(elms, "·") == "s·t·a·r·s·h·i·p· ·i·s· ·c·o·o·l"_expect);
429 }
430 CHECK (0 == Dummy::checksum());
431
432 { // Scenario-4 : exception from element constructor
433 SeveralBuilder<Dummy> builder;
434
435 builder.emplace<Num<3>>(42);
436 CHECK (1 == builder.size());
437
439 try {
440 builder.emplace<Num<3>>(23);
441 NOTREACHED ("did not throw");
442 }
443 catch(...)
444 {
445 // Exception emanated from Dummy(baseclass) ctor;
446 // at that point, the local val was set to the seed (≙23).
447 // When a constructor fails in C++, the destructor is not called,
448 // thus we have to compensate here to balance the checksum
449 Dummy::checksum() -= 23;
450 }
451 CHECK (1 == builder.size());
453 builder.emplace<Num<3>>(23);
454
455 auto elms = builder.build();
456 CHECK (2 == elms.size());
457 CHECK (elms.front().calc(1) == 3 + 1 + (42+42+42));
458 CHECK (elms.back().calc(5) == 3 + 5 + (23+23+23));
459 }
460 // all other destructors properly invoked...
461 CHECK (0 == Dummy::checksum());
462 }
463
464
465
477 void
479 {
480 auto loc = [](auto& something){ return util::addrID (something); };
481 auto calcSpread = [&](auto& several){ return loc(several[1]) - loc(several[0]); };
482
483 { // Scenario-1 : tightly packed values
484 Several<int> elms = makeSeveral({21,34,55}).build();
485 CHECK (21 == elms[0]);
486 CHECK (34 == elms[1]);
487 CHECK (55 == elms[2]);
488 CHECK (3 == elms.size());
489 CHECK (sizeof(elms) == sizeof(void*));
490
491 CHECK (sizeof(int) == alignof(int));
492 size_t spread = calcSpread (elms);
493 CHECK (spread == sizeof(int));
494 CHECK (loc(elms.back()) == loc(elms.front()) + 2*spread);
495 }
496
497 { // Scenario-2 : alignment
498 struct Ali
499 {
500 alignas(64)
501 char charm = 'u';
502 };
503
504 auto elms = makeSeveral<Ali>().fillElm(5).build();
505 CHECK (5 == elms.size());
506 CHECK (sizeof(elms) == sizeof(void*));
507
508 size_t spread = calcSpread (elms);
509 CHECK (spread == alignof(Ali));
510 CHECK (loc(elms.front()) % alignof(Ali) == 0);
511 CHECK (loc(elms.back()) == loc(elms.front()) + 4*spread);
512 }
513
514 { // Scenario-3 : noncopyable objects
515 auto elms = makeSeveral<ShortBlocker>().fillElm(5).build();
516
517 auto v0 = elms[0].val; auto p0 = loc(elms[0]);
518 auto v1 = elms[1].val; auto p1 = loc(elms[1]);
519 auto v2 = elms[2].val; auto p2 = loc(elms[2]);
520 auto v3 = elms[3].val; auto p3 = loc(elms[3]);
521 auto v4 = elms[4].val; auto p4 = loc(elms[4]);
522
523 CHECK (5 == elms.size());
524 auto moved = move(elms);
525 CHECK (5 == moved.size());
526 CHECK (loc(elms) != loc(moved));
527 CHECK (isnil (elms));
528
529 CHECK (loc(moved[0]) == p0);
530 CHECK (loc(moved[1]) == p1);
531 CHECK (loc(moved[2]) == p2);
532 CHECK (loc(moved[3]) == p3);
533 CHECK (loc(moved[4]) == p4);
534
535 CHECK (moved[0].val == v0);
536 CHECK (moved[1].val == v1);
537 CHECK (moved[2].val == v2);
538 CHECK (moved[3].val == v3);
539 CHECK (moved[4].val == v4);
540
541 CHECK (calcSpread(moved) == sizeof(ShortBlocker));
542 }
543 }
544
545
546
552 void
554 {
555 // Setup-1: use the TrackingAllocator
556 CHECK (0 == Dummy::checksum());
557 CHECK (0 == TrackingAllocator::checksum());
558
559 Several<Dummy> elms;
560 size_t expectedAlloc;
561 CHECK (0 == TrackingAllocator::numAlloc());
562 CHECK (0 == TrackingAllocator::use_count());
563 {
564 auto builder = makeSeveral<Dummy>()
565 .withAllocator<test::TrackAlloc>()
566 .fillElm(55);
567
568 size_t elmSiz = sizeof(Dummy);
569 size_t buffSiz = elmSiz * builder.capacity();
570 size_t headerSiz = sizeof(ArrayBucket<Dummy>);
571 expectedAlloc = headerSiz + buffSiz;
572
573 CHECK (TrackingAllocator::numBytes() == expectedAlloc);
574 CHECK (TrackingAllocator::numAlloc() == 1);
575 CHECK (TrackingAllocator::use_count()== 2); // one instance in the builder, one in the deleter
576 CHECK (TrackingAllocator::checksum() > 0);
577
578 elms = builder.build();
579 }
580 CHECK (elms.size() == 55);
581 CHECK (TrackingAllocator::numBytes() == expectedAlloc);
582 CHECK (TrackingAllocator::numAlloc() == 1);
583 CHECK (TrackingAllocator::use_count()== 1); // only one allocator instance in the deleter left
584
585 auto others = move(elms);
586 CHECK (elms.size() == 0);
587 CHECK (others.size() == 55);
588 CHECK (TrackingAllocator::numBytes() == expectedAlloc);
589 CHECK (TrackingAllocator::numAlloc() == 1);
590 CHECK (TrackingAllocator::use_count()== 1);
591
592 others = move(Several<Dummy>{}); // automatically triggers de-allocation
593 CHECK (others.size() == 0);
594
595 CHECK (0 == Dummy::checksum());
596 CHECK (0 == TrackingAllocator::numBytes());
597 CHECK (0 == TrackingAllocator::numAlloc());
598 CHECK (0 == TrackingAllocator::use_count());
599 CHECK (0 == TrackingAllocator::checksum());
600
601
602
603 {// Setup-2: use an AllocationCLuster instance
605 size_t allotted = clu.numBytes();
606 CHECK (allotted == 0);
607 {
608 auto builder = makeSeveral<Dummy>()
609 .withAllocator(clu)
610 .reserve(4) // use a limited pre-reservation
611 .fillElm(4); // fill all the allocated space with 4 new elements
612
613 size_t buffSiz = sizeof(Dummy) * builder.capacity();
614 size_t headerSiz = sizeof(ArrayBucket<Dummy>);
615 expectedAlloc = headerSiz + buffSiz;
616 CHECK (4 == builder.size());
617 CHECK (4 == builder.capacity());
618 CHECK (1 == clu.numExtents()); // AllocationCluster has only opened one extent thus far
619 CHECK (expectedAlloc == clu.numBytes()); // and the allocated space matches the demand precisely
620
621 builder.append (Dummy{23}); // now request to add just one further element
622 CHECK (8 == builder.capacity()); // ...which causes the builder to double up the reserve capacity
623
624 buffSiz = sizeof(Dummy) * builder.capacity();
625 expectedAlloc = headerSiz + buffSiz;
626 CHECK (1 == clu.numExtents()); // However, AllocationCluster was able to adjust allocation in-place
627 CHECK (expectedAlloc == clu.numBytes()); // and thus the new increased buffer is still placed in the first extent
628
629 // perform another unrelated allocation
630 Dummy& extraDummy = clu.create<Dummy>(55);
631 CHECK (1 == clu.numExtents());
632 CHECK (clu.numBytes() > expectedAlloc+sizeof(Dummy)); // but now we've used some further space behind that point
633
634 builder.reserve(9); // ...which means that the AllocationCluster can no longer adjust dynamically
635 CHECK (5 == builder.size()); // .....because this is only possible on the latest allocation opened
636 CHECK (9 <= builder.capacity()); // And while we still got the increased capacity as desired,
637 CHECK (2 == clu.numExtents()); // this was only possible by wasting space and copying into a new extent
638 buffSiz = sizeof(Dummy) * builder.capacity();
639 expectedAlloc = headerSiz + buffSiz;
640 CHECK (expectedAlloc <= AllocationCluster::max_size());
641 CHECK (clu.numBytes() == AllocationCluster::max_size()
642 + expectedAlloc);
643
644 allotted = clu.numBytes();
645 // request to throw away excess reserve
646 builder.shrinkFit();
647 CHECK (5 == builder.size());
648 CHECK (5 == builder.capacity());
649 CHECK (allotted > clu.numBytes()); // dynamic adjustment was possible, since it is the latest allocation
650 allotted = clu.numBytes();
651
652 elms = builder.build(); // Note: assigning to the existing front-end (which is storage agnostic)
653 CHECK (5 == elms.size());
654 CHECK (23 == elms.back().getVal());
655 CHECK (55 == extraDummy.getVal());
656 } // Now the Builder and the ExtraDummy is gone...
657 CHECK (5 == elms.size()); // while all created elements are still there, sitting in the Allocationcluster
658 CHECK (23 == elms.back().getVal());
659 CHECK (2 == clu.numExtents());
660 CHECK (clu.numBytes() == allotted);
661
662 CHECK (Dummy::checksum() > 0);
663 elms = move(Several<Dummy>{});
664 CHECK (Dummy::checksum() == 55); // all elements within Several were cleaned-up...
665 CHECK (2 == clu.numExtents()); // but the base allocation itself lives as long as the AllocationCluster
666 CHECK (clu.numBytes() == allotted);
667 }// AllocationCluster goes out of scope...
668 CHECK (Dummy::checksum() == 0); // now the (already unreachable) extraDummy was cleaned up
669 } // WARNING: contents in Several would now be dangling (if we haden't killed them)
670 };
671
672
673
676
677
678
679}} // namespace lib::test
Memory management for the low-level model (render nodes network).
A pile of objects sharing common allocation and lifecycle.
static constexpr size_t max_size()
Maximum individual allocation size that can be handled.
TY & create(ARGS &&...)
Builder to create and populate a lib::Several<I>.
SeveralBuilder && fillElm(size_t cntNew, ARGS &&...args)
emplace a number of elements of the defined element type E
size_t capReserve() const
SeveralBuilder && appendAll(IT &&data)
append a copy of all values exposed through an iterator
SeveralBuilder && emplace(ARGS &&...args)
create a new content element within the managed storage
SeveralBuilder && append(VAL &&val, VALS &&...vals)
append copies of one or several arbitrary elements
SeveralBuilder && reserve(size_t cntElm=1, size_t elmSiz=reqSiz< TY >())
ensure up-front that a desired capacity is allocated
Several< I > build()
Terminal Builder: complete and lock the collection contents.
Abstraction: Fixed array of elements.
Definition several.hpp:166
size_t size() const
Definition several.hpp:194
A Dummy object for tests.
static void activateCtorFailure(bool indeed=true)
static long & checksum()
C++ standard compliant custom allocator adapter backed by the TrackingAllocator and the MemoryPool de...
static size_t numAlloc(Literal pool=GLOBAL)
get active allocation count for mem-pool
static size_t numBytes(Literal pool=GLOBAL)
calculate currently allotted Bytes for mem-pool
static size_t use_count(Literal pool=GLOBAL)
determine number of active front-end handles
static HashVal checksum(Literal pool=GLOBAL)
get Checksum for specific mem-pool
Num(Num &&oNum) noexcept
allow for move construction
Any copy and copy construction prohibited.
Definition nocopy.hpp:38
#define LERR_(_NAME_)
Definition error.hpp:45
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
unsigned int uint
Definition integral.hpp:29
Building tree expanding and backtracking evaluations within hierarchical scopes.
std::vector< int > VecI
Definition test-coll.hpp:34
Implementation namespace for support and library code.
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
int rani(uint bound=_iBOUND())
Definition random.hpp:135
SeveralBuilder< I, E > makeSeveral()
Entrance Point: start building a lib::Several instance.
Test runner and basic definitions for tests.
constexpr bool isLimited(NB lowerBound, NUM val, NB upperBound)
Definition util.hpp:99
size_t addrID(X const &x)
generate an unique numeric ID based on the referred entity
Definition util.hpp:391
std::string toString(TY const &val) noexcept
get some string representation of any object, reliably.
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Definition run.hpp:116
Builder to create and populate instances of the lib::Several container.
some bits of unit test helper code to fabricate collections with test data
A collection of frequently used helper functions to support unit testing.
#define VERIFY_FAIL(FAILURE_MSG, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises a std::exception, which additionally contains some FAI...
#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT)
Macro to verify that a statement indeed raises an exception.
Unittest helper code: a custom allocator to track memory usage.
unittest helper code: test dummy objects to track instances.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...