LUMIERA.clone/tests/vault/gear/scheduler-invocation-test.cpp
Ichthyostega 72258c06bd Scheduler: reconciled into clearer design
The problem with passing the deadline was just a blatant symptom
that something with the overall design was not quite right, leading
to mix-up of interfaces and implementation functions, and more and more
detail parameters spreading throughout the call chains.

The turning point was to realise the two conceptual levels
crossing and interconnected within the »Scheduler-Service«

- the Activity-Language describes the patterns of processing
- the Scheduler components handle time-bound events

So by turning the (previously private) queue entry into an
ActivationEvent, the design could be balanced.
This record becomes the common agens within the Scheduler,
and builds upon / layers on top of the common agens of the
Language, which is the Activity record.
2023-11-04 04:49:13 +01:00

293 lines
10 KiB
C++

/*
SchedulerInvocation(Test) - verify queue processing in the scheduler
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 02139, USA.
* *****************************************************/
/** @file scheduler-invocation-test.cpp
** unit test \ref SchedulerInvocation_test
*/
#include "lib/test/run.hpp"
#include "vault/gear/scheduler-invocation.hpp"
#include "lib/util.hpp"
using test::Test;
using util::isSameObject;
namespace vault{
namespace gear {
namespace test {
/********************************************************************//**
* @test Scheduler Layer-1: queue processing and invocation by priority.
* @see SchedulerCommutator_test
* @see SchedulerUsage_test
*/
class SchedulerInvocation_test : public Test
{
virtual void
run (Arg)
{
simpleUsage();
verify_Queuing();
verify_WaterLevel();
verify_Significance();
verify_stability();
verify_isDue();
}
/** @test demonstrate a simple usage scenario of data passing
*/
void
simpleUsage()
{
SchedulerInvocation sched;
Activity activity;
Time when{1,2,3};
Time dead{2,3,4};
CHECK (not sched.peekHead());
sched.instruct ({activity, when, dead});
sched.feedPrioritisation();
CHECK (sched.peekHead());
Activity* head = sched.pullHead();
CHECK (not sched.peekHead());
CHECK (isSameObject (*head, activity));
}
/** @test verify records are passed properly through the queues
* - add multiple elements to the instruct queue
* - after `feedPrioritisation` these appear as output
*/
void
verify_Queuing()
{
SchedulerInvocation sched;
Activity one{1u,1u};
Activity two{2u,2u};
Activity wee{3u,3u};
Time t{5,5};
sched.instruct ({one, t});
sched.instruct ({two, t});
sched.instruct ({wee, t});
CHECK (not sched.peekHead());
sched.feedPrioritisation();
CHECK (isSameObject (*sched.pullHead(), one));
CHECK (isSameObject (*sched.pullHead(), two));
CHECK (isSameObject (*sched.pullHead(), wee));
CHECK (not sched.peekHead());
CHECK (sched.empty());
}
/** @test verify the given time point is utilised for prioritisation
* - order at output is determined by the time spec
* - even later added elements can push back
* previously visible elements at head
*/
void
verify_WaterLevel()
{
SchedulerInvocation sched;
Activity a1{1u,1u};
Activity a2{2u,2u};
Activity a3{3u,3u};
Activity a4{4u,4u};
sched.instruct ({a2, Time{2,0}});
sched.instruct ({a4, Time{4,0}});
sched.feedPrioritisation();
CHECK (isSameObject (*sched.peekHead(), a2));
sched.instruct ({a3, Time{3,0}});
sched.instruct ({a1, Time{1,0}});
CHECK (isSameObject (*sched.peekHead(), a2));
sched.feedPrioritisation();
CHECK (isSameObject (*sched.pullHead(), a1));
CHECK (isSameObject (*sched.pullHead(), a2));
CHECK (isSameObject (*sched.pullHead(), a3));
CHECK (isSameObject (*sched.pullHead(), a4));
}
/** @test sort order is not necessarily stable
* if using identical time specs on entrance
*/
void
verify_stability()
{
SchedulerInvocation sched;
Activity a1{1u,1u};
Activity a2{2u,2u};
Activity a3{3u,3u};
Activity a4{4u,4u};
sched.feedPrioritisation ({a1, Time{0,5}});
sched.feedPrioritisation ({a2, Time{0,5}});
sched.feedPrioritisation ({a3, Time{0,5}});
sched.feedPrioritisation ({a4, Time{0,4}});
CHECK (isSameObject (*sched.pullHead(), a4));
CHECK (isSameObject (*sched.pullHead(), a3));
CHECK (isSameObject (*sched.pullHead(), a1));
CHECK (isSameObject (*sched.pullHead(), a2));
CHECK (not sched.pullHead());
}
/** @test the entry appearing at head _is due_
* when its time is at or before current time.
*/
void
verify_isDue()
{
SchedulerInvocation sched;
Activity a1{1u,1u};
sched.feedPrioritisation ({a1, Time{0,5}});
CHECK (isSameObject (*sched.peekHead(), a1));
CHECK ( sched.isDue (Time{0,10}));
CHECK ( sched.isDue (Time{0,5}));
CHECK (not sched.isDue (Time{0,1}));
sched.pullHead();
CHECK (not sched.peekHead());
CHECK (not sched.isDue (Time{0,1}));
CHECK (not sched.isDue (Time{0,10}));
}
/** @test verify extended classification data and information functions at scheduler head
* - add entries providing extra information regarding significance
* - verify that missing the deadline is detected
* - entries past deadline are marked _outdated_ (will be dropped by Layer-2)
* - entries can be tagged with an ManifestationID, allowing to enable
* or disable visibility for a »family« of schedule entries
* - use the flag for _compulsory_ entries, allowing to detect
* a fatal _jammed_ situation where the head entry is
* out of time, while marked mandatory to process.
*/
void
verify_Significance()
{
SchedulerInvocation sched;
Activity act;
sched.feedPrioritisation ({act, Time{2,0}, Time{3,0}});
CHECK (Time(2,0) == sched.headTime());
CHECK ( sched.isDue (Time{2,0}));
CHECK (not sched.isMissed (Time{2,0}));
CHECK (not sched.isMissed (Time{3,0}));
CHECK ( sched.isMissed (Time{4,0}));
CHECK (not sched.isOutdated (Time{2,0}));
CHECK (not sched.isOutdated (Time{3,0}));
CHECK ( sched.isOutdated (Time{4,0}));
sched.feedPrioritisation ({act, Time{1,0}, Time{3,0}, ManifestationID{5}});
CHECK (Time(1,0) == sched.headTime());
CHECK ( sched.isOutdated (Time{1,0}));
CHECK (not sched.isMissed (Time{1,0}));
sched.activate (ManifestationID{5});
CHECK (Time(1,0) == sched.headTime());
CHECK ( sched.isDue (Time{1,0}));
CHECK (not sched.isOutdated (Time{1,0}));
CHECK (not sched.isOutdated (Time{3,0}));
CHECK ( sched.isOutdated (Time{4,0}));
CHECK ( sched.isMissed (Time{4,0}));
CHECK ( sched.isDue (Time{4,0}));
sched.activate (ManifestationID{23});
CHECK (not sched.isOutdated (Time{1,0}));
sched.drop (ManifestationID{5});
CHECK (Time(1,0) == sched.headTime());
CHECK ( sched.isOutdated (Time{1,0}));
CHECK ( sched.isOutdated (Time{4,0}));
CHECK ( sched.isMissed (Time{4,0}));
CHECK (not sched.isMissed (Time{1,0}));
CHECK ( sched.isDue (Time{1,0}));
sched.feedPrioritisation ({act, Time{0,0}, Time{2,0}, ManifestationID{23}, true});
CHECK (Time(0,0) == sched.headTime()); // ^^^^ marked as compulsory
CHECK (not sched.isMissed (Time{1,0}));
CHECK (not sched.isOutdated (Time{1,0}));
CHECK (not sched.isOutOfTime(Time{2,0})); // still OK /at/ deadline
CHECK ( sched.isOutOfTime(Time{3,0})); // ↯ past deadline yet marked as compulsory
CHECK ( sched.isOutdated (Time{3,0}));
CHECK ( sched.isMissed (Time{3,0}));
sched.drop (ManifestationID{5});
CHECK ( sched.isOutOfTime(Time{3,0})); // Manifestation-5 is altogether irrelevant for this case
sched.drop (ManifestationID{23});
CHECK (Time(0,0) == sched.headTime());
CHECK ( sched.isOutdated (Time{1,0}));
CHECK (not sched.isOutOfTime(Time{2,0}));
CHECK (not sched.isOutOfTime(Time{3,0})); // the disabled manifestation-23 masks the fatal out-of-time state
CHECK ( sched.isOutdated (Time{3,0}));
CHECK ( sched.isMissed (Time{3,0}));
sched.pullHead();
CHECK (Time(1,0) == sched.headTime());
CHECK ( sched.isOutdated (Time{1,0}));
CHECK ( sched.isDue (Time{1,0}));
sched.pullHead();
CHECK (Time(2,0) == sched.headTime());
CHECK (not sched.isOutdated (Time{2,0}));
CHECK ( sched.isOutdated (Time{4,0}));
CHECK ( sched.isMissed (Time{4,0}));
CHECK ( sched.isDue (Time{4,0}));
sched.pullHead();
CHECK (Time::NEVER == sched.headTime());
CHECK (not sched.isMissed (Time{4,0}));
CHECK (not sched.isOutdated (Time{4,0}));
CHECK (not sched.isDue (Time{4,0}));
CHECK (sched.empty());
}
};
/** Register this test class... */
LAUNCHER (SchedulerInvocation_test, "unit engine");
}}} // namespace vault::gear::test