LUMIERA.clone/tests/basics/time/digxel-test.cpp
Ichthyostega 806db414dd Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
 * there is no entity "Lumiera.org" which holds any copyrights
 * Lumiera source code is provided under the GPL Version 2+

== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''

The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!

The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00

416 lines
11 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Digxel(Test) - cover behaviour of a generic number-element holder
Copyright (C)
2011, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** 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. See the file COPYING for further details.
* *****************************************************************/
/** @file digxel-test.cpp
** unit test \ref Digxel_test
*/
#include "lib/test/run.hpp"
#include "lib/format-cout.hpp"
#include "lib/format-string.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/time/digxel.hpp"
#include "lib/random.hpp"
#include "lib/util.hpp"
using lumiera::error::LUMIERA_ERROR_ASSERTION;
using util::isSameObject;
using util::isnil;
using lib::rani;
namespace lib {
namespace time{
namespace test{
namespace { // Test data and setup
const uint REPEAT = 40;
const uint RAND_RANGE = 100;
const uint RAND_DENOM = 3;
const uint TIMING_CNT = 10000000;
inline double
randomFrac()
{
double arbitrary = 1 + rani(RAND_RANGE);
arbitrary /= 1 + rani(RAND_DENOM);
static double prevVal;
if (arbitrary != prevVal)
{
prevVal = arbitrary;
return arbitrary;
}
else
return randomFrac();
}
inline uint
isOdd (uint i)
{
return i % 2;
}
/* === special Digxel configuration for this test === */
struct VerySpecialFormat
: digxel::PrintfFormatter<double, 11>
{
VerySpecialFormat() : digxel::PrintfFormatter<double,11>("##%+5.1f ##") { }
};
typedef Digxel<double, VerySpecialFormat> TestDigxel;
double sum(0),
checksum(0);
void
sideeffectSum (TestDigxel* digxel, double val)
{
sum += val;
digxel->setValueRaw (val);
}
double preval(0), newval(0);
void
protocollingMutator (TestDigxel* digxel, double val)
{
preval = newval;
newval = val;
digxel->setValueRaw (val);
}
void
limitingMutator (TestDigxel* digxel, double value2set)
{
digxel->setValueRaw ((+1 < value2set) ? +1.0
: (-1 > value2set) ? -1.0
: value2set);
}
void
trivialMutator (TestDigxel* digxel, double value2set)
{
digxel->setValueRaw (value2set);
}
void
emptyMutator (TestDigxel*, double)
{
/* do nothing */
}
}//(End)Test setup
/*******************************************************************//**
* @test verify correct behaviour of an display "Digxel":
* A self-contained numeric element to support building displays.
* - build a Digxel
* - set a value
* - retrieve formatted display
* - verify comparisons and increments
* - performing side-effects from the setter-functor
* - formatted value caching
*/
class Digxel_test : public Test
{
virtual void
run (Arg arg)
{
seedRand();
checkSimpleUsage();
checkMutation ();
verifyMutatorInfluence();
verifyAssignMutatingOperators();
verifyComparisons();
checkCopy ();
checkDisplayOverrun();
if (!isnil (arg))
timingMeasurements();
}
void
checkSimpleUsage ()
{
TestDigxel digi;
CHECK (0 == digi);
CHECK ("## +0.0 ##" == string(digi));
cout << "empty____" << digi << endl;
digi = -88.77;
CHECK (-88.77 == digi);
CHECK ("##-88.8 ##" == string(digi));
cout << "value____" << digi << endl;
}
void
checkMutation ()
{
TestDigxel digi;
// configure what the Digxel does on "mutation"
digi.installMutator (sideeffectSum, digi);
CHECK (0 == digi);
sum = checksum = 0;
for (uint i=0; i < REPEAT; ++i)
{
double arbitrary = randomFrac();
checksum += arbitrary; // for verification
//
digi = arbitrary; //...causes invocation of mutation functor
CHECK (sum == checksum, "divergence after adding %f in iteration %d", arbitrary, i);
CHECK (digi == arbitrary);
}
CHECK (0 < sum);
}
void
verifyMutatorInfluence ()
{
TestDigxel digi;
// using the default mutator
CHECK (0 == digi);
digi = 12.3;
CHECK (12.3 == digi);
// a special mutator to limit the value
digi.installMutator (limitingMutator, digi);
CHECK (12.3 == digi);
digi = 12.3;
CHECK (12.3 == digi); // triggered on real change only
digi = 12.2;
CHECK (1 == digi);
digi = 0.5;
CHECK (0.5 == digi);
digi = -0.678;
CHECK (-0.678 == digi);
digi = -9.1011;
CHECK (-1 == digi);
digi.setValueRaw(12.3); // bypassing mutator
CHECK (12.3 == digi);
}
/** @test verify the self-assigning increment/decrement operators.
* @note especially these need to invoke the mutator function,
* much like a direct assignment. We use a special mutator
* to protocol the previous / new value.
*/
void
verifyAssignMutatingOperators ()
{
TestDigxel digi;
digi.installMutator (protocollingMutator, digi);
digi = 12.3;
CHECK ( 0.0 == preval && 12.3 == newval);
digi += 10;
CHECK (12.3 == preval && 22.3 == newval);
digi -= 5;
CHECK (22.3 == preval && 17.3 == newval);
++digi;
CHECK (17.3 == preval && 18.3 == newval);
digi++;
CHECK (18.3 == preval && 19.3 == newval);
--digi;
CHECK (19.3 == preval && 18.3 == newval);
digi--;
CHECK (18.3 == preval && 17.3 == newval);
double val = ++digi;
CHECK (18.3 == digi && 18.3 == val);
val = digi++;
CHECK (19.3 == digi && 18.3 == val);
val = --digi;
CHECK (18.3 == digi && 18.3 == val);
val = digi--;
CHECK (17.3 == digi && 18.3 == val);
}
void
verifyComparisons ()
{
TestDigxel d1;
TestDigxel d2;
CHECK (d1 == d2);
double someValue = d1 + randomFrac();
d1 = someValue;
CHECK (d1 == someValue);
CHECK (d1 != d2);
CHECK (d2 != d1);
d2 = d1 + 22;
CHECK (d1 < d2);
CHECK (d1 <= d2);
CHECK (!(d1 > d2));
CHECK (!(d1 >= d2));
CHECK (!(d1 == d2));
}
void
checkCopy ()
{
TestDigxel d1;
double someValue = randomFrac();
d1 = someValue;
CHECK (d1 == someValue);
TestDigxel d2(d1);
CHECK (d2 == someValue);
CHECK (not isSameObject (d1, d2));
d1 = randomFrac();
CHECK (d1 != d2);
CHECK (d2 == someValue);
}
/** @test Digxel should be protected
* against display buffer overrun */
void
checkDisplayOverrun ()
{
TestDigxel digi;
digi = 123456789.12345678;
string formatted;
#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
VERIFY_ERROR (ASSERTION, formatted = digi.show() ); // should trigger assertion
formatted = digi.show(); // second time doesn't reformat
#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
CHECK (formatted.length() <= digi.maxlen());
}
/** @test perform several timing measurements and
* especially verify the effect of caching formatted values.
* Digxel avoids reformatting unchanged values; besides that
* it is possible to install a "mutator" functor for invoking
* all kinds of special behaviour on value changes. Of course
* doing so comes with a (considerable) price tag....
* @note the measurement especially show the effects of
* locality, which can vary largely over several runs.
*/
void
timingMeasurements ()
{
TestDigxel digi;
digi = 1;
clock_t start(0), stop(0);
util::_Fmt resultDisplay("timings(%s)%|36T.|%4.0fns\n");
#define START_TIMINGS start=clock();
#define DISPLAY_TIMINGS(ID)\
stop = clock(); \
uint ID = stop-start;\
cout << resultDisplay % STRINGIFY (ID) % (double(ID)/CLOCKS_PER_SEC/TIMING_CNT*1e9) ;
START_TIMINGS
for (uint i=0; i < TIMING_CNT; ++i)
{
isOdd (i);
}
DISPLAY_TIMINGS (empty_loop)
START_TIMINGS
for (uint i=0; i < TIMING_CNT; ++i)
{
digi = 1;
isOdd (i);
}
DISPLAY_TIMINGS (without_reformatting)
START_TIMINGS
for (uint i=0; i < TIMING_CNT; ++i)
{
digi = isOdd (i);
}
DISPLAY_TIMINGS (with_reformatting)
digi.installMutator (emptyMutator, digi);
START_TIMINGS
for (uint i=0; i < TIMING_CNT; ++i)
{
digi = isOdd (i);
}
DISPLAY_TIMINGS (with_empty_mutator)
digi.installMutator (trivialMutator, digi);
START_TIMINGS
for (uint i=0; i < TIMING_CNT; ++i)
{
digi = isOdd (i);
}
DISPLAY_TIMINGS (with_trivial_mutator)
digi.installMutator (&TestDigxel::setValueRaw, digi);
START_TIMINGS
for (uint i=0; i < TIMING_CNT; ++i)
{
digi = isOdd (i);
}
DISPLAY_TIMINGS (with_memfun_mutator)
CHECK (without_reformatting < with_reformatting);
}
};
/** Register this test class... */
LAUNCHER (Digxel_test, "unit common");
}}} // namespace lib::time::test