##// END OF EJS Templates
Add the cmath include missing
perrinel -
r400:202e9db74e6e
parent child
Show More
@@ -1,132 +1,134
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4
5 5 #include <QDateTime>
6 6 #include <QFile>
7 7 #include <QRegularExpression>
8 8
9 #include <cmath>
10
9 11 Q_LOGGING_CATEGORY(LOG_AmdaResultParser, "AmdaResultParser")
10 12
11 13 namespace {
12 14
13 15 /// Format for dates in result files
14 16 const auto DATE_FORMAT = QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz");
15 17
16 18 /// Separator between values in a result line
17 19 const auto RESULT_LINE_SEPARATOR = QRegularExpression{QStringLiteral("\\s+")};
18 20
19 21 /// Regex to find unit in a line. Examples of valid lines:
20 22 /// ... - Units : nT - ...
21 23 /// ... -Units:nT- ...
22 24 /// ... -Units: mΒ²- ...
23 25 /// ... - Units : m/s - ...
24 26 const auto UNIT_REGEX = QRegularExpression{QStringLiteral("-\\s*Units\\s*:\\s*(.+?)\\s*-")};
25 27
26 28 /// Converts a string date to a double date
27 29 /// @return a double that represents the date in seconds, NaN if the string date can't be converted
28 30 double doubleDate(const QString &stringDate) noexcept
29 31 {
30 32 auto dateTime = QDateTime::fromString(stringDate, DATE_FORMAT);
31 33 return dateTime.isValid() ? (dateTime.toMSecsSinceEpoch() / 1000.)
32 34 : std::numeric_limits<double>::quiet_NaN();
33 35 }
34 36
35 37 /**
36 38 * Reads stream to retrieve x-axis unit
37 39 * @param stream the stream to read
38 40 * @return the unit that has been read in the stream, a default unit (time unit with no label) if an
39 41 * error occured during reading
40 42 */
41 43 Unit readXAxisUnit(QTextStream &stream)
42 44 {
43 45 QString line{};
44 46
45 47 if (stream.readLineInto(&line)) {
46 48 auto match = UNIT_REGEX.match(line);
47 49 if (match.hasMatch()) {
48 50 return Unit{match.captured(1), true};
49 51 }
50 52 else {
51 53 qCWarning(LOG_AmdaResultParser())
52 54 << QObject::tr("Can't read unit: invalid line %1").arg(line);
53 55 }
54 56 }
55 57 else {
56 58 qCWarning(LOG_AmdaResultParser()) << QObject::tr("Can't read unit: end of file");
57 59 }
58 60
59 61 // Error cases
60 62 return Unit{{}, true};
61 63 }
62 64
63 65 /**
64 66 * Reads stream to retrieve results
65 67 * @param stream the stream to read
66 68 * @return the pair of vectors x-axis data/values data that has been read in the stream
67 69 */
68 70 QPair<QVector<double>, QVector<double> > readResults(QTextStream &stream)
69 71 {
70 72 auto xData = QVector<double>{};
71 73 auto valuesData = QVector<double>{};
72 74
73 75 QString line{};
74 76 while (stream.readLineInto(&line)) {
75 77 auto lineData = line.split(RESULT_LINE_SEPARATOR, QString::SkipEmptyParts);
76 78 if (lineData.size() == 2) {
77 79 // X : the data is converted from date to double (in secs)
78 80 auto x = doubleDate(lineData.at(0));
79 81
80 82 // Value
81 83 bool valueOk;
82 84 auto value = lineData.at(1).toDouble(&valueOk);
83 85
84 86 // Adds result only if x and value are valid
85 87 if (!std::isnan(x) && !std::isnan(value) && valueOk) {
86 88 xData.push_back(x);
87 89 valuesData.push_back(value);
88 90 }
89 91 else {
90 92 qCWarning(LOG_AmdaResultParser())
91 93 << QObject::tr(
92 94 "Can't retrieve results from line %1: x and/or value are invalid")
93 95 .arg(line);
94 96 }
95 97 }
96 98 else {
97 99 qCWarning(LOG_AmdaResultParser())
98 100 << QObject::tr("Can't retrieve results from line %1: invalid line").arg(line);
99 101 }
100 102 }
101 103
102 104 return qMakePair(std::move(xData), std::move(valuesData));
103 105 }
104 106
105 107 } // namespace
106 108
107 109 std::shared_ptr<IDataSeries> AmdaResultParser::readTxt(const QString &filePath) noexcept
108 110 {
109 111 QFile file{filePath};
110 112
111 113 if (!file.open(QFile::ReadOnly | QIODevice::Text)) {
112 114 qCCritical(LOG_AmdaResultParser())
113 115 << QObject::tr("Can't retrieve AMDA data from file %1: %2")
114 116 .arg(filePath, file.errorString());
115 117 return nullptr;
116 118 }
117 119
118 120 QTextStream stream{&file};
119 121
120 122 // Ignore first two lines (comments lines)
121 123 stream.readLine();
122 124 stream.readLine();
123 125
124 126 // Reads x-axis unit
125 127 auto xAxisUnit = readXAxisUnit(stream);
126 128
127 129 // Reads results
128 130 auto results = readResults(stream);
129 131
130 132 return std::make_shared<ScalarSeries>(std::move(results.first), std::move(results.second),
131 133 xAxisUnit, Unit{});
132 134 }
@@ -1,179 +1,179
1 1 #include "AmdaResultParser.h"
2 2
3 3 #include <Data/ScalarSeries.h>
4 4
5 5 #include <QObject>
6 6 #include <QtTest>
7 7
8 8 namespace {
9 9
10 10 /// Path for the tests
11 11 const auto TESTS_RESOURCES_PATH
12 12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13 13
14 14 QString inputFilePath(const QString &inputFileName)
15 15 {
16 16 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
17 17 }
18 18
19 19 struct ExpectedResults {
20 20 explicit ExpectedResults() = default;
21 21
22 22 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
23 23 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
24 24 QVector<double> valuesData)
25 25 : m_ParsingOK{true},
26 26 m_XAxisUnit{xAxisUnit},
27 27 m_ValuesUnit{valuesUnit},
28 28 m_XAxisData{},
29 29 m_ValuesData{std::move(valuesData)}
30 30 {
31 31 // Converts QVector<QDateTime> to QVector<double>
32 32 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
33 33 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
34 34 }
35 35
36 36 /**
37 37 * Validates a DataSeries compared to the expected results
38 38 * @param results the DataSeries to validate
39 39 */
40 40 void validate(std::shared_ptr<IDataSeries> results)
41 41 {
42 42 if (m_ParsingOK) {
43 43 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
44 44 QVERIFY(scalarSeries != nullptr);
45 45
46 46 // Checks units
47 47 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
48 48 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
49 49
50 50 // Checks values
51 51 QVERIFY(scalarSeries->xAxisData()->data() == m_XAxisData);
52 52 QVERIFY(scalarSeries->valuesData()->data() == m_ValuesData);
53 53 }
54 54 else {
55 55 QVERIFY(results == nullptr);
56 56 }
57 57 }
58 58
59 59 // Parsing was successfully completed
60 60 bool m_ParsingOK{false};
61 61 // Expected x-axis unit
62 62 Unit m_XAxisUnit{};
63 63 // Expected values unit
64 64 Unit m_ValuesUnit{};
65 65 // Expected x-axis data
66 66 QVector<double> m_XAxisData{};
67 67 // Expected values data
68 68 QVector<double> m_ValuesData{};
69 69 };
70 70
71 71 } // namespace
72 72
73 73 Q_DECLARE_METATYPE(ExpectedResults)
74 74
75 75 class TestAmdaResultParser : public QObject {
76 76 Q_OBJECT
77 77 private slots:
78 78 /// Input test data
79 79 /// @sa testTxtJson()
80 80 void testReadTxt_data();
81 81
82 82 /// Tests parsing of a TXT file
83 83 void testReadTxt();
84 84 };
85 85
86 86 void TestAmdaResultParser::testReadTxt_data()
87 87 {
88 88 // ////////////// //
89 89 // Test structure //
90 90 // ////////////// //
91 91
92 92 // Name of TXT file to read
93 93 QTest::addColumn<QString>("inputFileName");
94 94 // Expected results
95 95 QTest::addColumn<ExpectedResults>("expectedResults");
96 96
97 97 // ////////// //
98 98 // Test cases //
99 99 // ////////// //
100 100
101 101 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
102 102 return QDateTime{{year, month, day}, {hours, minutes, seconds}};
103 103 };
104 104
105 105 // Valid file
106 106 QTest::newRow("Valid file")
107 107 << QStringLiteral("ValidScalar1.txt")
108 108 << ExpectedResults{
109 109 Unit{QStringLiteral("nT"), true}, Unit{},
110 110 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
111 111 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
112 112 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
113 113 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
114 114 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
115 115 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
116 116 -2.55800, -2.43250, -2.42200}};
117 117
118 118 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
119 119 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
120 120 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
121 121 QVector<QDateTime>{}, QVector<double>{}};
122 122 QTest::newRow("Wrong unit file")
123 123 << QStringLiteral("WrongUnit.txt")
124 124 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
125 125 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
126 126 dateTime(2013, 9, 23, 9, 1, 30),
127 127 dateTime(2013, 9, 23, 9, 2, 30)},
128 128 QVector<double>{-2.83950, -2.71850, -2.52150}};
129 129
130 130 QTest::newRow("Wrong results file (date of first line is invalid")
131 131 << QStringLiteral("WrongDate.txt")
132 132 << ExpectedResults{
133 133 Unit{QStringLiteral("nT"), true}, Unit{},
134 134 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
135 135 QVector<double>{-2.71850, -2.52150}};
136 136
137 137 QTest::newRow("Wrong results file (too many values for first line")
138 138 << QStringLiteral("TooManyValues.txt")
139 139 << ExpectedResults{
140 140 Unit{QStringLiteral("nT"), true}, Unit{},
141 141 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
142 142 QVector<double>{-2.71850, -2.52150}};
143 143
144 144 QTest::newRow("Wrong results file (value of first line is invalid")
145 145 << QStringLiteral("WrongValue.txt")
146 146 << ExpectedResults{
147 147 Unit{QStringLiteral("nT"), true}, Unit{},
148 148 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
149 149 QVector<double>{-2.71850, -2.52150}};
150 150
151 151 QTest::newRow("Wrong results file (value of first line is NaN")
152 152 << QStringLiteral("NaNValue.txt")
153 153 << ExpectedResults{
154 154 Unit{QStringLiteral("nT"), true}, Unit{},
155 155 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
156 156 QVector<double>{-2.71850, -2.52150}};
157 157
158 158 // Invalid file
159 QTest::newRow("Invalid file (unexisting file)")
160 << QStringLiteral("UnexistingFile.txt") << ExpectedResults{};
159 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
160 << ExpectedResults{};
161 161 }
162 162
163 163 void TestAmdaResultParser::testReadTxt()
164 164 {
165 165 QFETCH(QString, inputFileName);
166 166 QFETCH(ExpectedResults, expectedResults);
167 167
168 168 // Parses file
169 169 auto filePath = inputFilePath(inputFileName);
170 170 auto results = AmdaResultParser::readTxt(filePath);
171 171
172 172 // ///////////////// //
173 173 // Validates results //
174 174 // ///////////////// //
175 175 expectedResults.validate(results);
176 176 }
177 177
178 178 QTEST_MAIN(TestAmdaResultParser)
179 179 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now