2023-07-03 18:40:37 +02:00
/*
BlockFlow ( Test ) - verify scheduler memory management scheme
Copyright ( C ) Lumiera . org
2023 , 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 block-flow-test.cpp
* * unit test \ ref BlockFlow_test
*/
# include "lib/test/run.hpp"
2023-07-05 15:10:34 +02:00
# include "lib/test/test-helper.hpp"
2023-07-03 18:40:37 +02:00
# include "vault/gear/block-flow.hpp"
//#include "lib/time/timevalue.hpp"
//#include "lib/format-cout.hpp"
2023-07-13 03:41:24 +02:00
# include "lib/test/diagnostic-output.hpp" ////////////////////////////////TODO
2023-07-14 01:51:00 +02:00
# include "lib/util.hpp"
2023-07-03 18:40:37 +02:00
//#include <utility>
using test : : Test ;
//using std::move;
2023-07-14 01:51:00 +02:00
using util : : isSameObject ;
2023-07-05 15:10:34 +02:00
using lib : : test : : randTime ;
2023-07-14 01:51:00 +02:00
using lib : : test : : showType ;
2023-07-03 18:40:37 +02:00
namespace vault {
2023-07-05 15:10:34 +02:00
namespace gear {
2023-07-03 18:40:37 +02:00
namespace test {
// using lib::time::FrameRate;
// using lib::time::Offset;
// using lib::time::Time;
/*****************************************************************/ /**
* @ test document the memory management scheme used by the Scheduler .
* @ see SchedulerActivity_test
* @ see SchedulerUsage_test
*/
class BlockFlow_test : public Test
{
virtual void
run ( Arg )
{
simpleUsage ( ) ;
2023-07-13 01:51:21 +02:00
verifyAPI ( ) ;
handleEpoch ( ) ;
placeActivity ( ) ;
adjustEpochs ( ) ;
storageFlow ( ) ;
2023-07-03 18:40:37 +02:00
}
2023-07-13 19:19:55 +02:00
/** @test demonstrate a simple usage scenario
* - open new Epoch to allocate an Activity
* - clean - up at a future time point
2023-07-03 18:40:37 +02:00
*/
void
simpleUsage ( )
{
2023-07-05 15:10:34 +02:00
BlockFlow bFlow ;
Time deadline = randTime ( ) ;
2023-07-13 03:41:24 +02:00
2023-07-13 01:51:21 +02:00
Activity & tick = bFlow . until ( deadline ) . create ( ) ;
2023-07-13 03:41:24 +02:00
CHECK ( tick . verb_ = = Activity : : TICK ) ;
CHECK ( 1 = = watch ( bFlow ) . cntEpochs ( ) ) ;
CHECK ( watch ( bFlow ) . first ( ) > deadline ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . first ( ) - deadline = = bFlow . getEpochStep ( ) ) ;
2023-07-05 15:10:34 +02:00
bFlow . discardBefore ( deadline + Time { 0 , 5 } ) ;
2023-07-13 03:41:24 +02:00
CHECK ( 0 = = watch ( bFlow ) . cntEpochs ( ) ) ;
2023-07-03 18:40:37 +02:00
}
2023-07-13 01:51:21 +02:00
/** @test verify the primary BlockFlow API functions in isolation
* @ todo WIP 7 / 23 ⟶ define ⟶ implement
2023-07-03 18:40:37 +02:00
*/
void
2023-07-13 01:51:21 +02:00
verifyAPI ( )
2023-07-03 18:40:37 +02:00
{
2023-07-13 19:19:55 +02:00
//SHOW_EXPR(watch(bFlow).cntEpochs());
//SHOW_EXPR(watch(bFlow).poolSize());
//SHOW_EXPR(watch(bFlow).first());
2023-07-03 18:40:37 +02:00
}
2023-07-14 01:51:00 +02:00
/** @test cover properties and handling of Epochs (low-level)
* - demonstrate that Epoch is placed into an Extent
* - verify that both Extent and Epoch access the same memory block
* - demonstrate the standard setup and initialisation of an Epoch
* - allocate some Activities into the storage and observe free - managment
* - detect when the Epoch is filled up
* - verify alive / dead decision relative to given deadline
* @ note this test covers helpers and implementation structures of BlockFlow ,
* without actually using a BlockFlow instance ; rather , the typical handling
* and low - level bookkeeping aspects are emulated and observed
2023-07-03 18:40:37 +02:00
*/
void
2023-07-13 01:51:21 +02:00
handleEpoch ( )
{
2023-07-14 01:51:00 +02:00
using Extent = Allocator : : Extent ;
// the raw storage Extent is a compact block
// providing uninitialised storage typed as `vault::gear::Activity`
Allocator alloc ;
alloc . openNew ( ) ;
Extent & extent = * alloc . begin ( ) ;
CHECK ( extent . size ( ) = = Extent : : SIZ : : value ) ;
CHECK ( sizeof ( extent ) = = extent . size ( ) * sizeof ( Activity ) ) ;
CHECK ( showType < Extent : : value_type > ( ) = = " vault::gear::Activity " _expect ) ;
// we can just access some slot and place data there
extent [ 55 ] . data_ . feed . one = 555555555555555 ;
// now establish an Epoch in this storage block:
Epoch & epoch = Epoch : : setup ( alloc . begin ( ) , Time { 0 , 10 } ) ;
// the underlying storage is not touched yet...
CHECK ( epoch [ 55 ] . data_ . feed . one = = 555555555555555 ) ;
// but in the first slot, an »EpochGate« has been implanted
Epoch : : EpochGate & gate = epoch . gate ( ) ;
CHECK ( isSameObject ( gate , epoch [ 0 ] ) ) ;
CHECK ( isSameObject ( epoch [ 0 ] , extent [ 0 ] ) ) ;
CHECK ( Time { gate . deadline ( ) } = = Time ( 0 , 10 ) ) ;
CHECK ( Time { gate . deadline ( ) } = = Time { epoch [ 0 ] . data_ . condition . dead } ) ;
CHECK ( Activity : : GATE = = epoch [ 0 ] . verb_ ) ;
// the gate's `next`-pointer is (ab)used to manage the next allocation slot
CHECK ( isSameObject ( * gate . next , epoch [ extent . size ( ) - 1 ] ) ) ;
// the storage there is not yet used, but will be overwritten by the ctor call
epoch [ extent . size ( ) - 1 ] . data_ . timing . instant = Time { 5 , 5 } ;
2023-07-16 18:03:27 +02:00
// allocate a new Activity into the next free slot (using a faked AllocatorHandle)
BlockFlow : : AllocatorHandle allocHandle { alloc . begin ( ) , nullptr } ;
2023-07-14 01:51:00 +02:00
Activity & timeStart = allocHandle . create ( Activity : : TIMESTART ) ;
CHECK ( isSameObject ( timeStart , epoch [ extent . size ( ) - 1 ] ) ) ;
// this Activity object is properly initialised (and memory was altered)
CHECK ( epoch [ extent . size ( ) - 1 ] . data_ . timing . instant ! = Time ( 5 , 5 ) ) ;
CHECK ( epoch [ extent . size ( ) - 1 ] . data_ . timing . instant = = Time : : NEVER ) ;
CHECK ( timeStart . verb_ = = Activity : : TIMESTART ) ;
CHECK ( timeStart . data_ . timing . instant = = Time : : NEVER ) ;
CHECK ( timeStart . data_ . timing . quality = = 0 ) ;
// and the free-pointer was decremented to point to the next free slot
CHECK ( isSameObject ( * gate . next , epoch [ extent . size ( ) - 2 ] ) ) ;
// which also implies that there is still ample space left...
CHECK ( gate . hasFreeSlot ( ) ) ;
// so let's eat this space up...
for ( uint i = extent . size ( ) - 2 ; i > 1 ; - - i )
2023-07-16 18:03:27 +02:00
gate . claimNextSlot ( ) ;
2023-07-14 01:51:00 +02:00
// one final slot is left (beyond of the EpochGate itself)
CHECK ( isSameObject ( * gate . next , epoch [ 1 ] ) ) ;
CHECK ( gate . hasFreeSlot ( ) ) ;
2023-07-16 18:03:27 +02:00
gate . claimNextSlot ( ) ;
2023-07-14 01:51:00 +02:00
// aaand the boat is full...
CHECK ( not gate . hasFreeSlot ( ) ) ;
CHECK ( isSameObject ( * gate . next , epoch [ 0 ] ) ) ;
// a given Epoch can be checked for relevance against a deadline
CHECK ( gate . deadline ( ) = = Time ( 0 , 10 ) ) ;
CHECK ( gate . isAlive ( Time ( 0 , 5 ) ) ) ;
CHECK ( gate . isAlive ( Time ( 999 , 9 ) ) ) ;
CHECK ( not gate . isAlive ( Time ( 0 , 10 ) ) ) ;
CHECK ( not gate . isAlive ( Time ( 1 , 10 ) ) ) ;
////////////////////////////////////////////////////////////////////////////////////////TICKET #1298 : actually use a GATE implementation and then also check the count-down latch
2023-07-13 01:51:21 +02:00
}
/** @test TODO place Activity record into storage
2023-07-14 02:58:00 +02:00
* @ todo WIP 7 / 23 ⟶ ✔ define ⟶ 🔁 implement
2023-07-13 01:51:21 +02:00
*/
void
placeActivity ( )
{
2023-07-14 02:58:00 +02:00
BlockFlow bFlow ;
Time t1 = Time { 0 , 10 } ;
Time t2 = Time { 500 , 10 } ;
Time t3 = Time { 0 , 11 } ;
auto & a1 = bFlow . until ( t1 ) . create ( ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 10s200ms " _expect ) ;
CHECK ( watch ( bFlow ) . find ( a1 ) = = " 10s200ms " _expect ) ;
2023-07-14 02:58:00 +02:00
auto & a3 = bFlow . until ( t3 ) . create ( ) ;
2023-07-15 21:37:58 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 10s200ms|10s400ms|10s600ms|10s800ms|11s " _expect ) ;
CHECK ( watch ( bFlow ) . find ( a3 ) = = " 11s " _expect ) ;
2023-07-14 02:58:00 +02:00
auto & a2 = bFlow . until ( t2 ) . create ( ) ;
2023-07-15 21:37:58 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 10s200ms|10s400ms|10s600ms|10s800ms|11s " _expect ) ;
CHECK ( watch ( bFlow ) . find ( a2 ) = = " 10s600ms " _expect ) ;
2023-07-14 02:58:00 +02:00
Time t0 = Time { 0 , 5 } ;
auto & a0 = bFlow . until ( t0 ) . create ( ) ;
2023-07-15 21:37:58 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 10s200ms|10s400ms|10s600ms|10s800ms|11s " _expect ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . find ( a0 ) = = " 10s200ms " _expect ) ;
2023-07-14 02:58:00 +02:00
BlockFlow : : AllocatorHandle allocHandle = bFlow . until ( Time { 300 , 10 } ) ;
for ( uint i = 1 ; i < Epoch : : SIZ ( ) ; + + i )
allocHandle . create ( ) ;
CHECK ( allocHandle . currDeadline ( ) = = Time ( 400 , 10 ) ) ;
CHECK ( not allocHandle . hasFreeSlot ( ) ) ;
2023-07-16 03:06:02 +02:00
auto & a4 = allocHandle . create ( ) ;
2023-07-14 02:58:00 +02:00
CHECK ( allocHandle . currDeadline ( ) = = Time ( 600 , 10 ) ) ;
CHECK ( allocHandle . hasFreeSlot ( ) ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . find ( a4 ) = = " 10s600ms " _expect ) ;
2023-07-14 02:58:00 +02:00
for ( uint i = 1 ; i < Epoch : : SIZ ( ) ; + + i )
allocHandle . create ( ) ;
CHECK ( allocHandle . currDeadline ( ) = = Time ( 800 , 10 ) ) ;
2023-07-16 03:06:02 +02:00
CHECK ( allocHandle . hasFreeSlot ( ) ) ;
2023-07-14 02:58:00 +02:00
auto & a5 = bFlow . until ( Time { 220 , 10 } ) . create ( ) ;
2023-07-16 03:06:02 +02:00
CHECK ( watch ( bFlow ) . find ( a5 ) = = " 10s800ms " _expect ) ;
2023-07-14 02:58:00 +02:00
allocHandle = bFlow . until ( Time { 900 , 10 } ) ;
2023-07-16 03:06:02 +02:00
for ( uint i = 2 ; i < Epoch : : SIZ ( ) ; + + i )
2023-07-14 02:58:00 +02:00
allocHandle . create ( ) ;
2023-07-16 03:06:02 +02:00
CHECK ( allocHandle . currDeadline ( ) = = Time ( 0 , 11 ) ) ;
2023-07-14 02:58:00 +02:00
CHECK ( not allocHandle . hasFreeSlot ( ) ) ;
auto & a6 = bFlow . until ( Time { 850 , 10 } ) . create ( ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . find ( a6 ) = = " 11s150ms " _expect ) ;
2023-07-15 21:37:58 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 10s200ms|10s400ms|10s600ms|10s800ms|11sms|11s150ms " _expect ) ;
2023-07-14 02:58:00 +02:00
auto & a7 = bFlow . until ( Time { 500 , 11 } ) . create ( ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . find ( a7 ) = = " 11s600ms " _expect ) ;
2023-07-15 21:37:58 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 10s200ms|10s400ms|10s600ms|10s800ms|11sms|11s150ms|11s300ms|11s450ms|11s600ms " _expect ) ;
2023-07-14 02:58:00 +02:00
bFlow . discardBefore ( Time { 999 , 10 } ) ;
2023-07-15 21:37:58 +02:00
CHECK ( watch ( bFlow ) . allEpochs ( ) = = " 11s|11s150ms|11s300ms|11s450ms|11s600ms " _expect ) ;
2023-07-14 02:58:00 +02:00
auto & a8 = bFlow . until ( Time { 500 , 10 } ) . create ( ) ;
2023-07-15 18:54:59 +02:00
CHECK ( watch ( bFlow ) . find ( a8 ) = = " 11s150ms " _expect ) ;
2023-07-13 01:51:21 +02:00
}
/** @test TODO load based regulation of Epoch spacing
2023-07-14 02:58:00 +02:00
* @ todo WIP 7 / 23 ⟶ 🔁 define ⟶ implement
2023-07-13 01:51:21 +02:00
*/
void
adjustEpochs ( )
{
2023-07-15 18:54:59 +02:00
BlockFlow bFlow ;
CHECK ( bFlow . getEpochStep ( ) = = INITIAL_EPOCH_STEP ) ;
bFlow . markEpochOverflow ( ) ;
CHECK ( bFlow . getEpochStep ( ) = = INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR ) ;
bFlow . markEpochOverflow ( ) ;
CHECK ( bFlow . getEpochStep ( ) = = INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR * OVERFLOW_BOOST_FACTOR ) ;
Duration dur1 = INITIAL_EPOCH_STEP ;
Duration dur2 = INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR ;
TimeVar step = bFlow . getEpochStep ( ) ;
Rat fill = 8 _r / 10 ;
Rat N = AVERAGE_EPOCHS ;
bFlow . markEpochUnderflow ( dur1 , fill ) ;
CHECK ( bFlow . getEpochStep ( ) = = step * ( ( N - 1 ) / N ) + dur1 * ( 1 / N / fill ) ) ;
step = bFlow . getEpochStep ( ) ;
fill = 3 _r / 10 ;
bFlow . markEpochUnderflow ( dur2 , fill ) ;
CHECK ( bFlow . getEpochStep ( ) = = step * ( ( N - 1 ) / N ) + dur2 * ( 1 / N / fill ) ) ;
2023-07-13 01:51:21 +02:00
}
/** @test TODO maintain progression of epochs.
* @ todo WIP 7 / 23 ⟶ define ⟶ implement
*/
void
storageFlow ( )
2023-07-03 18:40:37 +02:00
{
}
} ;
/** Register this test class... */
LAUNCHER ( BlockFlow_test , " unit engine " ) ;
2023-07-05 15:10:34 +02:00
} } } // namespace vault::gear::test