FuzzingValidators.cpp
228 lines
| 8.5 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r1193 | #include "FuzzingValidators.h" | ||
Alexandre Leroux
|
r1199 | #include "FuzzingDefs.h" | ||
#include <Data/DataSeries.h> | ||||
Alexandre Leroux
|
r1198 | #include <Variable/Variable.h> | ||
Alexandre Leroux
|
r1193 | |||
Alexandre Leroux
|
r1194 | #include <QTest> | ||
Alexandre Leroux
|
r1193 | #include <functional> | ||
Q_LOGGING_CATEGORY(LOG_FuzzingValidators, "FuzzingValidators") | ||||
namespace { | ||||
Alexandre Leroux
|
r1198 | // ////////////// // | ||
// DATA VALIDATOR // | ||||
// ////////////// // | ||||
/// Singleton used to validate data of a variable | ||||
class DataValidatorHelper { | ||||
public: | ||||
/// @return the single instance of the helper | ||||
static DataValidatorHelper &instance(); | ||||
virtual ~DataValidatorHelper() noexcept = default; | ||||
virtual void validate(const VariableState &variableState) const = 0; | ||||
}; | ||||
/** | ||||
* Default implementation of @sa DataValidatorHelper | ||||
*/ | ||||
class DefaultDataValidatorHelper : public DataValidatorHelper { | ||||
public: | ||||
void validate(const VariableState &variableState) const override | ||||
{ | ||||
Q_UNUSED(variableState); | ||||
qCWarning(LOG_FuzzingValidators()).noquote() << "Checking variable's data... WARN: no data " | ||||
"verification is available for this server"; | ||||
} | ||||
}; | ||||
Alexandre Leroux
|
r1200 | /// Data resolution in local server's files | ||
const auto LOCALHOST_SERVER_RESOLUTION = 4; | ||||
Alexandre Leroux
|
r1201 | /// Reference value used to generate the data on the local server (a value is the number of seconds | ||
/// between the data date and this reference date) | ||||
const auto LOCALHOST_REFERENCE_VALUE | ||||
= DateUtils::secondsSinceEpoch(QDateTime{QDate{2000, 1, 1}, QTime{}, Qt::UTC}); | ||||
Alexandre Leroux
|
r1198 | /** | ||
* Implementation of @sa DataValidatorHelper for the local AMDA server | ||||
*/ | ||||
class LocalhostServerDataValidatorHelper : public DataValidatorHelper { | ||||
public: | ||||
void validate(const VariableState &variableState) const override | ||||
{ | ||||
Alexandre Leroux
|
r1199 | // Don't check data for null variable | ||
if (!variableState.m_Variable || variableState.m_Range == INVALID_RANGE) { | ||||
return; | ||||
} | ||||
auto message = "Checking variable's data..."; | ||||
auto toDateString = [](double value) { return DateUtils::dateTime(value).toString(); }; | ||||
// Checks that data are defined | ||||
auto variableDataSeries = variableState.m_Variable->dataSeries(); | ||||
if (variableDataSeries == nullptr && variableState.m_Range != INVALID_RANGE) { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() | ||||
<< message << "FAIL: the variable has no data while a range is defined"; | ||||
QFAIL(""); | ||||
} | ||||
Alexandre Leroux
|
r1200 | auto dataIts = variableDataSeries->xAxisRange(variableState.m_Range.m_TStart, | ||
variableState.m_Range.m_TEnd); | ||||
// Checks that the data are well defined in the range: | ||||
// - there is at least one data | ||||
// - the data are consistent (no data holes) | ||||
if (std::distance(dataIts.first, dataIts.second) == 0) { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() | ||||
<< message << "FAIL: the variable has no data"; | ||||
QFAIL(""); | ||||
} | ||||
auto firstXAxisData = dataIts.first->x(); | ||||
auto lastXAxisData = (dataIts.second - 1)->x(); | ||||
if (std::abs(firstXAxisData - variableState.m_Range.m_TStart) > LOCALHOST_SERVER_RESOLUTION | ||||
|| std::abs(lastXAxisData - variableState.m_Range.m_TEnd) | ||||
> LOCALHOST_SERVER_RESOLUTION) { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() | ||||
<< message << "FAIL: the data in the defined range are inconsistent (data hole " | ||||
"found at the beginning or the end)"; | ||||
QFAIL(""); | ||||
} | ||||
auto dataHoleIt = std::adjacent_find( | ||||
dataIts.first, dataIts.second, [](const auto &it1, const auto &it2) { | ||||
/// @todo: validate resolution | ||||
return std::abs(it1.x() - it2.x()) > 2 * (LOCALHOST_SERVER_RESOLUTION - 1); | ||||
}); | ||||
if (dataHoleIt != dataIts.second) { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() | ||||
<< message << "FAIL: the data in the defined range are inconsistent (data hole " | ||||
"found between times " | ||||
<< toDateString(dataHoleIt->x()) << "and " << toDateString((dataHoleIt + 1)->x()) | ||||
<< ")"; | ||||
QFAIL(""); | ||||
} | ||||
Alexandre Leroux
|
r1201 | // Checks values | ||
auto dataIndex = 0; | ||||
for (auto dataIt = dataIts.first; dataIt != dataIts.second; ++dataIt, ++dataIndex) { | ||||
auto xAxisData = dataIt->x(); | ||||
auto valuesData = dataIt->values(); | ||||
for (auto valueIndex = 0, valueEnd = valuesData.size(); valueIndex < valueEnd; | ||||
++valueIndex) { | ||||
auto value = valuesData.at(valueIndex); | ||||
auto expectedValue = xAxisData + valueIndex * LOCALHOST_SERVER_RESOLUTION | ||||
- LOCALHOST_REFERENCE_VALUE; | ||||
if (value != expectedValue) { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() | ||||
<< message << "FAIL: incorrect value data at time" | ||||
<< toDateString(xAxisData) << ", index" << valueIndex << "(found:" << value | ||||
<< ", expected:" << expectedValue << ")"; | ||||
QFAIL(""); | ||||
} | ||||
} | ||||
} | ||||
// At this step validation is OK | ||||
qCInfo(LOG_FuzzingValidators()).noquote() << message << "OK"; | ||||
Alexandre Leroux
|
r1198 | } | ||
}; | ||||
/// Creates the @sa DataValidatorHelper according to the server passed in parameter | ||||
std::unique_ptr<DataValidatorHelper> createDataValidatorInstance(const QString &server) | ||||
{ | ||||
if (server == QString{"localhost"}) { | ||||
return std::make_unique<LocalhostServerDataValidatorHelper>(); | ||||
} | ||||
else { | ||||
return std::make_unique<DefaultDataValidatorHelper>(); | ||||
} | ||||
} | ||||
DataValidatorHelper &DataValidatorHelper::instance() | ||||
{ | ||||
// Creates instance depending on the SCIQLOP_AMDA_SERVER value at compile time | ||||
static auto instance = createDataValidatorInstance(SCIQLOP_AMDA_SERVER); | ||||
return *instance; | ||||
} | ||||
Alexandre Leroux
|
r1197 | // /////////////// // | ||
// RANGE VALIDATOR // | ||||
// /////////////// // | ||||
/** | ||||
* Checks that a range of a variable matches the expected range passed as a parameter | ||||
* @param variable the variable for which to check the range | ||||
* @param expectedRange the expected range | ||||
* @param getVariableRangeFun the function to retrieve the range from the variable | ||||
* @remarks if the variable is null, checks that the expected range is the invalid range | ||||
*/ | ||||
void validateRange(std::shared_ptr<Variable> variable, const SqpRange &expectedRange, | ||||
std::function<SqpRange(const Variable &)> getVariableRangeFun) | ||||
{ | ||||
auto compare = [](const auto &range, const auto &expectedRange, const auto &message) { | ||||
if (range == expectedRange) { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() << message << "OK"; | ||||
} | ||||
else { | ||||
qCInfo(LOG_FuzzingValidators()).noquote() | ||||
<< message << "FAIL (current range:" << range | ||||
<< ", expected range:" << expectedRange << ")"; | ||||
QFAIL(""); | ||||
} | ||||
}; | ||||
if (variable) { | ||||
compare(getVariableRangeFun(*variable), expectedRange, "Checking variable's range..."); | ||||
} | ||||
else { | ||||
compare(INVALID_RANGE, expectedRange, "Checking that there is no range set..."); | ||||
} | ||||
} | ||||
Alexandre Leroux
|
r1193 | /** | ||
* Default implementation of @sa IFuzzingValidator. This validator takes as parameter of its | ||||
* construction a function of validation which is called in the validate() method | ||||
*/ | ||||
class FuzzingValidator : public IFuzzingValidator { | ||||
public: | ||||
/// Signature of a validation function | ||||
using ValidationFunction = std::function<void(const VariableState &variableState)>; | ||||
explicit FuzzingValidator(ValidationFunction fun) : m_Fun(std::move(fun)) {} | ||||
void validate(const VariableState &variableState) const override { m_Fun(variableState); } | ||||
private: | ||||
ValidationFunction m_Fun; | ||||
}; | ||||
} // namespace | ||||
Alexandre Leroux
|
r1194 | |||
std::unique_ptr<IFuzzingValidator> FuzzingValidatorFactory::create(FuzzingValidatorType type) | ||||
{ | ||||
switch (type) { | ||||
case FuzzingValidatorType::DATA: | ||||
return std::make_unique<FuzzingValidator>([](const VariableState &variableState) { | ||||
Alexandre Leroux
|
r1198 | DataValidatorHelper::instance().validate(variableState); | ||
Alexandre Leroux
|
r1194 | }); | ||
case FuzzingValidatorType::RANGE: | ||||
return std::make_unique<FuzzingValidator>([](const VariableState &variableState) { | ||||
Alexandre Leroux
|
r1197 | auto getVariableRange = [](const Variable &variable) { return variable.range(); }; | ||
validateRange(variableState.m_Variable, variableState.m_Range, getVariableRange); | ||||
Alexandre Leroux
|
r1194 | }); | ||
default: | ||||
// Default case returns invalid validator | ||||
break; | ||||
} | ||||
// Invalid validator | ||||
return std::make_unique<FuzzingValidator>( | ||||
[](const VariableState &) { QFAIL("Invalid validator"); }); | ||||
} | ||||