From 385fbf866df8eaba6ebb7bf9ac8a0aae943894ef 2017-08-17 08:29:18 From: Alexandre Leroux Date: 2017-08-17 08:29:18 Subject: [PATCH] Merge branch 'feature/VectorSeries' into develop --- diff --git a/core/include/Data/ArrayData.h b/core/include/Data/ArrayData.h index 9f5a240..18eb70c 100644 --- a/core/include/Data/ArrayData.h +++ b/core/include/Data/ArrayData.h @@ -290,6 +290,21 @@ public: return m_Data[0]; } + // ///////////// // + // 2-dim methods // + // ///////////// // + + /** + * @return the data + * @remarks this method is only available for a two-dimensional ArrayData + */ + template > + DataContainer data() const noexcept + { + QReadLocker locker{&m_Lock}; + return m_Data; + } + private: DataContainer m_Data; mutable QReadWriteLock m_Lock; diff --git a/core/include/Data/DataSeries.h b/core/include/Data/DataSeries.h index 8637c28..0cba40a 100644 --- a/core/include/Data/DataSeries.h +++ b/core/include/Data/DataSeries.h @@ -1,20 +1,25 @@ #ifndef SCIQLOP_DATASERIES_H #define SCIQLOP_DATASERIES_H +#include "CoreGlobal.h" + #include #include #include #include - #include #include #include -Q_DECLARE_LOGGING_CATEGORY(LOG_DataSeries) -Q_LOGGING_CATEGORY(LOG_DataSeries, "DataSeries") - +// We don't use the Qt macro since the log is used in the header file, which causes multiple log +// definitions with inheritance. Inline method is used instead +inline const QLoggingCategory &LOG_DataSeries() +{ + static const QLoggingCategory category{"DataSeries"}; + return category; +} /** * @brief The DataSeries class is the base (abstract) implementation of IDataSeries. @@ -27,7 +32,7 @@ Q_LOGGING_CATEGORY(LOG_DataSeries, "DataSeries") * */ template -class DataSeries : public IDataSeries { +class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries { public: class IteratorValue { public: diff --git a/core/include/Data/VectorSeries.h b/core/include/Data/VectorSeries.h new file mode 100644 index 0000000..634039c --- /dev/null +++ b/core/include/Data/VectorSeries.h @@ -0,0 +1,30 @@ +#ifndef SCIQLOP_VECTORSERIES_H +#define SCIQLOP_VECTORSERIES_H + +#include "CoreGlobal.h" + +#include + +/** + * @brief The VectorSeries class is the implementation for a data series representing a vector. + */ +class SCIQLOP_CORE_EXPORT VectorSeries : public DataSeries<2> { +public: + /** + * Ctor. The vectors must have the same size, otherwise a ScalarSeries with no values will be + * created. + * @param xAxisData x-axis data + * @param xvaluesData x-values data + * @param yvaluesData y-values data + * @param zvaluesData z-values data + */ + explicit VectorSeries(QVector xAxisData, QVector xValuesData, + QVector yValuesData, QVector zValuesData, + const Unit &xAxisUnit, const Unit &valuesUnit); + + std::unique_ptr clone() const; + + std::shared_ptr subDataSeries(const SqpRange &range) override; +}; + +#endif // SCIQLOP_VECTORSERIES_H diff --git a/core/src/Data/ScalarSeries.cpp b/core/src/Data/ScalarSeries.cpp index 94387da..7b5b64a 100644 --- a/core/src/Data/ScalarSeries.cpp +++ b/core/src/Data/ScalarSeries.cpp @@ -18,20 +18,10 @@ std::shared_ptr ScalarSeries::subDataSeries(const SqpRange &range) auto subValuesData = QVector(); this->lockRead(); { - const auto ¤tXData = this->xAxisData()->cdata(); - const auto ¤tValuesData = this->valuesData()->cdata(); - - auto xDataBegin = currentXData.cbegin(); - auto xDataEnd = currentXData.cend(); - - auto lowerIt = std::lower_bound(xDataBegin, xDataEnd, range.m_TStart); - auto upperIt = std::upper_bound(xDataBegin, xDataEnd, range.m_TEnd); - auto distance = std::distance(xDataBegin, lowerIt); - - auto valuesDataIt = currentValuesData.cbegin() + distance; - for (auto xAxisDataIt = lowerIt; xAxisDataIt != upperIt; ++xAxisDataIt, ++valuesDataIt) { - subXAxisData.append(*xAxisDataIt); - subValuesData.append(*valuesDataIt); + auto bounds = subData(range.m_TStart, range.m_TEnd); + for (auto it = bounds.first; it != bounds.second; ++it) { + subXAxisData.append(it->x()); + subValuesData.append(it->value()); } } this->unlock(); diff --git a/core/src/Data/VectorSeries.cpp b/core/src/Data/VectorSeries.cpp new file mode 100644 index 0000000..06432e5 --- /dev/null +++ b/core/src/Data/VectorSeries.cpp @@ -0,0 +1,39 @@ +#include "Data/VectorSeries.h" + +VectorSeries::VectorSeries(QVector xAxisData, QVector xValuesData, + QVector yValuesData, QVector zValuesData, + const Unit &xAxisUnit, const Unit &valuesUnit) + : DataSeries{std::make_shared >(std::move(xAxisData)), xAxisUnit, + std::make_shared >(QVector >{ + std::move(xValuesData), std::move(yValuesData), std::move(zValuesData)}), + valuesUnit} +{ +} + +std::unique_ptr VectorSeries::clone() const +{ + return std::make_unique(*this); +} + +std::shared_ptr VectorSeries::subDataSeries(const SqpRange &range) +{ + auto subXAxisData = QVector(); + auto subXValuesData = QVector(); + auto subYValuesData = QVector(); + auto subZValuesData = QVector(); + + this->lockRead(); + { + auto bounds = subData(range.m_TStart, range.m_TEnd); + for (auto it = bounds.first; it != bounds.second; ++it) { + subXAxisData.append(it->x()); + subXValuesData.append(it->value(0)); + subYValuesData.append(it->value(1)); + subZValuesData.append(it->value(2)); + } + } + this->unlock(); + + return std::make_shared(subXAxisData, subXValuesData, subYValuesData, + subZValuesData, this->xAxisUnit(), this->valuesUnit()); +} diff --git a/core/vera-exclusions/exclusions.txt b/core/vera-exclusions/exclusions.txt index cbba6aa..d8f594e 100644 --- a/core/vera-exclusions/exclusions.txt +++ b/core/vera-exclusions/exclusions.txt @@ -9,6 +9,7 @@ ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D) ArrayData\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (arraydata_detail) ArrayData\.h:\d+:.*IPSIS_S06.*found: (D) ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim) +DataSeries\.h:\d+:.*IPSIS_S04_METHOD.*found: LOG_DataSeries DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.* # Ignore false positive relative to iterators diff --git a/plugins/amda/include/AmdaDefs.h b/plugins/amda/include/AmdaDefs.h index 4189ea9..5ae3b9d 100644 --- a/plugins/amda/include/AmdaDefs.h +++ b/plugins/amda/include/AmdaDefs.h @@ -9,6 +9,7 @@ // Relevant keys in JSON file extern const QString AMDA_COMPONENT_KEY; +extern const QString AMDA_DATA_TYPE_KEY; extern const QString AMDA_PRODUCT_KEY; extern const QString AMDA_ROOT_KEY; extern const QString AMDA_XML_ID_KEY; diff --git a/plugins/amda/include/AmdaResultParser.h b/plugins/amda/include/AmdaResultParser.h index 8c4bc61..0d590d5 100644 --- a/plugins/amda/include/AmdaResultParser.h +++ b/plugins/amda/include/AmdaResultParser.h @@ -12,8 +12,10 @@ class IDataSeries; Q_DECLARE_LOGGING_CATEGORY(LOG_AmdaResultParser) struct SCIQLOP_AMDA_EXPORT AmdaResultParser { + enum class ValueType { SCALAR, VECTOR, UNKNOWN }; - static std::shared_ptr readTxt(const QString &filePath) noexcept; + static std::shared_ptr readTxt(const QString &filePath, + ValueType valueType) noexcept; }; #endif // SCIQLOP_AMDARESULTPARSER_H diff --git a/plugins/amda/src/AmdaDefs.cpp b/plugins/amda/src/AmdaDefs.cpp index f90f076..63cd914 100644 --- a/plugins/amda/src/AmdaDefs.cpp +++ b/plugins/amda/src/AmdaDefs.cpp @@ -1,6 +1,7 @@ #include "AmdaDefs.h" const QString AMDA_COMPONENT_KEY = QStringLiteral("component"); +const QString AMDA_DATA_TYPE_KEY = QStringLiteral("dataType"); const QString AMDA_PRODUCT_KEY = QStringLiteral("parameter"); const QString AMDA_ROOT_KEY = QStringLiteral("dataCenter"); const QString AMDA_XML_ID_KEY = QStringLiteral("xml:id"); diff --git a/plugins/amda/src/AmdaPlugin.cpp b/plugins/amda/src/AmdaPlugin.cpp index 423b6d2..8d64b92 100644 --- a/plugins/amda/src/AmdaPlugin.cpp +++ b/plugins/amda/src/AmdaPlugin.cpp @@ -32,15 +32,6 @@ void associateActions(DataSourceItem &item, const QUuid &dataSourceUid) const auto itemType = item.type(); if (itemType == DataSourceItemType::PRODUCT) { - /// @todo : As for the moment we do not manage the loading of vectors, in the case of a - /// parameter, we update the identifier of download of the data: - /// - if the parameter has no component, the identifier remains the same - /// - if the parameter has at least one component, the identifier is that of the first - /// component (for example, "imf" becomes "imf (0)") - if (item.childCount() != 0) { - item.setData(AMDA_XML_ID_KEY, item.child(0)->data(AMDA_XML_ID_KEY)); - } - addLoadAction(QObject::tr("Load %1 product").arg(item.name())); } else if (itemType == DataSourceItemType::COMPONENT) { diff --git a/plugins/amda/src/AmdaProvider.cpp b/plugins/amda/src/AmdaProvider.cpp index baa2b00..a320b84 100644 --- a/plugins/amda/src/AmdaProvider.cpp +++ b/plugins/amda/src/AmdaProvider.cpp @@ -36,6 +36,19 @@ QString dateFormat(double sqpRange) noexcept return dateTime.toString(AMDA_TIME_FORMAT); } +AmdaResultParser::ValueType valueType(const QString &valueType) +{ + if (valueType == QStringLiteral("scalar")) { + return AmdaResultParser::ValueType::SCALAR; + } + else if (valueType == QStringLiteral("vector")) { + return AmdaResultParser::ValueType::VECTOR; + } + else { + return AmdaResultParser::ValueType::UNKNOWN; + } +} + } // namespace AmdaProvider::AmdaProvider() @@ -86,6 +99,10 @@ void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVa } qCDebug(LOG_AmdaProvider()) << tr("AmdaProvider::retrieveData") << dateTime; + // Retrieves the data type that determines whether the expected format for the result file is + // scalar, vector... + auto productValueType = valueType(data.value(AMDA_DATA_TYPE_KEY).toString()); + // /////////// // // Creates URL // // /////////// // @@ -98,30 +115,31 @@ void AmdaProvider::retrieveData(QUuid token, const SqpRange &dateTime, const QVa auto tempFile = std::make_shared(); // LAMBDA - auto httpDownloadFinished - = [this, dateTime, tempFile](QNetworkReply *reply, QUuid dataId) noexcept { - - // Don't do anything if the reply was abort - if (reply->error() != QNetworkReply::OperationCanceledError) { - - if (tempFile) { - auto replyReadAll = reply->readAll(); - if (!replyReadAll.isEmpty()) { - tempFile->write(replyReadAll); - } - tempFile->close(); - - // Parse results file - if (auto dataSeries = AmdaResultParser::readTxt(tempFile->fileName())) { - emit dataProvided(dataId, dataSeries, dateTime); - } - else { - /// @todo ALX : debug - } - } - } - - }; + auto httpDownloadFinished = [this, dateTime, tempFile, + productValueType](QNetworkReply *reply, QUuid dataId) noexcept { + + // Don't do anything if the reply was abort + if (reply->error() != QNetworkReply::OperationCanceledError) { + + if (tempFile) { + auto replyReadAll = reply->readAll(); + if (!replyReadAll.isEmpty()) { + tempFile->write(replyReadAll); + } + tempFile->close(); + + // Parse results file + if (auto dataSeries + = AmdaResultParser::readTxt(tempFile->fileName(), productValueType)) { + emit dataProvided(dataId, dataSeries, dateTime); + } + else { + /// @todo ALX : debug + } + } + } + + }; auto httpFinishedLambda = [this, httpDownloadFinished, tempFile](QNetworkReply *reply, QUuid dataId) noexcept { diff --git a/plugins/amda/src/AmdaResultParser.cpp b/plugins/amda/src/AmdaResultParser.cpp index ba5b907..73af4a0 100644 --- a/plugins/amda/src/AmdaResultParser.cpp +++ b/plugins/amda/src/AmdaResultParser.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -45,6 +46,25 @@ bool isCommentLine(const QString &line) return line.startsWith("#"); } +/// @return the number of lines to be read depending on the type of value passed in parameter +int nbValues(AmdaResultParser::ValueType valueType) noexcept +{ + switch (valueType) { + case AmdaResultParser::ValueType::SCALAR: + return 1; + case AmdaResultParser::ValueType::VECTOR: + return 3; + case AmdaResultParser::ValueType::UNKNOWN: + // Invalid case + break; + } + + // Invalid cases + qCCritical(LOG_AmdaResultParser()) + << QObject::tr("Can't get the number of values to read: unsupported type"); + return 0; +} + /** * Reads stream to retrieve x-axis unit * @param stream the stream to read @@ -74,10 +94,13 @@ Unit readXAxisUnit(QTextStream &stream) * @param stream the stream to read * @return the pair of vectors x-axis data/values data that has been read in the stream */ -QPair, QVector > readResults(QTextStream &stream) +QPair, QVector > > +readResults(QTextStream &stream, AmdaResultParser::ValueType valueType) { + auto expectedNbValues = nbValues(valueType); + auto xData = QVector{}; - auto valuesData = QVector{}; + auto valuesData = QVector >(expectedNbValues); QString line{}; @@ -85,27 +108,31 @@ QPair, QVector > readResults(QTextStream &stream) // Ignore comment lines if (!isCommentLine(line)) { auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts); - if (lineData.size() == 2) { + if (lineData.size() == expectedNbValues + 1) { // X : the data is converted from date to double (in secs) auto x = doubleDate(lineData.at(0)); - // Value - bool valueOk; - auto value = lineData.at(1).toDouble(&valueOk); - // Adds result only if x is valid. Then, if value is invalid, it is set to NaN if (!std::isnan(x)) { xData.push_back(x); - if (!valueOk) { - qCWarning(LOG_AmdaResultParser()) - << QObject::tr( - "Value from line %1 is invalid and will be converted to NaN") - .arg(line); - value = std::numeric_limits::quiet_NaN(); + // Values + for (auto valueIndex = 0; valueIndex < expectedNbValues; ++valueIndex) { + auto column = valueIndex + 1; + + bool valueOk; + auto value = lineData.at(column).toDouble(&valueOk); + + if (!valueOk) { + qCWarning(LOG_AmdaResultParser()) + << QObject::tr( + "Value from (line %1, column %2) is invalid and will be " + "converted to NaN") + .arg(line, column); + value = std::numeric_limits::quiet_NaN(); + } + valuesData[valueIndex].append(value); } - - valuesData.push_back(value); } else { qCWarning(LOG_AmdaResultParser()) @@ -125,8 +152,15 @@ QPair, QVector > readResults(QTextStream &stream) } // namespace -std::shared_ptr AmdaResultParser::readTxt(const QString &filePath) noexcept +std::shared_ptr AmdaResultParser::readTxt(const QString &filePath, + ValueType valueType) noexcept { + if (valueType == ValueType::UNKNOWN) { + qCCritical(LOG_AmdaResultParser()) + << QObject::tr("Can't retrieve AMDA data: the type of values to be read is unknown"); + return nullptr; + } + QFile file{filePath}; if (!file.open(QFile::ReadOnly | QIODevice::Text)) { @@ -153,8 +187,30 @@ std::shared_ptr AmdaResultParser::readTxt(const QString &filePath) // Reads results stream.seek(0); // returns to the beginning of the file - auto results = readResults(stream); + auto results = readResults(stream, valueType); + + // Creates data series + switch (valueType) { + case ValueType::SCALAR: + Q_ASSERT(results.second.size() == 1); + return std::make_shared( + std::move(results.first), std::move(results.second.takeFirst()), xAxisUnit, Unit{}); + case ValueType::VECTOR: { + Q_ASSERT(results.second.size() == 3); + auto xValues = results.second.takeFirst(); + auto yValues = results.second.takeFirst(); + auto zValues = results.second.takeFirst(); + return std::make_shared(std::move(results.first), std::move(xValues), + std::move(yValues), std::move(zValues), xAxisUnit, + Unit{}); + } + case ValueType::UNKNOWN: + // Invalid case + break; + } - return std::make_shared(std::move(results.first), std::move(results.second), - xAxisUnit, Unit{}); + // Invalid cases + qCCritical(LOG_AmdaResultParser()) + << QObject::tr("Can't create data series: unsupported value type"); + return nullptr; } diff --git a/plugins/amda/tests-resources/TestAmdaResultParser/ValidVector1.txt b/plugins/amda/tests-resources/TestAmdaResultParser/ValidVector1.txt new file mode 100644 index 0000000..cadabb8 --- /dev/null +++ b/plugins/amda/tests-resources/TestAmdaResultParser/ValidVector1.txt @@ -0,0 +1,12 @@ +#Time Format : YYYY-MM-DDThh:mm:ss.mls +#imf - Type : Local Parameter @ CDPP/AMDA - Name : imf_gse - Units : nT - Size : 3 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim +2013-07-02T09:13:50.000 -0.332000 3.20600 0.0580000 +2013-07-02T09:14:06.000 -1.01100 2.99900 0.496000 +2013-07-02T09:14:22.000 -1.45700 2.78500 1.01800 +2013-07-02T09:14:38.000 -1.29300 2.73600 1.48500 +2013-07-02T09:14:54.000 -1.21700 2.61200 1.66200 +2013-07-02T09:15:10.000 -1.44300 2.56400 1.50500 +2013-07-02T09:15:26.000 -1.27800 2.89200 1.16800 +2013-07-02T09:15:42.000 -1.20200 2.86200 1.24400 +2013-07-02T09:15:58.000 -1.22000 2.85900 1.15000 +2013-07-02T09:16:14.000 -1.25900 2.76400 1.35800 \ No newline at end of file diff --git a/plugins/amda/tests/TestAmdaResultParser.cpp b/plugins/amda/tests/TestAmdaResultParser.cpp index 9b21c44..13241c2 100644 --- a/plugins/amda/tests/TestAmdaResultParser.cpp +++ b/plugins/amda/tests/TestAmdaResultParser.cpp @@ -1,6 +1,7 @@ #include "AmdaResultParser.h" #include +#include #include #include @@ -11,6 +12,11 @@ namespace { const auto TESTS_RESOURCES_PATH = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath(); +QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds) +{ + return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC}; +} + /// Compares two vectors that can potentially contain NaN values bool compareVectors(const QVector &v1, const QVector &v2) { @@ -31,17 +37,50 @@ bool compareVectors(const QVector &v1, const QVector &v2) return result; } +bool compareVectors(const QVector > &v1, const QVector > &v2) +{ + if (v1.size() != v2.size()) { + return false; + } + + auto result = true; + for (auto i = 0; i < v1.size() && result; ++i) { + result &= compareVectors(v1.at(i), v2.at(i)); + } + + return result; +} + +QVector > valuesData(const ArrayData<1> &arrayData) +{ + return QVector >{arrayData.data()}; +} + +QVector > valuesData(const ArrayData<2> &arrayData) +{ + return arrayData.data(); +} + + QString inputFilePath(const QString &inputFileName) { return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath(); } +template struct ExpectedResults { explicit ExpectedResults() = default; - /// Ctor with QVector as x-axis data. Datetimes are converted to doubles explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector &xAxisData, QVector valuesData) + : ExpectedResults(xAxisUnit, valuesUnit, xAxisData, + QVector >{std::move(valuesData)}) + { + } + + /// Ctor with QVector as x-axis data. Datetimes are converted to doubles + explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector &xAxisData, + QVector > valuesData) : m_ParsingOK{true}, m_XAxisUnit{xAxisUnit}, m_ValuesUnit{valuesUnit}, @@ -60,17 +99,17 @@ struct ExpectedResults { void validate(std::shared_ptr results) { if (m_ParsingOK) { - auto scalarSeries = dynamic_cast(results.get()); - QVERIFY(scalarSeries != nullptr); + auto dataSeries = dynamic_cast(results.get()); + QVERIFY(dataSeries != nullptr); // Checks units - QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit); - QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit); + QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit); + QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit); // Checks values : as the vectors can potentially contain NaN values, we must use a // custom vector comparison method - QVERIFY(compareVectors(scalarSeries->xAxisData()->data(), m_XAxisData)); - QVERIFY(compareVectors(scalarSeries->valuesData()->data(), m_ValuesData)); + QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData)); + QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData)); } else { QVERIFY(results == nullptr); @@ -86,47 +125,74 @@ struct ExpectedResults { // Expected x-axis data QVector m_XAxisData{}; // Expected values data - QVector m_ValuesData{}; + QVector > m_ValuesData{}; }; } // namespace -Q_DECLARE_METATYPE(ExpectedResults) +Q_DECLARE_METATYPE(ExpectedResults) +Q_DECLARE_METATYPE(ExpectedResults) class TestAmdaResultParser : public QObject { Q_OBJECT +private: + template + void testReadDataStructure() + { + // ////////////// // + // Test structure // + // ////////////// // + + // Name of TXT file to read + QTest::addColumn("inputFileName"); + // Expected results + QTest::addColumn >("expectedResults"); + } + + template + void testRead(AmdaResultParser::ValueType valueType) + { + QFETCH(QString, inputFileName); + QFETCH(ExpectedResults, expectedResults); + + // Parses file + auto filePath = inputFilePath(inputFileName); + auto results = AmdaResultParser::readTxt(filePath, valueType); + + // ///////////////// // + // Validates results // + // ///////////////// // + expectedResults.validate(results); + } + private slots: /// Input test data - /// @sa testTxtJson() - void testReadTxt_data(); + /// @sa testReadScalarTxt() + void testReadScalarTxt_data(); - /// Tests parsing of a TXT file - void testReadTxt(); + /// Tests parsing scalar series of a TXT file + void testReadScalarTxt(); + + /// Input test data + /// @sa testReadVectorTxt() + void testReadVectorTxt_data(); + + /// Tests parsing vector series of a TXT file + void testReadVectorTxt(); }; -void TestAmdaResultParser::testReadTxt_data() +void TestAmdaResultParser::testReadScalarTxt_data() { - // ////////////// // - // Test structure // - // ////////////// // - - // Name of TXT file to read - QTest::addColumn("inputFileName"); - // Expected results - QTest::addColumn("expectedResults"); + testReadDataStructure(); // ////////// // // Test cases // // ////////// // - auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) { - return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC}; - }; - // Valid files QTest::newRow("Valid file") << QStringLiteral("ValidScalar1.txt") - << ExpectedResults{ + << ExpectedResults{ Unit{QStringLiteral("nT"), true}, Unit{}, QVector{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30), @@ -138,7 +204,7 @@ void TestAmdaResultParser::testReadTxt_data() QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN") << QStringLiteral("WrongValue.txt") - << ExpectedResults{ + << ExpectedResults{ Unit{QStringLiteral("nT"), true}, Unit{}, QVector{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)}, @@ -146,7 +212,7 @@ void TestAmdaResultParser::testReadTxt_data() QTest::newRow("Valid file that contains NaN values") << QStringLiteral("NaNValue.txt") - << ExpectedResults{ + << ExpectedResults{ Unit{QStringLiteral("nT"), true}, Unit{}, QVector{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)}, @@ -154,58 +220,90 @@ void TestAmdaResultParser::testReadTxt_data() // Valid files but with some invalid lines (wrong unit, wrong values, etc.) QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt") - << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{}, - QVector{}, QVector{}}; + << ExpectedResults{Unit{QStringLiteral(""), true}, + Unit{}, QVector{}, + QVector{}}; QTest::newRow("Wrong unit file") << QStringLiteral("WrongUnit.txt") - << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{}, - QVector{dateTime(2013, 9, 23, 9, 0, 30), - dateTime(2013, 9, 23, 9, 1, 30), - dateTime(2013, 9, 23, 9, 2, 30)}, - QVector{-2.83950, -2.71850, -2.52150}}; + << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{}, + QVector{dateTime(2013, 9, 23, 9, 0, 30), + dateTime(2013, 9, 23, 9, 1, 30), + dateTime(2013, 9, 23, 9, 2, 30)}, + QVector{-2.83950, -2.71850, -2.52150}}; QTest::newRow("Wrong results file (date of first line is invalid") << QStringLiteral("WrongDate.txt") - << ExpectedResults{ + << ExpectedResults{ Unit{QStringLiteral("nT"), true}, Unit{}, QVector{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)}, QVector{-2.71850, -2.52150}}; QTest::newRow("Wrong results file (too many values for first line") << QStringLiteral("TooManyValues.txt") - << ExpectedResults{ + << ExpectedResults{ Unit{QStringLiteral("nT"), true}, Unit{}, QVector{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)}, QVector{-2.71850, -2.52150}}; QTest::newRow("Wrong results file (x of first line is NaN") << QStringLiteral("NaNX.txt") - << ExpectedResults{ + << ExpectedResults{ Unit{QStringLiteral("nT"), true}, Unit{}, QVector{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)}, QVector{-2.71850, -2.52150}}; + QTest::newRow("Invalid file type (vector)") + << QStringLiteral("ValidVector1.txt") + << ExpectedResults{Unit{QStringLiteral("nT"), true}, Unit{}, + QVector{}, QVector{}}; + // Invalid files - QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt") - << ExpectedResults{}; + QTest::newRow("Invalid file (unexisting file)") + << QStringLiteral("UnexistingFile.txt") << ExpectedResults{}; + + QTest::newRow("Invalid file (file not found on server)") + << QStringLiteral("FileNotFound.txt") << ExpectedResults{}; +} - QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt") - << ExpectedResults{}; +void TestAmdaResultParser::testReadScalarTxt() +{ + testRead(AmdaResultParser::ValueType::SCALAR); } -void TestAmdaResultParser::testReadTxt() +void TestAmdaResultParser::testReadVectorTxt_data() { - QFETCH(QString, inputFileName); - QFETCH(ExpectedResults, expectedResults); + testReadDataStructure(); + + // ////////// // + // Test cases // + // ////////// // + + // Valid files + QTest::newRow("Valid file") + << QStringLiteral("ValidVector1.txt") + << ExpectedResults{ + Unit{QStringLiteral("nT"), true}, Unit{}, + QVector{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6), + dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38), + dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10), + dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42), + dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)}, + QVector >{ + {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259}, + {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764}, + {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}}; - // Parses file - auto filePath = inputFilePath(inputFileName); - auto results = AmdaResultParser::readTxt(filePath); + // Valid files but with some invalid lines (wrong unit, wrong values, etc.) + QTest::newRow("Invalid file type (scalar)") + << QStringLiteral("ValidScalar1.txt") + << ExpectedResults{Unit{QStringLiteral("nT"), true}, Unit{}, + QVector{}, + QVector >{{}, {}, {}}}; +} - // ///////////////// // - // Validates results // - // ///////////////// // - expectedResults.validate(results); +void TestAmdaResultParser::testReadVectorTxt() +{ + testRead(AmdaResultParser::ValueType::VECTOR); } QTEST_MAIN(TestAmdaResultParser)