mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-13 13:13:02 +02:00
Added docs for QTestExtensions.h
This commit is contained in:
parent
ede365acc6
commit
57b86c0762
4 changed files with 336 additions and 238 deletions
|
@ -9,36 +9,51 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#ifndef hifi_QTestExtensions_hpp
|
||||
#define hifi_QTestExtensions_hpp
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <functional>
|
||||
|
||||
// Adds some additional functionality to QtTest (eg. explicitely defined fuzzy comparison
|
||||
// of float and custom data types), and some extension mechanisms to provide other
|
||||
// test functionality as needed.
|
||||
|
||||
// QFUZZY_COMPARE (actual_expr, expected_expr, epsilon / error tolerance):
|
||||
// Requires that you have two functions defined:
|
||||
// Implements several extensions to QtTest.
|
||||
//
|
||||
// V fuzzyCompare (const T & a, const T & b)
|
||||
// QTextStream & operator << (const T & v)
|
||||
// Problems with QtTest:
|
||||
// - QCOMPARE can compare float values (using a fuzzy compare), but uses an internal threshold
|
||||
// that cannot be set explicitely (and we need explicit, adjustable error thresholds for our physics
|
||||
// and math test code).
|
||||
// - QFAIL takes a const char * failure message, and writing custom messages to it is complicated.
|
||||
//
|
||||
// fuzzyCompare should take a data type, T, and return the difference between two
|
||||
// such values / objects in terms of a second type, V (which should match the error
|
||||
// value type). For glm::vec3, T = glm::vec3, V = float, for example
|
||||
// To solve this, we have:
|
||||
// - QFUZZY_COMPARE (compares floats, or *any other type* using explicitely defined error thresholds.
|
||||
// To use it, you need to have a fuzzyCompare function ((T, T) -> V), and operator << for QTextStream).
|
||||
// - QFAIL_WITH_MESSAGE("some " << streamed << " message"), which builds, writes to, and stringifies
|
||||
// a QTextStream using black magic.
|
||||
// - QCOMPARE_WITH_LAMBDA / QCOMPARE_WITH_FUNCTION, which implements QCOMPARE, but with a user-defined
|
||||
// test function ((T, T) -> bool).
|
||||
// - A simple framework to write additional custom test macros as needed (QCOMPARE is reimplemented
|
||||
// from scratch using QTest::qFail, for example).
|
||||
//
|
||||
|
||||
// Generic function that reimplements the debugging output of a QCOMPARE failure via QFAIL.
|
||||
// Use this to implement your own QCOMPARE-ish macros (see QEXPLICIT_FUZZY_COMPARE for
|
||||
// more info).
|
||||
// This version provides a callback to write additional messages.
|
||||
// If the messages span more than one line, wrap them with '\n\t' to get proper indentation.
|
||||
|
||||
// Generates a QCOMPARE-style failure message that can be passed to QTest::qFail.
|
||||
//
|
||||
// Formatting looks like this:
|
||||
// <qFail message> <failMessage....>
|
||||
// Actual: (<stringified actual expr>) : <actual value>
|
||||
// Expected: (<stringified expected expr>): <expected value>
|
||||
// < additional messages (should be separated by "\n\t" for indent formatting)>
|
||||
// Loc: [<file path....>(<linenum>)]
|
||||
//
|
||||
// Additional messages (after actual/expected) can be written using the std::function callback.
|
||||
// If these messages span more than one line, wrap them with "\n\t" to get proper indentation / formatting)
|
||||
//
|
||||
template <typename T>
|
||||
inline QString QTest_generateCompareFailureMessage (const char * failMessage, const T & actual, const T & expected, const char * actual_expr, const char * expected_expr, std::function<QTextStream & (QTextStream &)> writeAdditionalMessages)
|
||||
{
|
||||
inline QString QTest_generateCompareFailureMessage (
|
||||
const char * failMessage,
|
||||
const T & actual, const T & expected,
|
||||
const char * actual_expr, const char * expected_expr,
|
||||
std::function<QTextStream & (QTextStream &)> writeAdditionalMessages
|
||||
) {
|
||||
QString s1 = actual_expr, s2 = expected_expr;
|
||||
int pad1_ = qMax(s2.length() - s1.length(), 0);
|
||||
int pad2_ = qMax(s1.length() - s2.length(), 0);
|
||||
|
@ -55,9 +70,21 @@ inline QString QTest_generateCompareFailureMessage (const char * failMessage, co
|
|||
return msg;
|
||||
}
|
||||
|
||||
// Generates a QCOMPARE-style failure message that can be passed to QTest::qFail.
|
||||
//
|
||||
// Formatting looks like this:
|
||||
// <qFail message> <failMessage....>
|
||||
// Actual: (<stringified actual expr>) : <actual value>
|
||||
// Expected: (<stringified expected expr>): <expected value>
|
||||
// Loc: [<file path....>(<linenum>)]
|
||||
// (no message callback)
|
||||
//
|
||||
template <typename T>
|
||||
inline QString QTest_generateCompareFailureMessage (const char * failMessage, const T & actual, const T & expected, const char * actual_expr, const char * expected_expr)
|
||||
{
|
||||
inline QString QTest_generateCompareFailureMessage (
|
||||
const char * failMessage,
|
||||
const T & actual, const T & expected,
|
||||
const char * actual_expr, const char * expected_expr
|
||||
) {
|
||||
QString s1 = actual_expr, s2 = expected_expr;
|
||||
int pad1_ = qMax(s2.length() - s1.length(), 0);
|
||||
int pad2_ = qMax(s1.length() - s2.length(), 0);
|
||||
|
@ -73,7 +100,8 @@ inline QString QTest_generateCompareFailureMessage (const char * failMessage, co
|
|||
return msg;
|
||||
}
|
||||
|
||||
// Why does qt have to make things so complicated...?
|
||||
// Hacky function that can assemble a QString from a QTextStream via a callback
|
||||
// (ie. stream operations w/out qDebug())
|
||||
inline QString makeMessageFromStream (std::function<void(QTextStream &)> writeMessage) {
|
||||
QString msg;
|
||||
QTextStream stream(&msg);
|
||||
|
@ -81,83 +109,137 @@ inline QString makeMessageFromStream (std::function<void(QTextStream &)> writeMe
|
|||
return msg;
|
||||
}
|
||||
|
||||
inline void QTest_failWithCustomMessage (std::function<void(QTextStream &stream)> writeMessage, int line, const char *file)
|
||||
{
|
||||
inline void QTest_failWithCustomMessage (
|
||||
std::function<void(QTextStream &stream)> writeMessage, int line, const char *file
|
||||
) {
|
||||
QTest::qFail(qPrintable(makeMessageFromStream(writeMessage)), file, line);
|
||||
}
|
||||
|
||||
// Equivalent to QFAIL, but takes a message that can be formatted using stream operators.
|
||||
// Writes to a QTextStream internally, and calls QTest::qFail (the internal impl of QFAIL,
|
||||
// with the current file and line number)
|
||||
//
|
||||
// example:
|
||||
// inline void foo () {
|
||||
// int thing = 2;
|
||||
// QFAIL_WITH_MESSAGE("Message " << thing << ";");
|
||||
// }
|
||||
//
|
||||
#define QFAIL_WITH_MESSAGE(...) \
|
||||
do { \
|
||||
QTest_failWithCustomMessage([&](QTextStream& stream) { stream << __VA_ARGS__; }, __LINE__, __FILE__); \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
inline void foo () {
|
||||
int thing = 2;
|
||||
QFAIL_WITH_MESSAGE("Message " << thing << ";");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Generates a QCOMPARE style failure message with custom arguments.
|
||||
// This is expected to be wrapped in a macro (see QFUZZY_COMPARE), and it must
|
||||
// actually return on failure (unless other functionality is desired).
|
||||
// Calls qFail using QTest_generateCompareFailureMessage.
|
||||
// This is (usually) wrapped in macros, but if you call this directly you should return immediately to get QFAIL semantics.
|
||||
template <typename T>
|
||||
inline void QTest_failWithMessage(const char * failMessage, const T & actual, const T & expected, const char * actualExpr, const char * expectedExpr, int line, const char * file)
|
||||
{
|
||||
inline void QTest_failWithMessage(
|
||||
const char * failMessage,
|
||||
const T & actual, const T & expected,
|
||||
const char * actualExpr, const char * expectedExpr,
|
||||
int line, const char * file
|
||||
) {
|
||||
QTest::qFail(qPrintable(QTest_generateCompareFailureMessage(failMessage, actual, expected, actualExpr, expectedExpr)), file, line);
|
||||
}
|
||||
|
||||
// Generates a QCOMPARE style failure message with custom arguments.
|
||||
// Writing additional lines (eg:)
|
||||
// Actual (<expr>): <actual value>
|
||||
// Expected (<expr>): <expected value>
|
||||
// <Your additional messages here...>
|
||||
// Loc: [<filepath...>(<line num>)]
|
||||
// is provided via a lamdbda / closure that can write to the textstream.
|
||||
// Be aware that newlines are actually "\n\t" (with this impl), so use that to get
|
||||
// proper indenting (and add extra '\t's to get additional indentation).
|
||||
// Calls qFail using QTest_generateCompareFailureMessage.
|
||||
// This is (usually) wrapped in macros, but if you call this directly you should return immediately to get QFAIL semantics.
|
||||
template <typename T>
|
||||
inline void QTest_failWithMessage(const char * failMessage, const T & actual, const T & expected, const char * actualExpr, const char * expectedExpr, int line, const char * file, std::function<QTextStream &(QTextStream&)> writeAdditionalMessageLines) {
|
||||
inline void QTest_failWithMessage(
|
||||
const char * failMessage,
|
||||
const T & actual, const T & expected,
|
||||
const char * actualExpr, const char * expectedExpr,
|
||||
int line, const char * file,
|
||||
std::function<QTextStream &(QTextStream&)> writeAdditionalMessageLines
|
||||
) {
|
||||
QTest::qFail(qPrintable(QTest_generateCompareFailureMessage(failMessage, actual, expected, actualExpr, expectedExpr, writeAdditionalMessageLines)), file, line);
|
||||
}
|
||||
|
||||
// Implements QFUZZY_COMPARE
|
||||
template <typename T, typename V>
|
||||
inline auto QTest_fuzzyCompare(const T & actual, const T & expected, const char * actual_expr, const char * expected_expr, int line, const char * file, const V & epsilon) -> decltype(fuzzyCompare(actual, expected))
|
||||
{
|
||||
if (fuzzyCompare(actual, expected) > epsilon) {
|
||||
QTest::qFail(qPrintable(QTest_generateCompareFailureMessage(
|
||||
QTest_failWithMessage(
|
||||
"Compared values are not the same (fuzzy compare)",
|
||||
actual, expected, actual_expr, expected_expr,
|
||||
actual, expected, actual_expr, expected_expr, line, file,
|
||||
[&] (QTextStream & stream) -> QTextStream & {
|
||||
return stream << "Err tolerance: " << fuzzyCompare((actual), (expected)) << " > " << epsilon;
|
||||
})), file, line);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements a fuzzy QCOMPARE using an explicit epsilon error value.
|
||||
// If you use this, you must have the following functions defined for the types you're using:
|
||||
// <T, V> V fuzzyCompare (const T& a, const T& b) (should return the absolute, max difference between a and b)
|
||||
// <T> QTextStream & operator << (QTextStream& stream, const T& value)
|
||||
//
|
||||
// Here's an implementation for glm::vec3:
|
||||
// inline float fuzzyCompare (const glm::vec3 & a, const glm::vec3 & b) { // returns
|
||||
// return glm::distance(a, b);
|
||||
// }
|
||||
// inline QTextStream & operator << (QTextStream & stream, const T & v) {
|
||||
// return stream << "glm::vec3 { " << v.x << ", " << v.y << ", " << v.z << " }"
|
||||
// }
|
||||
//
|
||||
#define QFUZZY_COMPARE(actual, expected, epsilon) \
|
||||
do { \
|
||||
if (!QTest_fuzzyCompare(actual, expected, #actual, #expected, __LINE__, __FILE__, epsilon)) \
|
||||
return; \
|
||||
} while(0)
|
||||
|
||||
// Implements QCOMPARE using an explicit, externally defined test function.
|
||||
// The advantage of this (over a manual check or what have you) is that the values of actual and
|
||||
// expected are printed in the event that the test fails.
|
||||
//
|
||||
// testFunc(const T & actual, const T & expected) -> bool: true (test succeeds) | false (test fails)
|
||||
//
|
||||
#define QCOMPARE_WITH_FUNCTION(actual, expected, testFunc) \
|
||||
do { \
|
||||
if (!testFunc(actual, expected)) { \
|
||||
QTest_failWithMessage("Compared values are not the same", actual, expected, #actual, #expected, __LINE__, __FILE__); \
|
||||
return; \
|
||||
} \
|
||||
while (0)
|
||||
} while (0)
|
||||
|
||||
// Implements QCOMPARE using an explicit, externally defined test function.
|
||||
// Unlike QCOMPARE_WITH_FUNCTION, this func / closure takes no arguments (which is much more convenient
|
||||
// if you're using a c++11 closure / lambda).
|
||||
//
|
||||
// usage:
|
||||
// QCOMPARE_WITH_LAMBDA(foo, expectedFoo, [&foo, &expectedFoo] () {
|
||||
// return foo->isFooish() && foo->fooishness() >= expectedFoo->fooishness();
|
||||
// });
|
||||
// (fails if foo is not as fooish as expectedFoo)
|
||||
//
|
||||
#define QCOMPARE_WITH_LAMBDA(actual, expected, testClosure) \
|
||||
do { \
|
||||
if (!testClosure()) \
|
||||
QTest_failWithMessage("Compared values are not the same", actual, expected, #actual, #expected, __LINE__, __FILE__); \
|
||||
return; \
|
||||
} \
|
||||
while (0)
|
||||
} while (0)
|
||||
|
||||
// Same as QCOMPARE_WITH_FUNCTION, but with a custom fail message
|
||||
#define QCOMPARE_WITH_FUNCTION_AND_MESSAGE(actual, expected, testfunc, failMessage) \
|
||||
do { \
|
||||
if (!testFunc(actual, expected)) { \
|
||||
QTest_failWithMessage(failMessage, actual, expected, #actual, #expected, __LINE__, __FILE__); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Same as QCOMPARE_WITH_FUNCTION, but with a custom fail message
|
||||
#define QCOMPARE_WITH_LAMBDA(actual, expected, testClosure, failMessage) \
|
||||
do { \
|
||||
if (!testClosure()) \
|
||||
QTest_failWithMessage(failMessage, actual, expected, #actual, #expected, __LINE__, __FILE__); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ private slots:
|
|||
void testConeRollerConstraint();
|
||||
};
|
||||
|
||||
// Enable QFUZZY_COMPARE for glm::quat
|
||||
// Use QFUZZY_COMPARE and define it for glm::quat
|
||||
#include <glm/glm.hpp>
|
||||
float fuzzyCompare (const glm::quat & a, const glm::quat & b);
|
||||
QTextStream & operator << (QTextStream & stream, const glm::quat & q);
|
||||
|
|
|
@ -24,199 +24,203 @@ quint64 MovingMinMaxAvgTests::randQuint64() {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void MovingMinMaxAvgTests::runAllTests() {
|
||||
{
|
||||
// quint64 test
|
||||
void MovingMinMaxAvgTests::testQuint64() {
|
||||
// quint64 test
|
||||
|
||||
const int INTERVAL_LENGTH = 100;
|
||||
const int WINDOW_INTERVALS = 50;
|
||||
const int INTERVAL_LENGTH = 100;
|
||||
const int WINDOW_INTERVALS = 50;
|
||||
|
||||
MovingMinMaxAvg<quint64> stats(INTERVAL_LENGTH, WINDOW_INTERVALS);
|
||||
MovingMinMaxAvg<quint64> stats(INTERVAL_LENGTH, WINDOW_INTERVALS);
|
||||
|
||||
quint64 min = std::numeric_limits<quint64>::max();
|
||||
quint64 max = 0;
|
||||
double average = 0.0;
|
||||
int totalSamples = 0;
|
||||
quint64 min = std::numeric_limits<quint64>::max();
|
||||
quint64 max = 0;
|
||||
double average = 0.0;
|
||||
int totalSamples = 0;
|
||||
|
||||
quint64 windowMin;
|
||||
quint64 windowMax;
|
||||
double windowAverage;
|
||||
quint64 windowMin;
|
||||
quint64 windowMax;
|
||||
double windowAverage;
|
||||
|
||||
QQueue<quint64> windowSamples;
|
||||
// fill window samples
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
QQueue<quint64> windowSamples;
|
||||
// fill window samples
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
|
||||
quint64 sample = randQuint64();
|
||||
quint64 sample = randQuint64();
|
||||
|
||||
windowSamples.enqueue(sample);
|
||||
if (windowSamples.size() > INTERVAL_LENGTH * WINDOW_INTERVALS) {
|
||||
windowSamples.dequeue();
|
||||
windowSamples.enqueue(sample);
|
||||
if (windowSamples.size() > INTERVAL_LENGTH * WINDOW_INTERVALS) {
|
||||
windowSamples.dequeue();
|
||||
}
|
||||
|
||||
stats.update(sample);
|
||||
|
||||
min = std::min(min, sample);
|
||||
max = std::max(max, sample);
|
||||
average = (average * totalSamples + sample) / (totalSamples + 1);
|
||||
totalSamples++;
|
||||
|
||||
QCOMPARE(stats.getMin(), min);
|
||||
QCOMPARE(stats.getMax(), max);
|
||||
|
||||
QFUZZY_COMPARE((float) stats.getAverage() / (float) average, 1.0f, EPSILON);
|
||||
QFUZZY_COMPARE((float) stats.getAverage(), (float) average, EPSILON);
|
||||
|
||||
// QCOMPARE(fabsf(
|
||||
// (float)stats.getAverage() / (float)average - 1.0f
|
||||
// ) < EPSILON ||
|
||||
// fabsf(
|
||||
// (float)stats.getAverage() - (float)average) < EPSILON);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
assert(stats.getNewStatsAvailableFlag());
|
||||
stats.clearNewStatsAvailableFlag();
|
||||
|
||||
windowMin = std::numeric_limits<quint64>::max();
|
||||
windowMax = 0;
|
||||
windowAverage = 0.0;
|
||||
foreach(quint64 s, windowSamples) {
|
||||
windowMin = std::min(windowMin, s);
|
||||
windowMax = std::max(windowMax, s);
|
||||
windowAverage += (double)s;
|
||||
}
|
||||
windowAverage /= (double)windowSamples.size();
|
||||
|
||||
stats.update(sample);
|
||||
|
||||
min = std::min(min, sample);
|
||||
max = std::max(max, sample);
|
||||
average = (average * totalSamples + sample) / (totalSamples + 1);
|
||||
totalSamples++;
|
||||
|
||||
assert(stats.getMin() == min);
|
||||
assert(stats.getMax() == max);
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON ||
|
||||
fabsf((float)stats.getAverage() - (float)average) < EPSILON);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
assert(stats.getNewStatsAvailableFlag());
|
||||
stats.clearNewStatsAvailableFlag();
|
||||
|
||||
windowMin = std::numeric_limits<quint64>::max();
|
||||
windowMax = 0;
|
||||
windowAverage = 0.0;
|
||||
foreach(quint64 s, windowSamples) {
|
||||
windowMin = std::min(windowMin, s);
|
||||
windowMax = std::max(windowMax, s);
|
||||
windowAverage += (double)s;
|
||||
}
|
||||
windowAverage /= (double)windowSamples.size();
|
||||
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON ||
|
||||
fabsf((float)stats.getAverage() - (float)average) < EPSILON);
|
||||
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
}
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MovingMinMaxAvgTests::testInt() {
|
||||
// int test
|
||||
|
||||
const int INTERVAL_LENGTH = 1;
|
||||
const int WINDOW_INTERVALS = 75;
|
||||
|
||||
MovingMinMaxAvg<int> stats(INTERVAL_LENGTH, WINDOW_INTERVALS);
|
||||
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = 0;
|
||||
double average = 0.0;
|
||||
int totalSamples = 0;
|
||||
|
||||
int windowMin;
|
||||
int windowMax;
|
||||
double windowAverage;
|
||||
|
||||
QQueue<int> windowSamples;
|
||||
// fill window samples
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
|
||||
int sample = rand();
|
||||
|
||||
windowSamples.enqueue(sample);
|
||||
if (windowSamples.size() > INTERVAL_LENGTH * WINDOW_INTERVALS) {
|
||||
windowSamples.dequeue();
|
||||
}
|
||||
|
||||
stats.update(sample);
|
||||
|
||||
min = std::min(min, sample);
|
||||
max = std::max(max, sample);
|
||||
average = (average * totalSamples + sample) / (totalSamples + 1);
|
||||
totalSamples++;
|
||||
|
||||
assert(stats.getMin() == min);
|
||||
assert(stats.getMax() == max);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
assert(stats.getNewStatsAvailableFlag());
|
||||
stats.clearNewStatsAvailableFlag();
|
||||
|
||||
windowMin = std::numeric_limits<int>::max();
|
||||
windowMax = 0;
|
||||
windowAverage = 0.0;
|
||||
foreach(int s, windowSamples) {
|
||||
windowMin = std::min(windowMin, s);
|
||||
windowMax = std::max(windowMax, s);
|
||||
windowAverage += (double)s;
|
||||
}
|
||||
windowAverage /= (double)windowSamples.size();
|
||||
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MovingMinMaxAvgTests::testFloat() {
|
||||
// float test
|
||||
|
||||
const int INTERVAL_LENGTH = 57;
|
||||
const int WINDOW_INTERVALS = 1;
|
||||
|
||||
MovingMinMaxAvg<float> stats(INTERVAL_LENGTH, WINDOW_INTERVALS);
|
||||
|
||||
float min = std::numeric_limits<float>::max();
|
||||
float max = 0;
|
||||
double average = 0.0;
|
||||
int totalSamples = 0;
|
||||
|
||||
float windowMin;
|
||||
float windowMax;
|
||||
double windowAverage;
|
||||
|
||||
QQueue<float> windowSamples;
|
||||
// fill window samples
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
|
||||
float sample = randFloat();
|
||||
|
||||
windowSamples.enqueue(sample);
|
||||
if (windowSamples.size() > INTERVAL_LENGTH * WINDOW_INTERVALS) {
|
||||
windowSamples.dequeue();
|
||||
}
|
||||
|
||||
stats.update(sample);
|
||||
|
||||
min = std::min(min, sample);
|
||||
max = std::max(max, sample);
|
||||
average = (average * totalSamples + (double)sample) / (totalSamples + 1);
|
||||
totalSamples++;
|
||||
|
||||
assert(stats.getMin() == min);
|
||||
assert(stats.getMax() == max);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
assert(stats.getNewStatsAvailableFlag());
|
||||
stats.clearNewStatsAvailableFlag();
|
||||
|
||||
windowMin = std::numeric_limits<float>::max();
|
||||
windowMax = 0;
|
||||
windowAverage = 0.0;
|
||||
foreach(float s, windowSamples) {
|
||||
windowMin = std::min(windowMin, s);
|
||||
windowMax = std::max(windowMax, s);
|
||||
windowAverage += (double)s;
|
||||
}
|
||||
windowAverage /= (double)windowSamples.size();
|
||||
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// int test
|
||||
|
||||
const int INTERVAL_LENGTH = 1;
|
||||
const int WINDOW_INTERVALS = 75;
|
||||
|
||||
MovingMinMaxAvg<int> stats(INTERVAL_LENGTH, WINDOW_INTERVALS);
|
||||
|
||||
int min = std::numeric_limits<int>::max();
|
||||
int max = 0;
|
||||
double average = 0.0;
|
||||
int totalSamples = 0;
|
||||
|
||||
int windowMin;
|
||||
int windowMax;
|
||||
double windowAverage;
|
||||
|
||||
QQueue<int> windowSamples;
|
||||
// fill window samples
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
|
||||
int sample = rand();
|
||||
|
||||
windowSamples.enqueue(sample);
|
||||
if (windowSamples.size() > INTERVAL_LENGTH * WINDOW_INTERVALS) {
|
||||
windowSamples.dequeue();
|
||||
}
|
||||
|
||||
stats.update(sample);
|
||||
|
||||
min = std::min(min, sample);
|
||||
max = std::max(max, sample);
|
||||
average = (average * totalSamples + sample) / (totalSamples + 1);
|
||||
totalSamples++;
|
||||
|
||||
assert(stats.getMin() == min);
|
||||
assert(stats.getMax() == max);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
assert(stats.getNewStatsAvailableFlag());
|
||||
stats.clearNewStatsAvailableFlag();
|
||||
|
||||
windowMin = std::numeric_limits<int>::max();
|
||||
windowMax = 0;
|
||||
windowAverage = 0.0;
|
||||
foreach(int s, windowSamples) {
|
||||
windowMin = std::min(windowMin, s);
|
||||
windowMax = std::max(windowMax, s);
|
||||
windowAverage += (double)s;
|
||||
}
|
||||
windowAverage /= (double)windowSamples.size();
|
||||
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// float test
|
||||
|
||||
const int INTERVAL_LENGTH = 57;
|
||||
const int WINDOW_INTERVALS = 1;
|
||||
|
||||
MovingMinMaxAvg<float> stats(INTERVAL_LENGTH, WINDOW_INTERVALS);
|
||||
|
||||
float min = std::numeric_limits<float>::max();
|
||||
float max = 0;
|
||||
double average = 0.0;
|
||||
int totalSamples = 0;
|
||||
|
||||
float windowMin;
|
||||
float windowMax;
|
||||
double windowAverage;
|
||||
|
||||
QQueue<float> windowSamples;
|
||||
// fill window samples
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
|
||||
float sample = randFloat();
|
||||
|
||||
windowSamples.enqueue(sample);
|
||||
if (windowSamples.size() > INTERVAL_LENGTH * WINDOW_INTERVALS) {
|
||||
windowSamples.dequeue();
|
||||
}
|
||||
|
||||
stats.update(sample);
|
||||
|
||||
min = std::min(min, sample);
|
||||
max = std::max(max, sample);
|
||||
average = (average * totalSamples + (double)sample) / (totalSamples + 1);
|
||||
totalSamples++;
|
||||
|
||||
assert(stats.getMin() == min);
|
||||
assert(stats.getMax() == max);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
if ((i + 1) % INTERVAL_LENGTH == 0) {
|
||||
|
||||
assert(stats.getNewStatsAvailableFlag());
|
||||
stats.clearNewStatsAvailableFlag();
|
||||
|
||||
windowMin = std::numeric_limits<float>::max();
|
||||
windowMax = 0;
|
||||
windowAverage = 0.0;
|
||||
foreach(float s, windowSamples) {
|
||||
windowMin = std::min(windowMin, s);
|
||||
windowMax = std::max(windowMax, s);
|
||||
windowAverage += (double)s;
|
||||
}
|
||||
windowAverage /= (double)windowSamples.size();
|
||||
|
||||
assert(stats.getWindowMin() == windowMin);
|
||||
assert(stats.getWindowMax() == windowMax);
|
||||
assert(fabsf((float)stats.getAverage() / (float)average - 1.0f) < EPSILON);
|
||||
|
||||
} else {
|
||||
assert(!stats.getNewStatsAvailableFlag());
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("moving min/max/avg test passed!\n");
|
||||
}
|
||||
|
|
|
@ -12,14 +12,26 @@
|
|||
#ifndef hifi_MovingMinMaxAvgTests_h
|
||||
#define hifi_MovingMinMaxAvgTests_h
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
inline float fuzzyCompare (float a, float b) {
|
||||
return fabsf(a - b);
|
||||
}
|
||||
|
||||
#include "../QTestExtensions.hpp"
|
||||
|
||||
#include "MovingMinMaxAvg.h"
|
||||
#include "SharedUtil.h"
|
||||
|
||||
namespace MovingMinMaxAvgTests {
|
||||
|
||||
class MovingMinMaxAvgTests : public QObject {
|
||||
|
||||
private slots:
|
||||
void testQuint64 ();
|
||||
void testInt ();
|
||||
void testFloat ();
|
||||
|
||||
private:
|
||||
quint64 randQuint64();
|
||||
|
||||
void runAllTests();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // hifi_MovingMinMaxAvgTests_h
|
||||
|
|
Loading…
Reference in a new issue