#include "FuzzingValidators.h" #include "FuzzingDefs.h" #include #include #include #include Q_LOGGING_CATEGORY(LOG_FuzzingValidators, "FuzzingValidators") namespace { // ////////////// // // 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"; } }; /// Data resolution in local server's files const auto LOCALHOST_SERVER_RESOLUTION = 4; /// 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}); /** * Implementation of @sa DataValidatorHelper for the local AMDA server */ class LocalhostServerDataValidatorHelper : public DataValidatorHelper { public: void validate(const VariableState &variableState) const override { // 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(""); } 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(""); } // 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"; } }; /// Creates the @sa DataValidatorHelper according to the server passed in parameter std::unique_ptr createDataValidatorInstance(const QString &server) { if (server == QString{"localhost"}) { return std::make_unique(); } else { return std::make_unique(); } } DataValidatorHelper &DataValidatorHelper::instance() { // Creates instance depending on the SCIQLOP_AMDA_SERVER value at compile time static auto instance = createDataValidatorInstance(SCIQLOP_AMDA_SERVER); return *instance; } // /////////////// // // 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, const SqpRange &expectedRange, std::function 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..."); } } /** * 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; explicit FuzzingValidator(ValidationFunction fun) : m_Fun(std::move(fun)) {} void validate(const VariableState &variableState) const override { m_Fun(variableState); } private: ValidationFunction m_Fun; }; } // namespace std::unique_ptr FuzzingValidatorFactory::create(FuzzingValidatorType type) { switch (type) { case FuzzingValidatorType::DATA: return std::make_unique([](const VariableState &variableState) { DataValidatorHelper::instance().validate(variableState); }); case FuzzingValidatorType::RANGE: return std::make_unique([](const VariableState &variableState) { auto getVariableRange = [](const Variable &variable) { return variable.range(); }; validateRange(variableState.m_Variable, variableState.m_Range, getVariableRange); }); default: // Default case returns invalid validator break; } // Invalid validator return std::make_unique( [](const VariableState &) { QFAIL("Invalid validator"); }); }