2024-05-12 19:05:50 +02:00
/*
SeveralBuilder ( Test ) - building a limited fixed collection of elements
Copyright ( C ) Lumiera . org
2008 , Hermann Vosseler < Ichthyostega @ web . de >
2024 , Hermann Vosseler < Ichthyostega @ web . de >
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of
the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/** @file several-builder-test.cpp
* * unit test \ ref SeveralBuilder_test
*/
# include "lib/test/run.hpp"
2024-05-28 04:03:51 +02:00
# include "lib/test/testdummy.hpp"
2024-06-11 18:23:48 +02:00
# include "lib/test/test-coll.hpp"
2024-05-28 04:03:51 +02:00
# include "lib/test/test-helper.hpp"
# include "lib/test/diagnostic-output.hpp" ////////////////TODO
# include "lib/iter-explorer.hpp"
# include "lib/format-util.hpp"
2024-05-12 19:05:50 +02:00
# include "lib/util.hpp"
# include "lib/several-builder.hpp"
# include <vector>
2024-05-28 04:03:51 +02:00
# include <array>
2024-05-12 19:05:50 +02:00
using : : test : : Test ;
using std : : vector ;
2024-06-11 23:53:38 +02:00
using std : : array ;
2024-05-12 19:05:50 +02:00
using std : : rand ;
2024-05-28 04:03:51 +02:00
using lib : : explore ;
2024-06-11 18:23:48 +02:00
using util : : isLimited ;
using util : : isnil ;
2024-05-28 04:03:51 +02:00
using util : : join ;
2024-05-12 19:05:50 +02:00
namespace lib {
namespace test {
2024-05-28 04:03:51 +02:00
namespace { // diagnostic test types
2024-05-12 19:05:50 +02:00
2024-05-28 04:03:51 +02:00
/**
* Instance tracking sub - dummy
* - implements the Dummy interface
* - holds additional storage
* - specific implementation of the virtual operation
* - includes content of the additional storage into the
* checksum calculation , allowing to detect memory corruption
*/
template < uint i >
class Num
: public test : : Dummy
2024-05-12 19:05:50 +02:00
{
2024-05-28 04:03:51 +02:00
std : : array < int , i > ext_ ;
2024-05-12 19:05:50 +02:00
2024-05-28 04:03:51 +02:00
public :
Num ( uint seed = i )
2024-06-11 18:23:48 +02:00
: Dummy ( seed )
2024-05-12 19:05:50 +02:00
{
2024-05-28 04:03:51 +02:00
ext_ . fill ( seed ) ;
setVal ( ( i + 1 ) * seed ) ;
2024-05-12 19:05:50 +02:00
}
2024-05-28 04:03:51 +02:00
~ Num ( )
{
setVal ( getVal ( ) - explore ( ext_ ) . resultSum ( ) ) ;
}
long
2024-06-11 18:23:48 +02:00
calc ( int ii ) override
2024-05-28 04:03:51 +02:00
{
return i + ii + explore ( ext_ ) . resultSum ( ) ;
2024-05-12 19:05:50 +02:00
}
2024-06-12 03:23:20 +02:00
/// allow for move construction
Num ( Num & & oNum ) noexcept
: Num ( 0 )
{
swap ( * this , oNum ) ;
}
Num &
operator = ( Num & & oNum )
{
if ( & oNum ! = this )
swap ( * this , oNum ) ;
return * this ;
}
friend void
swap ( Num & num1 , Num & num2 ) ///< checksum neutral
{
std : : swap ( static_cast < Dummy & > ( num1 )
, static_cast < Dummy & > ( num2 ) ) ;
std : : swap ( num1 . ext_ , num2 . ext_ ) ;
}
2024-05-12 19:05:50 +02:00
} ;
} // (END) test types
/***************************************************************/ /**
2024-05-28 04:03:51 +02:00
* @ test use lib : : Several to establish small collections of elements ,
* possibly with sub - classing and controlled allocation .
* - the container is populated through a separate builder
* - the number of elements is flexible during population
* - the actual container allows random - access via base interface
2024-05-12 19:05:50 +02:00
* @ see several - builder . hpp
*/
class SeveralBuilder_test : public Test
{
virtual void
run ( Arg )
{
2024-05-28 04:03:51 +02:00
simpleUsage ( ) ;
check_Builder ( ) ;
check_ErrorHandling ( ) ;
2024-06-11 18:23:48 +02:00
check_ElementStorage ( ) ;
2024-05-28 04:03:51 +02:00
check_CustomAllocator ( ) ;
2024-05-12 19:05:50 +02:00
}
2024-06-11 02:48:23 +02:00
/** @test demonstrate basic behaviour
* @ todo WIP 6 / 24 ✔ define ⟶ ✔ implement
2024-05-28 04:03:51 +02:00
*/
2024-05-12 19:05:50 +02:00
void
2024-05-28 04:03:51 +02:00
simpleUsage ( )
2024-05-12 19:05:50 +02:00
{
2024-05-28 04:03:51 +02:00
auto elms = makeSeveral ( { 1 , 1 , 2 , 3 , 5 , 8 , 13 } ) . build ( ) ;
CHECK ( elms . size ( ) = = 7 ) ;
CHECK ( elms . back ( ) = = 13 ) ;
CHECK ( elms [ 3 ] = = 3 ) ;
CHECK ( join ( elms , " - " ) = = " 1-1-2-3-5-8-13 " _expect ) ;
2024-05-12 19:05:50 +02:00
}
2024-05-28 04:03:51 +02:00
2024-06-12 00:57:57 +02:00
/** @test various ways to build an populate the container
* - with a defined interface type \ a I , instances of arbitrary subclasses
* can be added , assuming there is sufficient pre - allocated buffer space ;
* all these subclass instances are accessed through the common interface .
* - yet the added elements can also be totally unrelated , in which case an
* * unchecked wild cast * will happen on access ; while certainly dangerous ,
* this behaviour allows for special low - level data layout tricks .
* - the results from an iterator can be used to populate by copy
2024-06-11 18:23:48 +02:00
* @ todo WIP 6 / 24 🔁 define ⟶ ✔ implement
2024-05-28 04:03:51 +02:00
*/
2024-05-12 19:05:50 +02:00
void
2024-05-28 04:03:51 +02:00
check_Builder ( )
2024-05-12 19:05:50 +02:00
{
2024-06-11 23:53:38 +02:00
{ // Scenario-1 : Baseclass and arbitrary subclass elements
SeveralBuilder < Dummy > builder ;
CHECK ( isnil ( builder ) ) ;
builder . emplace < Num < 3 > > ( )
. emplace < Num < 2 > > ( 1 ) ;
CHECK ( 2 = = builder . size ( ) ) ;
builder . fillElm ( 2 ) ;
CHECK ( 4 = = builder . size ( ) ) ;
builder . fillElm ( 3 , 5 ) ;
CHECK ( 7 = = builder . size ( ) ) ;
Several < Dummy > elms = builder . build ( ) ;
CHECK ( isnil ( builder ) ) ;
CHECK ( not isnil ( elms ) ) ;
CHECK ( 7 = = elms . size ( ) ) ;
CHECK ( elms [ 0 ] . getVal ( ) = = ( 3 + 1 ) * 3 ) ; // indeed a Num<3> with default-seed ≡ 3
CHECK ( elms [ 0 ] . calc ( 1 ) = = 3 + 1 + ( 3 + 3 + 3 ) ) ; // indeed called the overridden calc() operation
CHECK ( elms [ 1 ] . getVal ( ) = = ( 2 + 1 ) * 1 ) ; // indeed a Num<2> with seed ≡ 1
CHECK ( elms [ 1 ] . calc ( 1 ) = = 2 + 1 + ( 1 + 1 ) ) ; // indeed the overridden calc() picking from the Array(1,1)
CHECK ( isLimited ( 1 , elms [ 2 ] . getVal ( ) , 100'000'000 ) ) ; // indeed a Dummy with default random seed
CHECK ( isLimited ( 1 , elms [ 3 ] . getVal ( ) , 100'000'000 ) ) ; // and this one too, since we filled in two instances
CHECK ( elms [ 4 ] . getVal ( ) = = 5 ) ; // followed by tree instances Dummy(5)
CHECK ( elms [ 5 ] . getVal ( ) = = 5 ) ;
CHECK ( elms [ 6 ] . getVal ( ) = = 5 ) ;
CHECK ( elms [ 6 ] . calc ( 1 ) = = 5 + 1 ) ; // indeed invoking the base implementation of calc()
}
2024-06-11 18:23:48 +02:00
2024-06-11 23:53:38 +02:00
{ // Scenario-2 : unrelated element types
SeveralBuilder < uint32_t > builder ;
auto urgh = array < char , 5 > { " Urgh " } ;
auto phi = ( 1 + sqrtf ( 5 ) ) / 2 ;
builder . append ( urgh , phi , - 1 ) ; // can emplace arbitrary data
CHECK ( 3 = = builder . size ( ) ) ;
Several < uint32_t > elms = builder . build ( ) ; // WARNING: data accessed by wild cast to interface type
CHECK ( 3 = = elms . size ( ) ) ;
CHECK ( elms [ 0 ] = = * reinterpret_cast < const uint32_t * > ( " Urgh " ) ) ;
CHECK ( elms [ 1 ] = = * reinterpret_cast < uint32_t * > ( & phi ) ) ;
CHECK ( elms [ 2 ] = = uint32_t ( - 1 ) ) ;
}
2024-06-11 18:23:48 +02:00
2024-06-11 23:53:38 +02:00
{ // Scenario-3 : copy values from iterator
SeveralBuilder < int > builder ;
VecI seq = getTestSeq_int < VecI > ( 10 ) ;
builder . appendAll ( seq ) ;
CHECK ( 10 = = builder . size ( ) ) ;
auto elms = builder . build ( ) ;
CHECK ( 10 = = elms . size ( ) ) ;
CHECK ( join ( elms , " - " ) = = " 0-1-2-3-4-5-6-7-8-9 " _expect ) ;
}
2024-05-12 19:05:50 +02:00
}
2024-05-28 04:03:51 +02:00
/** @test TODO proper handling of exceptions during population
2024-06-10 18:52:25 +02:00
* @ todo WIP 6 / 24 🔁 define ⟶ implement
2024-05-28 04:03:51 +02:00
*/
2024-05-12 19:05:50 +02:00
void
2024-05-28 04:03:51 +02:00
check_ErrorHandling ( )
2024-05-12 19:05:50 +02:00
{
2024-06-12 00:57:57 +02:00
{ // Scenario-1 : Baseclass and arbitrary subclass elements
SeveralBuilder < Dummy > builder ;
// The first element will _prime_ the container for a suitable usage pattern
builder . emplace < Num < 1 > > ( ) ;
CHECK ( 1 = = builder . size ( ) ) ;
// Notably the first element established the _spread_ between index positions,
// which effectively limits the size of objects to be added. Moreover, since
// the element type was detected to be non-trivial, we can not correct this
// element spacing by shifting existing allocations (`memmove()` not possible)
CHECK ( sizeof ( Num < 1 > ) < sizeof ( Num < 5 > ) ) ;
VERIFY_FAIL ( " Unable to place element of type Num<5u> (size= "
, builder . emplace < Num < 5 > > ( ) ) ;
CHECK ( 1 = = builder . size ( ) ) ;
// Furthermore, the first element was detected to be a subclass,
// and the interface type `Dummy` has a virtual destructor;
// all added elements must comply to this scheme, once established
VERIFY_FAIL ( " Unable to handle (trivial-)destructor for element type long, "
" since this container has been primed to use virtual-baseclass-destructors. "
, builder . emplace < long > ( 55 ) ) ;
CHECK ( 1 = = builder . size ( ) ) ;
// the initial allocation added some reserve buffer space (for 10 elements)
// and we can fill that space with arbitrary subclass instances
builder . fillElm ( 5 ) ;
CHECK ( 6 = = builder . size ( ) ) ;
// But the initial allocation can not be increased, since that would require
// a re-allocation of a larger buffer, followed by copying the elements;
// but since the established scheme allows for _arbitrary_ subclasses,
// the builder does not know the exact type for safe element relocation.
VERIFY_FAIL ( " Unable to accommodate further element of type Dummy "
, builder . fillElm ( 20 ) ) ;
CHECK ( 10 = = builder . size ( ) ) ;
}
2024-06-12 03:23:20 +02:00
{ // Scenario-2 : Baseclass and elements of a single fixed subclass
SeveralBuilder < Dummy , Num < 5 > > builder ;
Dummy dum ;
SHOW_EXPR ( dum . getVal ( ) )
Dummy nem { move ( dum ) } ;
SHOW_EXPR ( dum . getVal ( ) )
SHOW_EXPR ( nem . getVal ( ) )
Num < 5 > no5 ;
SHOW_EXPR ( no5 . getVal ( ) )
Num < 5 > ne5 { move ( no5 ) } ;
SHOW_EXPR ( no5 . getVal ( ) )
SHOW_EXPR ( ne5 . getVal ( ) )
builder . fillElm ( 5 ) ;
CHECK ( 5 = = builder . size ( ) ) ;
builder . emplace < Num < 1 > > ( ) ; ///////////////////////////////////OOO this should trigger an exception -> need to code an explicit check right at the start of emplaceNewElm()
}
2024-05-12 19:05:50 +02:00
}
2024-06-11 18:23:48 +02:00
/** @test TODO verify correct placement of instances within storage
2024-06-10 18:52:25 +02:00
* @ todo WIP 6 / 24 🔁 define ⟶ implement
2024-05-28 04:03:51 +02:00
*/
2024-05-12 19:05:50 +02:00
void
2024-06-11 18:23:48 +02:00
check_ElementStorage ( )
2024-05-12 19:05:50 +02:00
{
}
2024-05-28 04:03:51 +02:00
/** @test TODO demonstrate integration with a custom allocator
2024-06-10 18:52:25 +02:00
* @ todo WIP 6 / 24 🔁 define ⟶ implement
2024-05-28 04:03:51 +02:00
*/
2024-05-12 19:05:50 +02:00
void
2024-05-28 04:03:51 +02:00
check_CustomAllocator ( )
2024-05-12 19:05:50 +02:00
{
}
} ;
/** Register this test class... */
LAUNCHER ( SeveralBuilder_test , " unit common " ) ;
} } // namespace lib::test