LUMIERA.clone/tests/basics/time/digxel-test.cpp

417 lines
11 KiB
C++
Raw Permalink Normal View History

/*
Digxel(Test) - cover behaviour of a generic number-element holder
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
Copyright (C)
2011, Hermann Vosseler <Ichthyostega@web.de>
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
  **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.
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
* *****************************************************************/
/** @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{
2011-01-06 01:35:22 +01:00
namespace { // Test data and setup
2011-01-06 01:35:22 +01:00
const uint REPEAT = 40;
const uint RAND_RANGE = 100;
const uint RAND_DENOM = 3;
2011-01-06 01:35:22 +01:00
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();
}
2011-01-04 20:56:47 +01:00
inline uint
isOdd (uint i)
{
return i % 2;
}
2011-01-06 01:35:22 +01:00
/* === 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);
2011-01-06 01:35:22 +01:00
void
sideeffectSum (TestDigxel* digxel, double val)
{
sum += val;
digxel->setValueRaw (val);
}
2011-01-04 20:56:47 +01:00
double preval(0), newval(0);
2011-01-06 01:35:22 +01:00
void
protocollingMutator (TestDigxel* digxel, double val)
{
preval = newval;
newval = val;
digxel->setValueRaw (val);
}
2011-01-04 20:56:47 +01:00
void
limitingMutator (TestDigxel* digxel, double value2set)
2011-01-04 20:56:47 +01:00
{
digxel->setValueRaw ((+1 < value2set) ? +1.0
: (-1 > value2set) ? -1.0
: value2set);
2011-01-04 20:56:47 +01:00
}
void
trivialMutator (TestDigxel* digxel, double value2set)
{
digxel->setValueRaw (value2set);
}
2011-01-04 20:56:47 +01:00
void
emptyMutator (TestDigxel*, double)
{
/* do nothing */
}
2011-01-06 01:35:22 +01:00
}//(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
2011-01-06 01:35:22 +01:00
* - performing side-effects from the setter-functor
* - formatted value caching
*/
class Digxel_test : public Test
{
virtual void
2025-06-07 23:59:57 +02:00
run (Arg arg)
{
seedRand();
checkSimpleUsage();
2011-01-04 20:56:47 +01:00
checkMutation ();
verifyMutatorInfluence();
verifyAssignMutatingOperators();
verifyComparisons();
2011-01-04 20:56:47 +01:00
checkCopy ();
checkDisplayOverrun();
if (!isnil (arg))
timingMeasurements();
2025-06-07 23:59:57 +02:00
}
void
checkSimpleUsage ()
{
TestDigxel digi;
CHECK (0 == digi);
2011-01-06 01:35:22 +01:00
CHECK ("## +0.0 ##" == string(digi));
cout << "empty____" << digi << endl;
2011-01-06 01:35:22 +01:00
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);
2011-01-06 01:35:22 +01:00
sum = checksum = 0;
for (uint i=0; i < REPEAT; ++i)
{
double arbitrary = randomFrac();
2011-01-06 01:35:22 +01:00
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);
}
2011-01-06 01:35:22 +01:00
CHECK (0 < sum);
}
void
2011-01-04 20:56:47 +01:00
verifyMutatorInfluence ()
{
TestDigxel digi;
2011-01-04 20:56:47 +01:00
// using the default mutator
CHECK (0 == digi);
2011-01-04 20:56:47 +01:00
digi = 12.3;
CHECK (12.3 == digi);
2011-01-06 01:35:22 +01:00
// a special mutator to limit the value
digi.installMutator (limitingMutator, digi);
2011-01-04 20:56:47 +01:00
CHECK (12.3 == digi);
digi = 12.3;
2011-01-20 21:39:17 +01:00
CHECK (12.3 == digi); // triggered on real change only
digi = 12.2;
2011-01-04 20:56:47 +01:00
CHECK (1 == digi);
2011-01-06 01:35:22 +01:00
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;
2025-06-07 23:59:57 +02:00
CHECK ( 0.0 == preval and 12.3 == newval);
digi += 10;
2025-06-07 23:59:57 +02:00
CHECK (12.3 == preval and 22.3 == newval);
digi -= 5;
2025-06-07 23:59:57 +02:00
CHECK (22.3 == preval and 17.3 == newval);
++digi;
2025-06-07 23:59:57 +02:00
CHECK (17.3 == preval and 18.3 == newval);
digi++;
2025-06-07 23:59:57 +02:00
CHECK (18.3 == preval and 19.3 == newval);
--digi;
2025-06-07 23:59:57 +02:00
CHECK (19.3 == preval and 18.3 == newval);
digi--;
2025-06-07 23:59:57 +02:00
CHECK (18.3 == preval and 17.3 == newval);
double val = ++digi;
2025-06-07 23:59:57 +02:00
CHECK (18.3 == digi and 18.3 == val);
val = digi++;
2025-06-07 23:59:57 +02:00
CHECK (19.3 == digi and 18.3 == val);
val = --digi;
2025-06-07 23:59:57 +02:00
CHECK (18.3 == digi and 18.3 == val);
val = digi--;
2025-06-07 23:59:57 +02:00
CHECK (17.3 == digi and 18.3 == val);
}
2011-01-06 01:35:22 +01:00
void
verifyComparisons ()
{
TestDigxel d1;
TestDigxel d2;
CHECK (d1 == d2);
double someValue = d1 + randomFrac();
2011-01-06 01:35:22 +01:00
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);
}
2011-01-06 01:35:22 +01:00
/** @test Digxel should be protected
* against display buffer overrun */
void
checkDisplayOverrun ()
{
TestDigxel digi;
digi = 123456789.12345678;
2011-01-06 01:35:22 +01:00
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
2011-01-06 01:35:22 +01:00
#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;
2011-01-06 01:35:22 +01:00
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)
2011-01-06 01:35:22 +01:00
CHECK (without_reformatting < with_reformatting);
}
};
/** Register this test class... */
LAUNCHER (Digxel_test, "unit common");
}}} // namespace lib::time::test