lumiera_/tests/library/del-stash-test.cpp
Ichthyostega a20e233ca0 Library: now using controlled seed and replaced rand (closes #1378)
After augmenting our `lib/random.hpp` abstraction framework to add the necessary flexibility,
a common seeding scheme was ''built into the Test-Runner.''
 * all tests relying on some kind of randomness should invoke `seedRand()`
 * this draws a seed from the `entropyGen` — which is also documented in the log
 * individual tests can now be launched with `--seed` to force a dedicated seed
 * moreover, tests should build a coherent structure of linked generators,
   especially when running concurrently. The existing tests were adapted accordingly

All usages of `rand()` in the code base were investigated and replaced
by suitable calls to our abstraction framework; the code base is thus
isolated from the actual implementation, simplifying further adaptation.
2024-11-17 19:45:41 +01:00

265 lines
6.7 KiB
C++

/*
DelStash(Test) - verify a facility to memorise and trigger deleter functions
Copyright (C) Lumiera.org
2010, 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 del-stash-test.cpp
** unit test \ref DelStash_test
*/
#include "lib/test/run.hpp"
#include "lib/del-stash.hpp"
namespace lib {
namespace test{
namespace { // probe victims
ulong MAX_MASS = 200; // number of victims to kill at once
ulong checksum = 0;
template<uint siz>
class Probe
{
uint mySiz_;
char myCrap_[siz];
public:
Probe()
: mySiz_(siz)
{
REQUIRE (siz);
for (uint i=0; i<siz; ++i)
{
char c (rani(256));
checksum += c;
myCrap_[i] = c;
}
}
~Probe()
{
CHECK (siz == mySiz_, "using wrong type information for de-allocation");
for (uint i=0; i<siz; ++i)
checksum -= myCrap_[i];
}
};
template<uint x>
inline Probe<x>*
makeViktim ()
{
return new Probe<x>();
}
template<uint x>
inline void
feedViktim (DelStash& killer)
{
killer.manage (new Probe<x>());
}
}//(End) test data
/************************************************************************//**
* @test create a bunch of objects with varying type and size, memorising
* how to kill them properly. Verify everyone is dead after mass-kill.
* Use a checksum not only to verify the number of objects created and
* destroyed, but also the individual (random) contents of the data
* within the objects, to ensure that the correct destructor
* actually is invoked for each type.
*
* @see lib::DelStash
*/
class DelStash_test : public Test
{
virtual void
run (Arg)
{
seedRand();
checksum = 0;
checkSingleKill();
checkCustomKill();
checkMassKill();
checkAutoKill();
}
void
checkSingleKill ()
{
DelStash killer;
CHECK (0 == killer.size());
killer.manage<short> (NULL);
CHECK (0 == killer.size());
Probe<5> *p = makeViktim<5>();
Probe<7> &r = *makeViktim<7>();
void *v = makeViktim<9>();
CHECK (0 < checksum);
killer.manage (p);
killer.manage (r);
killer.manage (static_cast<Probe<9>*> (v));
CHECK (3 == killer.size());
killer.kill (r);
CHECK (2 == killer.size());
killer.kill (p);
CHECK (1 == killer.size());
killer.kill (p);
CHECK (1 == killer.size()); // spurious kill requests ignored
killer.kill (v);
CHECK (0 == killer.size());
CHECK (0 == checksum);
}
void
feedViktims (DelStash& killer)
{
for (uint i=1; i <= MAX_MASS; ++i)
switch (i% 5) {
case 0: feedViktim<12> (killer); break;
case 1: feedViktim<23> (killer); break;
case 2: feedViktim<34> (killer); break;
case 3: feedViktim<45> (killer); break;
case 4: feedViktim<56> (killer); break;
}
}
void
checkMassKill ()
{
DelStash killer;
CHECK (0 == killer.size());
CHECK (0 == checksum);
CHECK (0 == killer.size());
feedViktims (killer);
CHECK (MAX_MASS == killer.size());
killer.killAll();
CHECK (0 == killer.size());
CHECK (0 == checksum);
}
void
checkAutoKill()
{
{
DelStash killer;
CHECK (0 == killer.size());
CHECK (0 == checksum);
feedViktims (killer);
Probe<444> * individuum = makeViktim<444>();
killer.manage (individuum);
feedViktims (killer);
killer.manage (makeViktim<5555>());
feedViktims (killer);
CHECK (3*MAX_MASS + 2 == killer.size());
killer.kill(individuum);
CHECK (3*MAX_MASS + 1 == killer.size());
CHECK (0 < checksum);
}// killer going out of scope...
CHECK (0 == checksum);
}
/** @test use a custom-provided
* deleter function
*/
void
checkCustomKill ()
{
DelStash killer;
CHECK (0 == killer.size());
/** a very specific setup,
* bound to mess up the checksum,
* unless the random bias is removed
* by the custom deleter function
*/
class Special
: Probe<555>
{
char secret_;
public:
Special()
: Probe<555>()
, secret_('a' + rani('z'-'a' +1))
{
checksum += secret_;
}
static void
selfKill (void *it)
{
Special *self = static_cast<Special*> (it);
checksum -= self->secret_;
delete self;
}
};
void * type_erased = new Special();
CHECK (0 < checksum);
killer.manage (type_erased, &Special::selfKill);
CHECK (1 == killer.size());
killer.kill(type_erased);
CHECK (0 == killer.size());
CHECK (0 == checksum);
}
};
/** Register this test class... */
LAUNCHER (DelStash_test, "unit common");
}} // namespace lib::test