##// END OF EJS Templates
Adds unit tests for reading vectors in AMDA
Alexandre Leroux -
r535:a7ded9f58584
parent child
Show More
@@ -0,0 +1,12
1 #Time Format : YYYY-MM-DDThh:mm:ss.mls
2 #imf - Type : Local Parameter @ CDPP/AMDA - Name : imf_gse - Units : nT - Size : 3 - Frame : GSE - Mission : ACE - Instrument : MFI - Dataset : mfi_final-prelim
3 2013-07-02T09:13:50.000 -0.332000 3.20600 0.0580000
4 2013-07-02T09:14:06.000 -1.01100 2.99900 0.496000
5 2013-07-02T09:14:22.000 -1.45700 2.78500 1.01800
6 2013-07-02T09:14:38.000 -1.29300 2.73600 1.48500
7 2013-07-02T09:14:54.000 -1.21700 2.61200 1.66200
8 2013-07-02T09:15:10.000 -1.44300 2.56400 1.50500
9 2013-07-02T09:15:26.000 -1.27800 2.89200 1.16800
10 2013-07-02T09:15:42.000 -1.20200 2.86200 1.24400
11 2013-07-02T09:15:58.000 -1.22000 2.85900 1.15000
12 2013-07-02T09:16:14.000 -1.25900 2.76400 1.35800 No newline at end of file
@@ -1,298 +1,313
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include <Common/SortUtils.h>
4 #include <Common/SortUtils.h>
5
5
6 #include <QReadLocker>
6 #include <QReadLocker>
7 #include <QReadWriteLock>
7 #include <QReadWriteLock>
8 #include <QVector>
8 #include <QVector>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 template <int Dim>
12 template <int Dim>
13 class ArrayData;
13 class ArrayData;
14
14
15 using DataContainer = QVector<QVector<double> >;
15 using DataContainer = QVector<QVector<double> >;
16
16
17 namespace arraydata_detail {
17 namespace arraydata_detail {
18
18
19 /// Struct used to sort ArrayData
19 /// Struct used to sort ArrayData
20 template <int Dim>
20 template <int Dim>
21 struct Sort {
21 struct Sort {
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
23 const std::vector<int> &sortPermutation)
23 const std::vector<int> &sortPermutation)
24 {
24 {
25 auto nbComponents = data.size();
25 auto nbComponents = data.size();
26 auto sortedData = DataContainer(nbComponents);
26 auto sortedData = DataContainer(nbComponents);
27
27
28 for (auto i = 0; i < nbComponents; ++i) {
28 for (auto i = 0; i < nbComponents; ++i) {
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
30 }
30 }
31
31
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
33 }
33 }
34 };
34 };
35
35
36 /// Specialization for uni-dimensional ArrayData
36 /// Specialization for uni-dimensional ArrayData
37 template <>
37 template <>
38 struct Sort<1> {
38 struct Sort<1> {
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
40 const std::vector<int> &sortPermutation)
40 const std::vector<int> &sortPermutation)
41 {
41 {
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
43 }
43 }
44 };
44 };
45
45
46 } // namespace arraydata_detail
46 } // namespace arraydata_detail
47
47
48 /**
48 /**
49 * @brief The ArrayData class represents a dataset for a data series.
49 * @brief The ArrayData class represents a dataset for a data series.
50 *
50 *
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
53 * number of values
53 * number of values
54 *
54 *
55 * @tparam Dim the dimension of the ArrayData (one or two)
55 * @tparam Dim the dimension of the ArrayData (one or two)
56 * @sa IDataSeries
56 * @sa IDataSeries
57 */
57 */
58 template <int Dim>
58 template <int Dim>
59 class ArrayData {
59 class ArrayData {
60 public:
60 public:
61 class IteratorValue {
61 class IteratorValue {
62 public:
62 public:
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
64 {
64 {
65 for (auto i = 0; i < container.size(); ++i) {
65 for (auto i = 0; i < container.size(); ++i) {
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
67 }
67 }
68 }
68 }
69
69
70 double at(int index) const { return *m_Its.at(index); }
70 double at(int index) const { return *m_Its.at(index); }
71 double first() const { return *m_Its.front(); }
71 double first() const { return *m_Its.front(); }
72
72
73 void next()
73 void next()
74 {
74 {
75 for (auto &it : m_Its) {
75 for (auto &it : m_Its) {
76 ++it;
76 ++it;
77 }
77 }
78 }
78 }
79
79
80 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
80 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
81
81
82 private:
82 private:
83 std::vector<DataContainer::value_type::const_iterator> m_Its;
83 std::vector<DataContainer::value_type::const_iterator> m_Its;
84 };
84 };
85
85
86 class Iterator {
86 class Iterator {
87 public:
87 public:
88 using iterator_category = std::forward_iterator_tag;
88 using iterator_category = std::forward_iterator_tag;
89 using value_type = const IteratorValue;
89 using value_type = const IteratorValue;
90 using difference_type = std::ptrdiff_t;
90 using difference_type = std::ptrdiff_t;
91 using pointer = value_type *;
91 using pointer = value_type *;
92 using reference = value_type &;
92 using reference = value_type &;
93
93
94 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
94 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
95
95
96 virtual ~Iterator() noexcept = default;
96 virtual ~Iterator() noexcept = default;
97 Iterator(const Iterator &) = default;
97 Iterator(const Iterator &) = default;
98 Iterator(Iterator &&) = default;
98 Iterator(Iterator &&) = default;
99 Iterator &operator=(const Iterator &) = default;
99 Iterator &operator=(const Iterator &) = default;
100 Iterator &operator=(Iterator &&) = default;
100 Iterator &operator=(Iterator &&) = default;
101
101
102 Iterator &operator++()
102 Iterator &operator++()
103 {
103 {
104 m_CurrentValue.next();
104 m_CurrentValue.next();
105 return *this;
105 return *this;
106 }
106 }
107
107
108 pointer operator->() const { return &m_CurrentValue; }
108 pointer operator->() const { return &m_CurrentValue; }
109 reference operator*() const { return m_CurrentValue; }
109 reference operator*() const { return m_CurrentValue; }
110
110
111 bool operator==(const Iterator &other) const
111 bool operator==(const Iterator &other) const
112 {
112 {
113 return m_CurrentValue == other.m_CurrentValue;
113 return m_CurrentValue == other.m_CurrentValue;
114 }
114 }
115
115
116 bool operator!=(const Iterator &other) const { return !(*this == other); }
116 bool operator!=(const Iterator &other) const { return !(*this == other); }
117
117
118 private:
118 private:
119 IteratorValue m_CurrentValue;
119 IteratorValue m_CurrentValue;
120 };
120 };
121
121
122 // ///// //
122 // ///// //
123 // Ctors //
123 // Ctors //
124 // ///// //
124 // ///// //
125
125
126 /**
126 /**
127 * Ctor for a unidimensional ArrayData
127 * Ctor for a unidimensional ArrayData
128 * @param data the data the ArrayData will hold
128 * @param data the data the ArrayData will hold
129 */
129 */
130 template <int D = Dim, typename = std::enable_if_t<D == 1> >
130 template <int D = Dim, typename = std::enable_if_t<D == 1> >
131 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
131 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
132 {
132 {
133 m_Data[0] = std::move(data);
133 m_Data[0] = std::move(data);
134 }
134 }
135
135
136 /**
136 /**
137 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
137 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
138 * greater than 2 and each component must have the same number of values
138 * greater than 2 and each component must have the same number of values
139 * @param data the data the ArrayData will hold
139 * @param data the data the ArrayData will hold
140 * @throws std::invalid_argument if the number of components is less than 2
140 * @throws std::invalid_argument if the number of components is less than 2
141 * @remarks if the number of values is not the same for each component, no value is set
141 * @remarks if the number of values is not the same for each component, no value is set
142 */
142 */
143 template <int D = Dim, typename = std::enable_if_t<D == 2> >
143 template <int D = Dim, typename = std::enable_if_t<D == 2> >
144 explicit ArrayData(DataContainer data)
144 explicit ArrayData(DataContainer data)
145 {
145 {
146 auto nbComponents = data.size();
146 auto nbComponents = data.size();
147 if (nbComponents < 2) {
147 if (nbComponents < 2) {
148 throw std::invalid_argument{
148 throw std::invalid_argument{
149 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
149 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
150 .arg(data.size())
150 .arg(data.size())
151 .toStdString()};
151 .toStdString()};
152 }
152 }
153
153
154 auto nbValues = data.front().size();
154 auto nbValues = data.front().size();
155 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
155 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
156 return component.size() == nbValues;
156 return component.size() == nbValues;
157 })) {
157 })) {
158 m_Data = std::move(data);
158 m_Data = std::move(data);
159 }
159 }
160 else {
160 else {
161 m_Data = DataContainer{nbComponents, QVector<double>{}};
161 m_Data = DataContainer{nbComponents, QVector<double>{}};
162 }
162 }
163 }
163 }
164
164
165 /// Copy ctor
165 /// Copy ctor
166 explicit ArrayData(const ArrayData &other)
166 explicit ArrayData(const ArrayData &other)
167 {
167 {
168 QReadLocker otherLocker{&other.m_Lock};
168 QReadLocker otherLocker{&other.m_Lock};
169 m_Data = other.m_Data;
169 m_Data = other.m_Data;
170 }
170 }
171
171
172 // /////////////// //
172 // /////////////// //
173 // General methods //
173 // General methods //
174 // /////////////// //
174 // /////////////// //
175
175
176 /**
176 /**
177 * Merges into the array data an other array data. The two array datas must have the same number
177 * Merges into the array data an other array data. The two array datas must have the same number
178 * of components so the merge can be done
178 * of components so the merge can be done
179 * @param other the array data to merge with
179 * @param other the array data to merge with
180 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
180 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
181 * inserted at the end
181 * inserted at the end
182 */
182 */
183 void add(const ArrayData<Dim> &other, bool prepend = false)
183 void add(const ArrayData<Dim> &other, bool prepend = false)
184 {
184 {
185 QWriteLocker locker{&m_Lock};
185 QWriteLocker locker{&m_Lock};
186 QReadLocker otherLocker{&other.m_Lock};
186 QReadLocker otherLocker{&other.m_Lock};
187
187
188 auto nbComponents = m_Data.size();
188 auto nbComponents = m_Data.size();
189 if (nbComponents != other.m_Data.size()) {
189 if (nbComponents != other.m_Data.size()) {
190 return;
190 return;
191 }
191 }
192
192
193 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
193 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
194 if (prepend) {
194 if (prepend) {
195 const auto &otherData = other.data(componentIndex);
195 const auto &otherData = other.data(componentIndex);
196 const auto otherDataSize = otherData.size();
196 const auto otherDataSize = otherData.size();
197
197
198 auto &data = m_Data[componentIndex];
198 auto &data = m_Data[componentIndex];
199 data.insert(data.begin(), otherDataSize, 0.);
199 data.insert(data.begin(), otherDataSize, 0.);
200
200
201 for (auto i = 0; i < otherDataSize; ++i) {
201 for (auto i = 0; i < otherDataSize; ++i) {
202 data.replace(i, otherData.at(i));
202 data.replace(i, otherData.at(i));
203 }
203 }
204 }
204 }
205 else {
205 else {
206 m_Data[componentIndex] += other.data(componentIndex);
206 m_Data[componentIndex] += other.data(componentIndex);
207 }
207 }
208 }
208 }
209 }
209 }
210
210
211 void clear()
211 void clear()
212 {
212 {
213 QWriteLocker locker{&m_Lock};
213 QWriteLocker locker{&m_Lock};
214
214
215 auto nbComponents = m_Data.size();
215 auto nbComponents = m_Data.size();
216 for (auto i = 0; i < nbComponents; ++i) {
216 for (auto i = 0; i < nbComponents; ++i) {
217 m_Data[i].clear();
217 m_Data[i].clear();
218 }
218 }
219 }
219 }
220
220
221 /**
221 /**
222 * @return the data of a component
222 * @return the data of a component
223 * @param componentIndex the index of the component to retrieve the data
223 * @param componentIndex the index of the component to retrieve the data
224 * @return the component's data, empty vector if the index is invalid
224 * @return the component's data, empty vector if the index is invalid
225 */
225 */
226 QVector<double> data(int componentIndex) const noexcept
226 QVector<double> data(int componentIndex) const noexcept
227 {
227 {
228 QReadLocker locker{&m_Lock};
228 QReadLocker locker{&m_Lock};
229
229
230 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
230 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
231 : QVector<double>{};
231 : QVector<double>{};
232 }
232 }
233
233
234 /// @return the size (i.e. number of values) of a single component
234 /// @return the size (i.e. number of values) of a single component
235 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
235 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
236 int size() const
236 int size() const
237 {
237 {
238 QReadLocker locker{&m_Lock};
238 QReadLocker locker{&m_Lock};
239 return m_Data[0].size();
239 return m_Data[0].size();
240 }
240 }
241
241
242 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
242 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
243 {
243 {
244 QReadLocker locker{&m_Lock};
244 QReadLocker locker{&m_Lock};
245 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
245 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
246 }
246 }
247
247
248 // ///////// //
248 // ///////// //
249 // Iterators //
249 // Iterators //
250 // ///////// //
250 // ///////// //
251
251
252 Iterator cbegin() const { return Iterator{m_Data, true}; }
252 Iterator cbegin() const { return Iterator{m_Data, true}; }
253 Iterator cend() const { return Iterator{m_Data, false}; }
253 Iterator cend() const { return Iterator{m_Data, false}; }
254
254
255 // ///////////// //
255 // ///////////// //
256 // 1-dim methods //
256 // 1-dim methods //
257 // ///////////// //
257 // ///////////// //
258
258
259 /**
259 /**
260 * @return the data at a specified index
260 * @return the data at a specified index
261 * @remarks index must be a valid position
261 * @remarks index must be a valid position
262 * @remarks this method is only available for a unidimensional ArrayData
262 * @remarks this method is only available for a unidimensional ArrayData
263 */
263 */
264 template <int D = Dim, typename = std::enable_if_t<D == 1> >
264 template <int D = Dim, typename = std::enable_if_t<D == 1> >
265 double at(int index) const noexcept
265 double at(int index) const noexcept
266 {
266 {
267 QReadLocker locker{&m_Lock};
267 QReadLocker locker{&m_Lock};
268 return m_Data[0].at(index);
268 return m_Data[0].at(index);
269 }
269 }
270
270
271 /**
271 /**
272 * @return the data as a vector, as a const reference
272 * @return the data as a vector, as a const reference
273 * @remarks this method is only available for a unidimensional ArrayData
273 * @remarks this method is only available for a unidimensional ArrayData
274 */
274 */
275 template <int D = Dim, typename = std::enable_if_t<D == 1> >
275 template <int D = Dim, typename = std::enable_if_t<D == 1> >
276 const QVector<double> &cdata() const noexcept
276 const QVector<double> &cdata() const noexcept
277 {
277 {
278 QReadLocker locker{&m_Lock};
278 QReadLocker locker{&m_Lock};
279 return m_Data.at(0);
279 return m_Data.at(0);
280 }
280 }
281
281
282 /**
282 /**
283 * @return the data as a vector
283 * @return the data as a vector
284 * @remarks this method is only available for a unidimensional ArrayData
284 * @remarks this method is only available for a unidimensional ArrayData
285 */
285 */
286 template <int D = Dim, typename = std::enable_if_t<D == 1> >
286 template <int D = Dim, typename = std::enable_if_t<D == 1> >
287 QVector<double> data() const noexcept
287 QVector<double> data() const noexcept
288 {
288 {
289 QReadLocker locker{&m_Lock};
289 QReadLocker locker{&m_Lock};
290 return m_Data[0];
290 return m_Data[0];
291 }
291 }
292
292
293 // ///////////// //
294 // 2-dim methods //
295 // ///////////// //
296
297 /**
298 * @return the data
299 * @remarks this method is only available for a two-dimensional ArrayData
300 */
301 template <int D = Dim, typename = std::enable_if_t<D == 2> >
302 DataContainer data() const noexcept
303 {
304 QReadLocker locker{&m_Lock};
305 return m_Data;
306 }
307
293 private:
308 private:
294 DataContainer m_Data;
309 DataContainer m_Data;
295 mutable QReadWriteLock m_Lock;
310 mutable QReadWriteLock m_Lock;
296 };
311 };
297
312
298 #endif // SCIQLOP_ARRAYDATA_H
313 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,212 +1,310
1 #include "AmdaResultParser.h"
1 #include "AmdaResultParser.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/VectorSeries.h>
4
5
5 #include <QObject>
6 #include <QObject>
6 #include <QtTest>
7 #include <QtTest>
7
8
8 namespace {
9 namespace {
9
10
10 /// Path for the tests
11 /// Path for the tests
11 const auto TESTS_RESOURCES_PATH
12 const auto TESTS_RESOURCES_PATH
12 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13 = QFileInfo{QString{AMDA_TESTS_RESOURCES_DIR}, "TestAmdaResultParser"}.absoluteFilePath();
13
14
15 QDateTime dateTime(int year, int month, int day, int hours, int minutes, int seconds)
16 {
17 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
18 }
19
14 /// Compares two vectors that can potentially contain NaN values
20 /// Compares two vectors that can potentially contain NaN values
15 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
21 bool compareVectors(const QVector<double> &v1, const QVector<double> &v2)
16 {
22 {
17 if (v1.size() != v2.size()) {
23 if (v1.size() != v2.size()) {
18 return false;
24 return false;
19 }
25 }
20
26
21 auto result = true;
27 auto result = true;
22 auto v2It = v2.cbegin();
28 auto v2It = v2.cbegin();
23 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
29 for (auto v1It = v1.cbegin(), v1End = v1.cend(); v1It != v1End && result; ++v1It, ++v2It) {
24 auto v1Value = *v1It;
30 auto v1Value = *v1It;
25 auto v2Value = *v2It;
31 auto v2Value = *v2It;
26
32
27 // If v1 is NaN, v2 has to be NaN too
33 // If v1 is NaN, v2 has to be NaN too
28 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
34 result = std::isnan(v1Value) ? std::isnan(v2Value) : (v1Value == v2Value);
29 }
35 }
30
36
31 return result;
37 return result;
32 }
38 }
33
39
40 bool compareVectors(const QVector<QVector<double> > &v1, const QVector<QVector<double> > &v2)
41 {
42 if (v1.size() != v2.size()) {
43 return false;
44 }
45
46 auto result = true;
47 for (auto i = 0; i < v1.size() && result; ++i) {
48 result &= compareVectors(v1.at(i), v2.at(i));
49 }
50
51 return result;
52 }
53
54 QVector<QVector<double> > valuesData(const ArrayData<1> &arrayData)
55 {
56 return QVector<QVector<double> >{arrayData.data()};
57 }
58
59 QVector<QVector<double> > valuesData(const ArrayData<2> &arrayData)
60 {
61 return arrayData.data();
62 }
63
64
34 QString inputFilePath(const QString &inputFileName)
65 QString inputFilePath(const QString &inputFileName)
35 {
66 {
36 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
67 return QFileInfo{TESTS_RESOURCES_PATH, inputFileName}.absoluteFilePath();
37 }
68 }
38
69
70 template <typename T>
39 struct ExpectedResults {
71 struct ExpectedResults {
40 explicit ExpectedResults() = default;
72 explicit ExpectedResults() = default;
41
73
42 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
43 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
74 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
44 QVector<double> valuesData)
75 QVector<double> valuesData)
76 : ExpectedResults(xAxisUnit, valuesUnit, xAxisData,
77 QVector<QVector<double> >{std::move(valuesData)})
78 {
79 }
80
81 /// Ctor with QVector<QDateTime> as x-axis data. Datetimes are converted to doubles
82 explicit ExpectedResults(Unit xAxisUnit, Unit valuesUnit, const QVector<QDateTime> &xAxisData,
83 QVector<QVector<double> > valuesData)
45 : m_ParsingOK{true},
84 : m_ParsingOK{true},
46 m_XAxisUnit{xAxisUnit},
85 m_XAxisUnit{xAxisUnit},
47 m_ValuesUnit{valuesUnit},
86 m_ValuesUnit{valuesUnit},
48 m_XAxisData{},
87 m_XAxisData{},
49 m_ValuesData{std::move(valuesData)}
88 m_ValuesData{std::move(valuesData)}
50 {
89 {
51 // Converts QVector<QDateTime> to QVector<double>
90 // Converts QVector<QDateTime> to QVector<double>
52 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
91 std::transform(xAxisData.cbegin(), xAxisData.cend(), std::back_inserter(m_XAxisData),
53 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
92 [](const auto &dateTime) { return dateTime.toMSecsSinceEpoch() / 1000.; });
54 }
93 }
55
94
56 /**
95 /**
57 * Validates a DataSeries compared to the expected results
96 * Validates a DataSeries compared to the expected results
58 * @param results the DataSeries to validate
97 * @param results the DataSeries to validate
59 */
98 */
60 void validate(std::shared_ptr<IDataSeries> results)
99 void validate(std::shared_ptr<IDataSeries> results)
61 {
100 {
62 if (m_ParsingOK) {
101 if (m_ParsingOK) {
63 auto scalarSeries = dynamic_cast<ScalarSeries *>(results.get());
102 auto dataSeries = dynamic_cast<T *>(results.get());
64 QVERIFY(scalarSeries != nullptr);
103 QVERIFY(dataSeries != nullptr);
65
104
66 // Checks units
105 // Checks units
67 QVERIFY(scalarSeries->xAxisUnit() == m_XAxisUnit);
106 QVERIFY(dataSeries->xAxisUnit() == m_XAxisUnit);
68 QVERIFY(scalarSeries->valuesUnit() == m_ValuesUnit);
107 QVERIFY(dataSeries->valuesUnit() == m_ValuesUnit);
69
108
70 // Checks values : as the vectors can potentially contain NaN values, we must use a
109 // Checks values : as the vectors can potentially contain NaN values, we must use a
71 // custom vector comparison method
110 // custom vector comparison method
72 QVERIFY(compareVectors(scalarSeries->xAxisData()->data(), m_XAxisData));
111 QVERIFY(compareVectors(dataSeries->xAxisData()->data(), m_XAxisData));
73 QVERIFY(compareVectors(scalarSeries->valuesData()->data(), m_ValuesData));
112 QVERIFY(compareVectors(valuesData(*dataSeries->valuesData()), m_ValuesData));
74 }
113 }
75 else {
114 else {
76 QVERIFY(results == nullptr);
115 QVERIFY(results == nullptr);
77 }
116 }
78 }
117 }
79
118
80 // Parsing was successfully completed
119 // Parsing was successfully completed
81 bool m_ParsingOK{false};
120 bool m_ParsingOK{false};
82 // Expected x-axis unit
121 // Expected x-axis unit
83 Unit m_XAxisUnit{};
122 Unit m_XAxisUnit{};
84 // Expected values unit
123 // Expected values unit
85 Unit m_ValuesUnit{};
124 Unit m_ValuesUnit{};
86 // Expected x-axis data
125 // Expected x-axis data
87 QVector<double> m_XAxisData{};
126 QVector<double> m_XAxisData{};
88 // Expected values data
127 // Expected values data
89 QVector<double> m_ValuesData{};
128 QVector<QVector<double> > m_ValuesData{};
90 };
129 };
91
130
92 } // namespace
131 } // namespace
93
132
94 Q_DECLARE_METATYPE(ExpectedResults)
133 Q_DECLARE_METATYPE(ExpectedResults<ScalarSeries>)
134 Q_DECLARE_METATYPE(ExpectedResults<VectorSeries>)
95
135
96 class TestAmdaResultParser : public QObject {
136 class TestAmdaResultParser : public QObject {
97 Q_OBJECT
137 Q_OBJECT
138 private:
139 template <typename T>
140 void testReadDataStructure()
141 {
142 // ////////////// //
143 // Test structure //
144 // ////////////// //
145
146 // Name of TXT file to read
147 QTest::addColumn<QString>("inputFileName");
148 // Expected results
149 QTest::addColumn<ExpectedResults<T> >("expectedResults");
150 }
151
152 template <typename T>
153 void testRead(AmdaResultParser::ValueType valueType)
154 {
155 QFETCH(QString, inputFileName);
156 QFETCH(ExpectedResults<T>, expectedResults);
157
158 // Parses file
159 auto filePath = inputFilePath(inputFileName);
160 auto results = AmdaResultParser::readTxt(filePath, valueType);
161
162 // ///////////////// //
163 // Validates results //
164 // ///////////////// //
165 expectedResults.validate(results);
166 }
167
98 private slots:
168 private slots:
99 /// Input test data
169 /// Input test data
100 /// @sa testTxtJson()
170 /// @sa testReadScalarTxt()
101 void testReadTxt_data();
171 void testReadScalarTxt_data();
102
172
103 /// Tests parsing of a TXT file
173 /// Tests parsing scalar series of a TXT file
104 void testReadTxt();
174 void testReadScalarTxt();
175
176 /// Input test data
177 /// @sa testReadVectorTxt()
178 void testReadVectorTxt_data();
179
180 /// Tests parsing vector series of a TXT file
181 void testReadVectorTxt();
105 };
182 };
106
183
107 void TestAmdaResultParser::testReadTxt_data()
184 void TestAmdaResultParser::testReadScalarTxt_data()
108 {
185 {
109 // ////////////// //
186 testReadDataStructure<ScalarSeries>();
110 // Test structure //
111 // ////////////// //
112
113 // Name of TXT file to read
114 QTest::addColumn<QString>("inputFileName");
115 // Expected results
116 QTest::addColumn<ExpectedResults>("expectedResults");
117
187
118 // ////////// //
188 // ////////// //
119 // Test cases //
189 // Test cases //
120 // ////////// //
190 // ////////// //
121
191
122 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
123 return QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC};
124 };
125
126 // Valid files
192 // Valid files
127 QTest::newRow("Valid file")
193 QTest::newRow("Valid file")
128 << QStringLiteral("ValidScalar1.txt")
194 << QStringLiteral("ValidScalar1.txt")
129 << ExpectedResults{
195 << ExpectedResults<ScalarSeries>{
130 Unit{QStringLiteral("nT"), true}, Unit{},
196 Unit{QStringLiteral("nT"), true}, Unit{},
131 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
197 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
132 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
198 dateTime(2013, 9, 23, 9, 2, 30), dateTime(2013, 9, 23, 9, 3, 30),
133 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
199 dateTime(2013, 9, 23, 9, 4, 30), dateTime(2013, 9, 23, 9, 5, 30),
134 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
200 dateTime(2013, 9, 23, 9, 6, 30), dateTime(2013, 9, 23, 9, 7, 30),
135 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
201 dateTime(2013, 9, 23, 9, 8, 30), dateTime(2013, 9, 23, 9, 9, 30)},
136 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
202 QVector<double>{-2.83950, -2.71850, -2.52150, -2.57633, -2.58050, -2.48325, -2.63025,
137 -2.55800, -2.43250, -2.42200}};
203 -2.55800, -2.43250, -2.42200}};
138
204
139 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
205 QTest::newRow("Valid file (value of first line is invalid but it is converted to NaN")
140 << QStringLiteral("WrongValue.txt")
206 << QStringLiteral("WrongValue.txt")
141 << ExpectedResults{
207 << ExpectedResults<ScalarSeries>{
142 Unit{QStringLiteral("nT"), true}, Unit{},
208 Unit{QStringLiteral("nT"), true}, Unit{},
143 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
209 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
144 dateTime(2013, 9, 23, 9, 2, 30)},
210 dateTime(2013, 9, 23, 9, 2, 30)},
145 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
211 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
146
212
147 QTest::newRow("Valid file that contains NaN values")
213 QTest::newRow("Valid file that contains NaN values")
148 << QStringLiteral("NaNValue.txt")
214 << QStringLiteral("NaNValue.txt")
149 << ExpectedResults{
215 << ExpectedResults<ScalarSeries>{
150 Unit{QStringLiteral("nT"), true}, Unit{},
216 Unit{QStringLiteral("nT"), true}, Unit{},
151 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
217 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30), dateTime(2013, 9, 23, 9, 1, 30),
152 dateTime(2013, 9, 23, 9, 2, 30)},
218 dateTime(2013, 9, 23, 9, 2, 30)},
153 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
219 QVector<double>{std::numeric_limits<double>::quiet_NaN(), -2.71850, -2.52150}};
154
220
155 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
221 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
156 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
222 QTest::newRow("No unit file") << QStringLiteral("NoUnit.txt")
157 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
223 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true},
158 QVector<QDateTime>{}, QVector<double>{}};
224 Unit{}, QVector<QDateTime>{},
225 QVector<double>{}};
159 QTest::newRow("Wrong unit file")
226 QTest::newRow("Wrong unit file")
160 << QStringLiteral("WrongUnit.txt")
227 << QStringLiteral("WrongUnit.txt")
161 << ExpectedResults{Unit{QStringLiteral(""), true}, Unit{},
228 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral(""), true}, Unit{},
162 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
229 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 0, 30),
163 dateTime(2013, 9, 23, 9, 1, 30),
230 dateTime(2013, 9, 23, 9, 1, 30),
164 dateTime(2013, 9, 23, 9, 2, 30)},
231 dateTime(2013, 9, 23, 9, 2, 30)},
165 QVector<double>{-2.83950, -2.71850, -2.52150}};
232 QVector<double>{-2.83950, -2.71850, -2.52150}};
166
233
167 QTest::newRow("Wrong results file (date of first line is invalid")
234 QTest::newRow("Wrong results file (date of first line is invalid")
168 << QStringLiteral("WrongDate.txt")
235 << QStringLiteral("WrongDate.txt")
169 << ExpectedResults{
236 << ExpectedResults<ScalarSeries>{
170 Unit{QStringLiteral("nT"), true}, Unit{},
237 Unit{QStringLiteral("nT"), true}, Unit{},
171 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
238 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
172 QVector<double>{-2.71850, -2.52150}};
239 QVector<double>{-2.71850, -2.52150}};
173
240
174 QTest::newRow("Wrong results file (too many values for first line")
241 QTest::newRow("Wrong results file (too many values for first line")
175 << QStringLiteral("TooManyValues.txt")
242 << QStringLiteral("TooManyValues.txt")
176 << ExpectedResults{
243 << ExpectedResults<ScalarSeries>{
177 Unit{QStringLiteral("nT"), true}, Unit{},
244 Unit{QStringLiteral("nT"), true}, Unit{},
178 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
245 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
179 QVector<double>{-2.71850, -2.52150}};
246 QVector<double>{-2.71850, -2.52150}};
180
247
181 QTest::newRow("Wrong results file (x of first line is NaN")
248 QTest::newRow("Wrong results file (x of first line is NaN")
182 << QStringLiteral("NaNX.txt")
249 << QStringLiteral("NaNX.txt")
183 << ExpectedResults{
250 << ExpectedResults<ScalarSeries>{
184 Unit{QStringLiteral("nT"), true}, Unit{},
251 Unit{QStringLiteral("nT"), true}, Unit{},
185 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
252 QVector<QDateTime>{dateTime(2013, 9, 23, 9, 1, 30), dateTime(2013, 9, 23, 9, 2, 30)},
186 QVector<double>{-2.71850, -2.52150}};
253 QVector<double>{-2.71850, -2.52150}};
187
254
255 QTest::newRow("Invalid file type (vector)")
256 << QStringLiteral("ValidVector1.txt")
257 << ExpectedResults<ScalarSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
258 QVector<QDateTime>{}, QVector<double>{}};
259
188 // Invalid files
260 // Invalid files
189 QTest::newRow("Invalid file (unexisting file)") << QStringLiteral("UnexistingFile.txt")
261 QTest::newRow("Invalid file (unexisting file)")
190 << ExpectedResults{};
262 << QStringLiteral("UnexistingFile.txt") << ExpectedResults<ScalarSeries>{};
263
264 QTest::newRow("Invalid file (file not found on server)")
265 << QStringLiteral("FileNotFound.txt") << ExpectedResults<ScalarSeries>{};
266 }
191
267
192 QTest::newRow("Invalid file (file not found on server)") << QStringLiteral("FileNotFound.txt")
268 void TestAmdaResultParser::testReadScalarTxt()
193 << ExpectedResults{};
269 {
270 testRead<ScalarSeries>(AmdaResultParser::ValueType::SCALAR);
194 }
271 }
195
272
196 void TestAmdaResultParser::testReadTxt()
273 void TestAmdaResultParser::testReadVectorTxt_data()
197 {
274 {
198 QFETCH(QString, inputFileName);
275 testReadDataStructure<VectorSeries>();
199 QFETCH(ExpectedResults, expectedResults);
276
277 // ////////// //
278 // Test cases //
279 // ////////// //
280
281 // Valid files
282 QTest::newRow("Valid file")
283 << QStringLiteral("ValidVector1.txt")
284 << ExpectedResults<VectorSeries>{
285 Unit{QStringLiteral("nT"), true}, Unit{},
286 QVector<QDateTime>{dateTime(2013, 7, 2, 9, 13, 50), dateTime(2013, 7, 2, 9, 14, 6),
287 dateTime(2013, 7, 2, 9, 14, 22), dateTime(2013, 7, 2, 9, 14, 38),
288 dateTime(2013, 7, 2, 9, 14, 54), dateTime(2013, 7, 2, 9, 15, 10),
289 dateTime(2013, 7, 2, 9, 15, 26), dateTime(2013, 7, 2, 9, 15, 42),
290 dateTime(2013, 7, 2, 9, 15, 58), dateTime(2013, 7, 2, 9, 16, 14)},
291 QVector<QVector<double> >{
292 {-0.332, -1.011, -1.457, -1.293, -1.217, -1.443, -1.278, -1.202, -1.22, -1.259},
293 {3.206, 2.999, 2.785, 2.736, 2.612, 2.564, 2.892, 2.862, 2.859, 2.764},
294 {0.058, 0.496, 1.018, 1.485, 1.662, 1.505, 1.168, 1.244, 1.15, 1.358}}};
200
295
201 // Parses file
296 // Valid files but with some invalid lines (wrong unit, wrong values, etc.)
202 auto filePath = inputFilePath(inputFileName);
297 QTest::newRow("Invalid file type (scalar)")
203 auto results = AmdaResultParser::readTxt(filePath);
298 << QStringLiteral("ValidScalar1.txt")
299 << ExpectedResults<VectorSeries>{Unit{QStringLiteral("nT"), true}, Unit{},
300 QVector<QDateTime>{},
301 QVector<QVector<double> >{{}, {}, {}}};
302 }
204
303
205 // ///////////////// //
304 void TestAmdaResultParser::testReadVectorTxt()
206 // Validates results //
305 {
207 // ///////////////// //
306 testRead<VectorSeries>(AmdaResultParser::ValueType::VECTOR);
208 expectedResults.validate(results);
209 }
307 }
210
308
211 QTEST_MAIN(TestAmdaResultParser)
309 QTEST_MAIN(TestAmdaResultParser)
212 #include "TestAmdaResultParser.moc"
310 #include "TestAmdaResultParser.moc"
General Comments 0
You need to be logged in to leave comments. Login now