##// END OF EJS Templates
Merge branch 'feature/SpectrogramSeries' into develop
Alexandre Leroux -
r867:73c43e70e278 merge
parent child
Show More
@@ -0,0 +1,58
1 #ifndef SCIQLOP_OPTIONALAXIS_H
2 #define SCIQLOP_OPTIONALAXIS_H
3
4 #include "CoreGlobal.h"
5 #include "Unit.h"
6
7 #include <memory>
8
9 template <int Dim>
10 class ArrayData;
11
12 /**
13 * @brief The OptionalAxis class defines an optional data axis for a series of data.
14 *
15 * An optional data axis is an axis that can be defined or not for a data series. If defined, it
16 * contains a unit and data (1-dim ArrayData). It is then possible to access the data or the unit.
17 * In the case of an undefined axis, the axis has no data and no unit. The methods for accessing the
18 * data or the unit are always callable but will return undefined values.
19 *
20 * @sa DataSeries
21 * @sa ArrayData
22 */
23 class SCIQLOP_CORE_EXPORT OptionalAxis {
24 public:
25 /// Ctor for an undefined axis
26 explicit OptionalAxis();
27 /// Ctor for a defined axis
28 /// @param data the axis' data
29 /// @param unit the axis' unit
30 /// @throws std::invalid_argument if no data is associated to the axis
31 explicit OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit);
32
33 /// Copy ctor
34 OptionalAxis(const OptionalAxis &other);
35 /// Assignment operator
36 OptionalAxis &operator=(OptionalAxis other);
37
38 /// @return the flag that indicates if the axis is defined or not
39 bool isDefined() const;
40
41 /// @return gets the data at the index passed in parameter, NaN if the index is outside the
42 /// bounds of the axis, or if the axis is undefined
43 double at(int index) const;
44 /// @return the number of data on the axis, 0 if the axis is not defined
45 int size() const;
46 /// @return the unit of the axis, an empty unit if the axis is not defined
47 Unit unit() const;
48
49 bool operator==(const OptionalAxis &other);
50 bool operator!=(const OptionalAxis &other);
51
52 private:
53 bool m_Defined; ///< Axis is defined or not
54 std::shared_ptr<ArrayData<1> > m_Data; ///< Axis' data
55 Unit m_Unit; ///< Axis' unit
56 };
57
58 #endif // SCIQLOP_OPTIONALAXIS_H
@@ -0,0 +1,33
1 #ifndef SCIQLOP_SPECTROGRAMSERIES_H
2 #define SCIQLOP_SPECTROGRAMSERIES_H
3
4 #include "CoreGlobal.h"
5
6 #include <Data/DataSeries.h>
7
8 /**
9 * @brief The SpectrogramSeries class is the implementation for a data series representing a
10 * spectrogram.
11 *
12 * It defines values on a x-axis and a y-axis.
13 */
14 class SCIQLOP_CORE_EXPORT SpectrogramSeries : public DataSeries<2> {
15 public:
16 /// Ctor
17 explicit SpectrogramSeries(std::vector<double> xAxisData, std::vector<double> yAxisData,
18 std::vector<double> valuesData, const Unit &xAxisUnit,
19 const Unit &yAxisUnit, const Unit &valuesUnit);
20
21 /// Ctor directly with the y-axis
22 explicit SpectrogramSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
23 std::shared_ptr<ArrayData<2> > valuesData, const Unit &valuesUnit,
24 OptionalAxis yAxis);
25
26 /// @sa DataSeries::clone()
27 std::unique_ptr<IDataSeries> clone() const override;
28
29 /// @sa DataSeries::subDataSeries()
30 std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) override;
31 };
32
33 #endif // SCIQLOP_SPECTROGRAMSERIES_H
@@ -0,0 +1,23
1 #ifndef SCIQLOP_UNIT_H
2 #define SCIQLOP_UNIT_H
3
4 #include <QString>
5 #include <tuple>
6
7 struct Unit {
8 explicit Unit(const QString &name = {}, bool timeUnit = false)
9 : m_Name{name}, m_TimeUnit{timeUnit}
10 {
11 }
12
13 inline bool operator==(const Unit &other) const
14 {
15 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
16 }
17 inline bool operator!=(const Unit &other) const { return !(*this == other); }
18
19 QString m_Name; ///< Unit name
20 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
21 };
22
23 #endif // SCIQLOP_UNIT_H
@@ -0,0 +1,74
1 #include <Data/OptionalAxis.h>
2
3 #include "Data/ArrayData.h"
4
5 OptionalAxis::OptionalAxis() : m_Defined{false}, m_Data{nullptr}, m_Unit{}
6 {
7 }
8
9 OptionalAxis::OptionalAxis(std::shared_ptr<ArrayData<1> > data, Unit unit)
10 : m_Defined{true}, m_Data{data}, m_Unit{std::move(unit)}
11 {
12 if (m_Data == nullptr) {
13 throw std::invalid_argument{"Data can't be null for a defined axis"};
14 }
15 }
16
17 OptionalAxis::OptionalAxis(const OptionalAxis &other)
18 : m_Defined{other.m_Defined},
19 m_Data{other.m_Data ? std::make_shared<ArrayData<1> >(*other.m_Data) : nullptr},
20 m_Unit{other.m_Unit}
21 {
22 }
23
24 OptionalAxis &OptionalAxis::operator=(OptionalAxis other)
25 {
26 std::swap(m_Defined, other.m_Defined);
27 std::swap(m_Data, other.m_Data);
28 std::swap(m_Unit, other.m_Unit);
29 }
30
31 bool OptionalAxis::isDefined() const
32 {
33 return m_Defined;
34 }
35
36 double OptionalAxis::at(int index) const
37 {
38 if (m_Defined) {
39 return (index >= 0 && index < m_Data->size()) ? m_Data->at(index)
40 : std::numeric_limits<double>::quiet_NaN();
41 }
42 else {
43 return std::numeric_limits<double>::quiet_NaN();
44 }
45 }
46
47 int OptionalAxis::size() const
48 {
49 return m_Defined ? m_Data->size() : 0;
50 }
51
52 Unit OptionalAxis::unit() const
53 {
54 return m_Defined ? m_Unit : Unit{};
55 }
56
57 bool OptionalAxis::operator==(const OptionalAxis &other)
58 {
59 // Axis not defined
60 if (!m_Defined) {
61 return !other.m_Defined;
62 }
63
64 // Axis defined
65 return m_Unit == other.m_Unit
66 && std::equal(
67 m_Data->cbegin(), m_Data->cend(), other.m_Data->cbegin(), other.m_Data->cend(),
68 [](const auto &it1, const auto &it2) { return it1.values() == it2.values(); });
69 }
70
71 bool OptionalAxis::operator!=(const OptionalAxis &other)
72 {
73 return !(*this == other);
74 }
@@ -0,0 +1,45
1 #include <Data/SpectrogramSeries.h>
2
3 SpectrogramSeries::SpectrogramSeries(std::vector<double> xAxisData, std::vector<double> yAxisData,
4 std::vector<double> valuesData, const Unit &xAxisUnit,
5 const Unit &yAxisUnit, const Unit &valuesUnit)
6 : SpectrogramSeries{
7 std::make_shared<ArrayData<1> >(std::move(xAxisData)), xAxisUnit,
8 std::make_shared<ArrayData<2> >(std::move(valuesData), yAxisData.size()), valuesUnit,
9 OptionalAxis{std::make_shared<ArrayData<1> >(std::move(yAxisData)), yAxisUnit}}
10 {
11 }
12
13 SpectrogramSeries::SpectrogramSeries(std::shared_ptr<ArrayData<1> > xAxisData,
14 const Unit &xAxisUnit,
15 std::shared_ptr<ArrayData<2> > valuesData,
16 const Unit &valuesUnit, OptionalAxis yAxis)
17 : DataSeries{std::move(xAxisData), xAxisUnit, std::move(valuesData), valuesUnit,
18 std::move(yAxis)}
19 {
20 }
21
22 std::unique_ptr<IDataSeries> SpectrogramSeries::clone() const
23 {
24 return std::make_unique<SpectrogramSeries>(*this);
25 }
26
27 std::shared_ptr<IDataSeries> SpectrogramSeries::subDataSeries(const SqpRange &range)
28 {
29 auto subXAxisData = std::vector<double>();
30 auto subValuesData = QVector<double>(); // Uses QVector to append easily values to it
31 this->lockRead();
32 auto bounds = xAxisRange(range.m_TStart, range.m_TEnd);
33 for (auto it = bounds.first; it != bounds.second; ++it) {
34 subXAxisData.push_back(it->x());
35 subValuesData.append(it->values());
36 }
37
38 auto yAxis = this->yAxis();
39 this->unlock();
40
41 return std::make_shared<SpectrogramSeries>(
42 std::make_shared<ArrayData<1> >(std::move(subXAxisData)), this->xAxisUnit(),
43 std::make_shared<ArrayData<2> >(subValuesData.toStdVector(), yAxis.size()),
44 this->valuesUnit(), std::move(yAxis));
45 }
@@ -0,0 +1,90
1 #include "DataSeriesBuilders.h"
2
3 #include <Data/ScalarSeries.h>
4 #include <Data/SpectrogramSeries.h>
5 #include <Data/VectorSeries.h>
6 #include <Data/Unit.h>
7
8 // ///////////// //
9 // ScalarBuilder //
10 // ///////////// //
11
12 ScalarBuilder &ScalarBuilder::setX(std::vector<double> xData)
13 {
14 m_XAxisData = std::move(xData);
15 return *this;
16 }
17
18 ScalarBuilder &ScalarBuilder::setValues(std::vector<double> valuesData)
19 {
20 m_ValuesData =std::move(valuesData);
21 return *this;
22 }
23
24 std::shared_ptr<ScalarSeries> ScalarBuilder::build()
25 {
26 return std::make_shared<ScalarSeries>(std::move(m_XAxisData), std::move(m_ValuesData), Unit{},
27 Unit{});
28 }
29
30 // ////////////////// //
31 // SpectrogramBuilder //
32 // ////////////////// //
33
34 SpectrogramBuilder &SpectrogramBuilder::setX(std::vector<double> xData)
35 {
36 m_XAxisData = std::move(xData);
37 return *this;
38 }
39
40 SpectrogramBuilder &SpectrogramBuilder::setY(std::vector<double> yData)
41 {
42 m_YAxisData =std::move(yData);
43 return *this;
44 }
45
46 SpectrogramBuilder &SpectrogramBuilder::setValues(std::vector<double> valuesData)
47 {
48 m_ValuesData =std::move(valuesData);
49 return *this;
50 }
51
52 std::shared_ptr<SpectrogramSeries> SpectrogramBuilder::build()
53 {
54 return std::make_shared<SpectrogramSeries>(std::move(m_XAxisData), std::move(m_YAxisData), std::move(m_ValuesData), Unit{},
55 Unit{}, Unit{});
56 }
57
58 // ///////////// //
59 // VectorBuilder //
60 // ///////////// //
61
62 VectorBuilder &VectorBuilder::setX(std::vector<double> xData)
63 {
64 m_XAxisData = std::move(xData);
65 return *this;
66 }
67
68 VectorBuilder &VectorBuilder::setXValues(std::vector<double> xValuesData)
69 {
70 m_XValuesData =std::move(xValuesData);
71 return *this;
72 }
73
74 VectorBuilder &VectorBuilder::setYValues(std::vector<double> yValuesData)
75 {
76 m_YValuesData =std::move(yValuesData);
77 return *this;
78 }
79
80 VectorBuilder &VectorBuilder::setZValues(std::vector<double> zValuesData)
81 {
82 m_ZValuesData =std::move(zValuesData);
83 return *this;
84 }
85
86 std::shared_ptr<VectorSeries> VectorBuilder::build()
87 {
88 return std::make_shared<VectorSeries>(std::move(m_XAxisData), std::move(m_XValuesData), std::move(m_YValuesData), std::move(m_ZValuesData), Unit{},
89 Unit{});
90 }
@@ -0,0 +1,74
1 #ifndef SCIQLOP_DATASERIESBUILDERS_H
2 #define SCIQLOP_DATASERIESBUILDERS_H
3
4 #include <memory>
5 #include <vector>
6
7 class ScalarSeries;
8 class SpectrogramSeries;
9 class VectorSeries;
10
11 /**
12 * @brief The ScalarBuilder class aims to facilitate the creation of a ScalarSeries for unit tests
13 * @sa ScalarSeries
14 */
15 class ScalarBuilder {
16 public:
17 /// Sets x-axis data of the series
18 ScalarBuilder & setX(std::vector<double> xData);
19 /// Sets values data of the series
20 ScalarBuilder & setValues(std::vector<double> valuesData);
21 /// Creates the series
22 std::shared_ptr<ScalarSeries> build();
23
24 private:
25 std::vector<double> m_XAxisData{};
26 std::vector<double> m_ValuesData{};
27 };
28
29 /**
30 * @brief The SpectrogramBuilder class aims to facilitate the creation of a SpectrogramSeries for unit tests
31 * @sa SpectrogramSeries
32 */
33 class SpectrogramBuilder {
34 public:
35 /// Sets x-axis data of the series
36 SpectrogramBuilder & setX(std::vector<double> xData);
37 /// Sets y-axis data of the series
38 SpectrogramBuilder & setY(std::vector<double> yData);
39 /// Sets values data of the series
40 SpectrogramBuilder & setValues(std::vector<double> valuesData);
41 /// Creates the series
42 std::shared_ptr<SpectrogramSeries> build();
43
44 private:
45 std::vector<double> m_XAxisData{};
46 std::vector<double> m_YAxisData{};
47 std::vector<double> m_ValuesData{};
48 };
49
50 /**
51 * @brief The VectorBuilder class aims to facilitate the creation of a VectorSeries for unit tests
52 * @sa VectorSeries
53 */
54 class VectorBuilder {
55 public:
56 /// Sets x-axis data of the series
57 VectorBuilder & setX(std::vector<double> xData);
58 /// Sets x-values data of the series
59 VectorBuilder & setXValues(std::vector<double> xValuesData);
60 /// Sets y-values data of the series
61 VectorBuilder & setYValues(std::vector<double> yValuesData);
62 /// Sets z-values data of the series
63 VectorBuilder & setZValues(std::vector<double> zValuesData);
64 /// Creates the series
65 std::shared_ptr<VectorSeries> build();
66
67 private:
68 std::vector<double> m_XAxisData{};
69 std::vector<double> m_XValuesData{};
70 std::vector<double> m_YValuesData{};
71 std::vector<double> m_ZValuesData{};
72 };
73
74 #endif // SCIQLOP_DATASERIESBUILDERS_H
@@ -0,0 +1,24
1 #include "DataSeriesUtils.h"
2
3 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
4 const DataContainer &valuesData)
5 {
6 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
7 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
8 QVERIFY(std::equal(
9 first, last, valuesData.cbegin(), valuesData.cend(),
10 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
11 }
12
13 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData, const std::vector<DataContainer> &valuesData)
14 {
15 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
16 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
17 for (auto i = 0; i < valuesData.size(); ++i) {
18 auto componentData = valuesData.at(i);
19
20 QVERIFY(std::equal(
21 first, last, componentData.cbegin(), componentData.cend(),
22 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
23 }
24 }
@@ -0,0 +1,373
1 /**
2 * The DataSeriesUtils file contains a set of utility methods that can be used to test the operations on a DataSeries.
3 *
4 * Most of these methods are template methods to adapt to any series (scalars, vectors, spectrograms...)
5 *
6 * @sa DataSeries
7 */
8 #ifndef SCIQLOP_DATASERIESUTILS_H
9 #define SCIQLOP_DATASERIESUTILS_H
10
11 #include <Data/DataSeriesIterator.h>
12 #include <Data/ScalarSeries.h>
13 #include <Data/SpectrogramSeries.h>
14 #include <Data/VectorSeries.h>
15
16 #include <memory>
17 #include <QtTest>
18
19 /// Underlying data in ArrayData
20 using DataContainer = std::vector<double>;
21
22 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
23 Q_DECLARE_METATYPE(std::shared_ptr<SpectrogramSeries>)
24 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
25
26 /**
27 * Checks that the range of a 1-dim data series contains the expected x-axis data and values data
28 * @param first the iterator on the beginning of the range to check
29 * @param last the iterator on the end of the range to check
30 * @param xData expected x-axis data for the range
31 * @param valuesData expected values data for the range
32 */
33 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
34 const DataContainer &valuesData);
35
36 /**
37 * Checks that the range of a 2-dim data series contains the expected x-axis data and values data
38 * @param first the iterator on the beginning of the range to check
39 * @param last the iterator on the end of the range to check
40 * @param xData expected x-axis data for the range
41 * @param valuesData expected values data for the range
42 */
43 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const DataContainer &xData,
44 const std::vector<DataContainer> &valuesData);
45
46 /**
47 * Sets the structure of unit tests concerning merge of two data series
48 * @tparam DataSeriesType the type of data series to merge
49 * @tparam ExpectedValuesType the type of values expected after merge
50 * @sa testMerge_t()
51 */
52 template <typename DataSeriesType, typename ExpectedValuesType>
53 void testMerge_struct() {
54 // Data series to merge
55 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries");
56 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries2");
57
58 // Expected values in the first data series after merge
59 QTest::addColumn<DataContainer>("expectedXAxisData");
60 QTest::addColumn<ExpectedValuesType>("expectedValuesData");
61 }
62
63 /**
64 * Unit test concerning merge of two data series
65 * @sa testMerge_struct()
66 */
67 template <typename DataSeriesType, typename ExpectedValuesType>
68 void testMerge_t(){
69 // Merges series
70 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries);
71 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries2);
72
73 dataSeries->merge(dataSeries2.get());
74
75 // Validates results : we check that the merge is valid
76 QFETCH(DataContainer, expectedXAxisData);
77 QFETCH(ExpectedValuesType, expectedValuesData);
78
79 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
80 }
81
82 /**
83 * Sets the structure of unit tests concerning merge of two data series that are of a different type
84 * @tparam SourceType the type of data series with which to make the merge
85 * @tparam DestType the type of data series in which to make the merge
86 * @sa testMergeDifferentTypes_t()
87 */
88 template <typename SourceType, typename DestType>
89 void testMergeDifferentTypes_struct()
90 {
91 // Data series to merge
92 QTest::addColumn<std::shared_ptr<DestType> >("dest");
93 QTest::addColumn<std::shared_ptr<SourceType> >("source");
94
95 // Expected values in the dest data series after merge
96 QTest::addColumn<DataContainer>("expectedXAxisData");
97 QTest::addColumn<DataContainer>("expectedValuesData");
98 }
99
100 /**
101 * Unit test concerning merge of two data series that are of a different type
102 * @sa testMergeDifferentTypes_struct()
103 */
104 template <typename SourceType, typename DestType>
105 void testMergeDifferentTypes_t()
106 {
107 // Merges series
108 QFETCH(std::shared_ptr<SourceType>, source);
109 QFETCH(std::shared_ptr<DestType>, dest);
110
111 dest->merge(source.get());
112
113 // Validates results : we check that the merge is valid and the data series is sorted on its
114 // x-axis data
115 QFETCH(DataContainer, expectedXAxisData);
116 QFETCH(DataContainer, expectedValuesData);
117
118 validateRange(dest->cbegin(), dest->cend(), expectedXAxisData, expectedValuesData);
119 }
120
121 /**
122 * Sets the structure of unit tests concerning getting the min x-axis data of a data series
123 * @tparam T the type of data series on which to make the operation
124 * @sa testMinXAxisData_t()
125 */
126 template <typename T>
127 void testMinXAxisData_struct(){
128 // Data series to get min data
129 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
130
131 // Min data
132 QTest::addColumn<double>("min");
133
134 // Expected results
135 QTest::addColumn<bool>(
136 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
137 QTest::addColumn<double>(
138 "expectedMin"); // Expected value when method doesn't return end iterator
139 }
140
141 /**
142 * Unit test concerning getting the min x-axis data of a data series
143 * @sa testMinXAxisData_struct()
144 */
145 template <typename T>
146 void testMinXAxisData_t()
147 {
148 QFETCH(std::shared_ptr<T>, dataSeries);
149 QFETCH(double, min);
150
151 QFETCH(bool, expectedOK);
152 QFETCH(double, expectedMin);
153
154 auto it = dataSeries->minXAxisData(min);
155
156 QCOMPARE(expectedOK, it != dataSeries->cend());
157
158 // If the method doesn't return a end iterator, checks with expected value
159 if (expectedOK) {
160 QCOMPARE(expectedMin, it->x());
161 }
162 }
163
164 /**
165 * Sets the structure of unit tests concerning getting the max x-axis data of a data series
166 * @tparam T the type of data series on which to make the operation
167 * @sa testMaxXAxisData_t()
168 */
169 template <typename T>
170 void testMaxXAxisData_struct(){
171 // Data series to get max data
172 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
173
174 // Max data
175 QTest::addColumn<double>("max");
176
177 // Expected results
178 QTest::addColumn<bool>(
179 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
180 QTest::addColumn<double>(
181 "expectedMax"); // Expected value when method doesn't return end iterator
182
183 }
184
185 /**
186 * Unit test concerning getting the max x-axis data of a data series
187 * @sa testMaxXAxisData_struct()
188 */
189 template <typename T>
190 void testMaxXAxisData_t()
191 {
192 QFETCH(std::shared_ptr<T>, dataSeries);
193 QFETCH(double, max);
194
195 QFETCH(bool, expectedOK);
196 QFETCH(double, expectedMax);
197
198 auto it = dataSeries->maxXAxisData(max);
199
200 QCOMPARE(expectedOK, it != dataSeries->cend());
201
202 // If the method doesn't return a end iterator, checks with expected value
203 if (expectedOK) {
204 QCOMPARE(expectedMax, it->x());
205 }
206 }
207
208 /**
209 * Sets the structure of unit tests concerning getting the purge of a data series
210 * @tparam T the type of data series on which to make the operation
211 * @sa testMinXAxisData_t()
212 */
213 template <typename T>
214 void testPurge_struct()
215 {
216 // Data series to purge
217 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
218 QTest::addColumn<double>("min");
219 QTest::addColumn<double>("max");
220
221 // Expected values after purge
222 QTest::addColumn<DataContainer>("expectedXAxisData");
223 QTest::addColumn<std::vector<DataContainer> >("expectedValuesData");
224 }
225
226 /**
227 * Unit test concerning getting the purge of a data series
228 * @sa testPurge_struct()
229 */
230 template <typename T>
231 void testPurge_t(){
232 QFETCH(std::shared_ptr<T>, dataSeries);
233 QFETCH(double, min);
234 QFETCH(double, max);
235
236 dataSeries->purge(min, max);
237
238 // Validates results
239 QFETCH(DataContainer, expectedXAxisData);
240 QFETCH(std::vector<DataContainer>, expectedValuesData);
241
242 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
243 expectedValuesData);
244 }
245
246 /**
247 * Sets the structure of unit tests concerning getting subdata of a data series
248 * @tparam DataSeriesType the type of data series on which to make the operation
249 * @tparam ExpectedValuesType the type of values expected after the operation
250 * @sa testSubDataSeries_t()
251 */
252 template <typename DataSeriesType, typename ExpectedValuesType>
253 void testSubDataSeries_struct() {
254 // Data series from which extract the subdata series
255 QTest::addColumn<std::shared_ptr<DataSeriesType> >("dataSeries");
256 // Range to extract
257 QTest::addColumn<SqpRange>("range");
258
259 // Expected values for the subdata series
260 QTest::addColumn<DataContainer>("expectedXAxisData");
261 QTest::addColumn<ExpectedValuesType>("expectedValuesData");
262 }
263
264 /**
265 * Unit test concerning getting subdata of a data series
266 * @sa testSubDataSeries_struct()
267 */
268 template <typename DataSeriesType, typename ExpectedValuesType>
269 void testSubDataSeries_t(){
270 QFETCH(std::shared_ptr<DataSeriesType>, dataSeries);
271 QFETCH(SqpRange, range);
272
273 // Makes the operation
274 auto subDataSeries = std::dynamic_pointer_cast<DataSeriesType>(dataSeries->subDataSeries(range));
275 QVERIFY(subDataSeries != nullptr);
276
277 // Validates results
278 QFETCH(DataContainer, expectedXAxisData);
279 QFETCH(ExpectedValuesType, expectedValuesData);
280
281 validateRange(subDataSeries->cbegin(), subDataSeries->cend(), expectedXAxisData, expectedValuesData);
282 }
283
284 /**
285 * Sets the structure of unit tests concerning getting the range of a data series
286 * @tparam T the type of data series on which to make the operation
287 * @sa testXAxisRange_t()
288 */
289 template <typename T>
290 void testXAxisRange_struct(){
291 // Data series to get x-axis range
292 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
293
294 // Min/max values
295 QTest::addColumn<double>("min");
296 QTest::addColumn<double>("max");
297
298 // Expected values
299 QTest::addColumn<DataContainer>("expectedXAxisData");
300 QTest::addColumn<DataContainer>("expectedValuesData");
301 }
302
303 /**
304 * Unit test concerning getting the range of a data series
305 * @sa testXAxisRange_struct()
306 */
307 template <typename T>
308 void testXAxisRange_t(){
309 QFETCH(std::shared_ptr<T>, dataSeries);
310 QFETCH(double, min);
311 QFETCH(double, max);
312
313 QFETCH(DataContainer, expectedXAxisData);
314 QFETCH(DataContainer, expectedValuesData);
315
316 auto bounds = dataSeries->xAxisRange(min, max);
317 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
318 }
319
320 /**
321 * Sets the structure of unit tests concerning getting values bounds of a data series
322 * @tparam T the type of data series on which to make the operation
323 * @sa testValuesBounds_t()
324 */
325 template <typename T>
326 void testValuesBounds_struct()
327 {
328 // Data series to get values bounds
329 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
330
331 // x-axis range
332 QTest::addColumn<double>("minXAxis");
333 QTest::addColumn<double>("maxXAxis");
334
335 // Expected results
336 QTest::addColumn<bool>(
337 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
338 QTest::addColumn<double>("expectedMinValue");
339 QTest::addColumn<double>("expectedMaxValue");
340 }
341
342 /**
343 * Unit test concerning getting values bounds of a data series
344 * @sa testValuesBounds_struct()
345 */
346 template <typename T>
347 void testValuesBounds_t()
348 {
349 QFETCH(std::shared_ptr<T>, dataSeries);
350 QFETCH(double, minXAxis);
351 QFETCH(double, maxXAxis);
352
353 QFETCH(bool, expectedOK);
354 QFETCH(double, expectedMinValue);
355 QFETCH(double, expectedMaxValue);
356
357 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
358 auto end = dataSeries->cend();
359
360 // Checks iterators with expected result
361 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
362
363 if (expectedOK) {
364 auto compare = [](const auto &v1, const auto &v2) {
365 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
366 };
367
368 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
369 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
370 }
371 }
372
373 #endif // SCIQLOP_DATASERIESUTILS_H
@@ -0,0 +1,151
1 #include <Data/ArrayData.h>
2 #include <Data/OptionalAxis.h>
3
4 #include <QObject>
5 #include <QtTest>
6
7 Q_DECLARE_METATYPE(OptionalAxis)
8 Q_DECLARE_METATYPE(Unit)
9
10 class TestOptionalAxis : public QObject {
11 Q_OBJECT
12
13 private slots:
14 /// Tests the creation of a undefined axis
15 void testNotDefinedAxisCtor();
16
17 /// Tests the creation of a undefined axis
18 void testDefinedAxisCtor_data();
19 void testDefinedAxisCtor();
20
21 /// Tests @sa OptionalAxis::at() method
22 void testAt_data();
23 void testAt();
24
25 /// Tests @sa OptionalAxis::size() method
26 void testSize_data();
27 void testSize();
28
29 /// Tests @sa OptionalAxis::unit() method
30 void testUnit_data();
31 void testUnit();
32 };
33
34 void TestOptionalAxis::testNotDefinedAxisCtor()
35 {
36 OptionalAxis notDefinedAxis{};
37 QVERIFY(!notDefinedAxis.isDefined());
38 }
39
40 void TestOptionalAxis::testDefinedAxisCtor_data()
41 {
42 QTest::addColumn<bool>("noData"); // If set to true, nullptr is passed as data of the axis
43 QTest::addColumn<std::vector<double> >(
44 "data"); // Values assigned to the axis when 'noData' flag is set to false
45 QTest::addColumn<Unit>("unit"); // Unit assigned to the axis
46
47 QTest::newRow("validData") << false << std::vector<double>{1, 2, 3} << Unit{"Hz"};
48 QTest::newRow("invalidData") << true << std::vector<double>{} << Unit{"Hz"};
49 }
50
51 void TestOptionalAxis::testDefinedAxisCtor()
52 {
53 QFETCH(bool, noData);
54 QFETCH(Unit, unit);
55
56 // When there is no data, we expect that the constructor returns exception
57 if (noData) {
58 QVERIFY_EXCEPTION_THROWN(OptionalAxis(nullptr, unit), std::invalid_argument);
59 }
60 else {
61 QFETCH(std::vector<double>, data);
62
63 OptionalAxis axis{std::make_shared<ArrayData<1> >(data), unit};
64 QVERIFY(axis.isDefined());
65 }
66 }
67
68 void TestOptionalAxis::testAt_data()
69 {
70 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
71 QTest::addColumn<int>("index"); // Index to test in the axis
72 QTest::addColumn<double>("expectedValue"); // Expected axis value for the index
73
74 OptionalAxis definedAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}),
75 Unit{"Hz"}};
76
77 QTest::newRow("data1") << definedAxis << 0 << 1.;
78 QTest::newRow("data2") << definedAxis << 1 << 2.;
79 QTest::newRow("data3") << definedAxis << 2 << 3.;
80 QTest::newRow("data4 (index out of bounds)")
81 << definedAxis << 3
82 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
83 QTest::newRow("data5 (index out of bounds)")
84 << definedAxis << -1
85 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for out of bounds index
86 QTest::newRow("data6 (axis not defined)")
87 << OptionalAxis{} << 0
88 << std::numeric_limits<double>::quiet_NaN(); // Expects NaN for undefined axis
89 }
90
91 void TestOptionalAxis::testAt()
92 {
93 QFETCH(OptionalAxis, axis);
94 QFETCH(int, index);
95 QFETCH(double, expectedValue);
96
97 auto value = axis.at(index);
98 QVERIFY((std::isnan(value) && std::isnan(expectedValue)) || value == expectedValue);
99 }
100
101 void TestOptionalAxis::testSize_data()
102 {
103 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
104 QTest::addColumn<int>("expectedSize"); // Expected number of data in the axis
105
106 // Lambda that creates default defined axis (with the values passed in parameter)
107 auto axis = [](std::vector<double> values) {
108 return OptionalAxis{std::make_shared<ArrayData<1> >(std::move(values)), Unit{"Hz"}};
109 };
110
111 QTest::newRow("data1") << axis({}) << 0;
112 QTest::newRow("data2") << axis({1, 2, 3}) << 3;
113 QTest::newRow("data3") << axis({1, 2, 3, 4}) << 4;
114 QTest::newRow("data4 (axis not defined)")
115 << OptionalAxis{} << 0; // Expects 0 for undefined axis
116 }
117
118 void TestOptionalAxis::testSize()
119 {
120 QFETCH(OptionalAxis, axis);
121 QFETCH(int, expectedSize);
122
123 QCOMPARE(axis.size(), expectedSize);
124 }
125
126 void TestOptionalAxis::testUnit_data()
127 {
128 QTest::addColumn<OptionalAxis>("axis"); // Axis used for test case (defined or not)
129 QTest::addColumn<Unit>("expectedUnit"); // Expected unit for the axis
130
131 // Lambda that creates default defined axis (with the unit passed in parameter)
132 auto axis = [](Unit unit) {
133 return OptionalAxis{std::make_shared<ArrayData<1> >(std::vector<double>{1, 2, 3}), unit};
134 };
135
136 QTest::newRow("data1") << axis(Unit{"Hz"}) << Unit{"Hz"};
137 QTest::newRow("data2") << axis(Unit{"t", true}) << Unit{"t", true};
138 QTest::newRow("data3 (axis not defined)")
139 << OptionalAxis{} << Unit{}; // Expects default unit for undefined axis
140 }
141
142 void TestOptionalAxis::testUnit()
143 {
144 QFETCH(OptionalAxis, axis);
145 QFETCH(Unit, expectedUnit);
146
147 QCOMPARE(axis.unit(), expectedUnit);
148 }
149
150 QTEST_MAIN(TestOptionalAxis)
151 #include "TestOptionalAxis.moc"
@@ -0,0 +1,426
1 #include "Data/ScalarSeries.h"
2
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
5
6 #include <QObject>
7 #include <QtTest>
8
9 /**
10 * @brief The TestScalarSeries class defines unit tests on scalar series.
11 *
12 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
13 */
14 class TestScalarSeries : public QObject {
15 Q_OBJECT
16 private slots:
17 /// Tests construction of a scalar series
18 void testCtor_data();
19 void testCtor();
20
21 /// Tests merge of two scalar series
22 void testMerge_data();
23 void testMerge();
24
25 /// Tests merge of a vector series in a scalar series
26 void testMergeWithVector_data();
27 void testMergeWithVector();
28
29 /// Tests get min x-axis data of a scalar series
30 void testMinXAxisData_data();
31 void testMinXAxisData();
32
33 /// Tests get max x-axis data of a scalar series
34 void testMaxXAxisData_data();
35 void testMaxXAxisData();
36
37 /// Tests purge of a scalar series
38 void testPurge_data();
39 void testPurge();
40
41 /// Tests get x-axis range of a scalar series
42 void testXAxisRange_data();
43 void testXAxisRange();
44
45 /// Tests get values bounds of a scalar series
46 void testValuesBounds_data();
47 void testValuesBounds();
48 };
49
50 void TestScalarSeries::testCtor_data()
51 {
52 // x-axis data
53 QTest::addColumn<DataContainer>("xAxisData");
54 // values data
55 QTest::addColumn<DataContainer>("valuesData");
56
57 // construction expected to be valid
58 QTest::addColumn<bool>("expectOK");
59 // expected x-axis data (when construction is valid)
60 QTest::addColumn<DataContainer>("expectedXAxisData");
61 // expected values data (when construction is valid)
62 QTest::addColumn<DataContainer>("expectedValuesData");
63
64 QTest::newRow("invalidData (different sizes of vectors)")
65 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300.} << false
66 << DataContainer{} << DataContainer{};
67
68 QTest::newRow("sortedData") << DataContainer{1., 2., 3., 4., 5.}
69 << DataContainer{100., 200., 300., 400., 500.} << true
70 << DataContainer{1., 2., 3., 4., 5.}
71 << DataContainer{100., 200., 300., 400., 500.};
72
73 QTest::newRow("unsortedData") << DataContainer{5., 4., 3., 2., 1.}
74 << DataContainer{100., 200., 300., 400., 500.} << true
75 << DataContainer{1., 2., 3., 4., 5.}
76 << DataContainer{500., 400., 300., 200., 100.};
77
78 QTest::newRow("unsortedData2")
79 << DataContainer{1., 4., 3., 5., 2.} << DataContainer{100., 200., 300., 400., 500.} << true
80 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 500., 300., 200., 400.};
81 }
82
83 void TestScalarSeries::testCtor()
84 {
85 // Creates series
86 QFETCH(DataContainer, xAxisData);
87 QFETCH(DataContainer, valuesData);
88 QFETCH(bool, expectOK);
89
90 if (expectOK) {
91 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
92 Unit{}, Unit{});
93
94 // Validates results : we check that the data series is sorted on its x-axis data
95 QFETCH(DataContainer, expectedXAxisData);
96 QFETCH(DataContainer, expectedValuesData);
97
98 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
99 }
100 else {
101 QVERIFY_EXCEPTION_THROWN(std::make_shared<ScalarSeries>(
102 std::move(xAxisData), std::move(valuesData), Unit{}, Unit{}),
103 std::invalid_argument);
104 }
105 }
106
107 void TestScalarSeries::testMerge_data()
108 {
109 testMerge_struct<ScalarSeries, DataContainer>();
110
111 QTest::newRow("sortedMerge") << ScalarBuilder{}
112 .setX({1., 2., 3., 4., 5.})
113 .setValues({100., 200., 300., 400., 500.})
114 .build()
115 << ScalarBuilder{}
116 .setX({6., 7., 8., 9., 10.})
117 .setValues({600., 700., 800., 900., 1000.})
118 .build()
119 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
120 << DataContainer{100., 200., 300., 400., 500.,
121 600., 700., 800., 900., 1000.};
122
123 QTest::newRow("unsortedMerge")
124 << ScalarBuilder{}
125 .setX({6., 7., 8., 9., 10.})
126 .setValues({600., 700., 800., 900., 1000.})
127 .build()
128 << ScalarBuilder{}
129 .setX({1., 2., 3., 4., 5.})
130 .setValues({100., 200., 300., 400., 500.})
131 .build()
132 << DataContainer{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
133 << DataContainer{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
134
135 QTest::newRow("unsortedMerge2 (merge not made because source is in the bounds of dest)")
136 << ScalarBuilder{}
137 .setX({1., 2., 8., 9., 10})
138 .setValues({100., 200., 800., 900., 1000.})
139 .build()
140 << ScalarBuilder{}
141 .setX({3., 4., 5., 6., 7.})
142 .setValues({300., 400., 500., 600., 700.})
143 .build()
144 << DataContainer{1., 2., 8., 9., 10.} << DataContainer{100., 200., 800., 900., 1000.};
145
146 QTest::newRow("unsortedMerge3")
147 << ScalarBuilder{}
148 .setX({3., 4., 5., 7., 8})
149 .setValues({300., 400., 500., 700., 800.})
150 .build()
151 << ScalarBuilder{}
152 .setX({1., 2., 3., 7., 10.})
153 .setValues({100., 200., 333., 777., 1000.})
154 .build()
155 << DataContainer{1., 2., 3., 4., 5., 7., 8., 10.}
156 << DataContainer{100., 200., 300., 400., 500., 700., 800., 1000.};
157
158 QTest::newRow("emptySource") << ScalarBuilder{}
159 .setX({3., 4., 5., 7., 8})
160 .setValues({300., 400., 500., 700., 800.})
161 .build()
162 << ScalarBuilder{}.setX({}).setValues({}).build()
163 << DataContainer{3., 4., 5., 7., 8.}
164 << DataContainer{300., 400., 500., 700., 800.};
165 }
166
167 void TestScalarSeries::testMerge()
168 {
169 testMerge_t<ScalarSeries, DataContainer>();
170 }
171
172 void TestScalarSeries::testMergeWithVector_data()
173 {
174 testMergeDifferentTypes_struct<VectorSeries, ScalarSeries>();
175
176 QTest::newRow("mergeVectorInScalar")
177 << ScalarBuilder{}
178 .setX({1., 2., 3., 4., 5.})
179 .setValues({100., 200., 300., 400., 500.})
180 .build()
181 << VectorBuilder{}
182 .setX({6., 7., 8., 9., 10.})
183 .setXValues({600., 700., 800., 900., 1000.})
184 .setYValues({610., 710., 810., 910., 1010.})
185 .setZValues({620., 720., 820., 920., 1020.})
186 .build()
187 << DataContainer{1., 2., 3., 4., 5.} << DataContainer{100., 200., 300., 400., 500.};
188 }
189
190 void TestScalarSeries::testMergeWithVector()
191 {
192 testMergeDifferentTypes_t<VectorSeries, ScalarSeries>();
193 }
194
195 void TestScalarSeries::testMinXAxisData_data()
196 {
197 testMinXAxisData_struct<ScalarSeries>();
198
199 QTest::newRow("minData1") << ScalarBuilder{}
200 .setX({1., 2., 3., 4., 5.})
201 .setValues({100., 200., 300., 400., 500.})
202 .build()
203 << 0. << true << 1.;
204 QTest::newRow("minData2") << ScalarBuilder{}
205 .setX({1., 2., 3., 4., 5.})
206 .setValues({100., 200., 300., 400., 500.})
207 .build()
208 << 1. << true << 1.;
209 QTest::newRow("minData3") << ScalarBuilder{}
210 .setX({1., 2., 3., 4., 5.})
211 .setValues({100., 200., 300., 400., 500.})
212 .build()
213 << 1.1 << true << 2.;
214 QTest::newRow("minData4") << ScalarBuilder{}
215 .setX({1., 2., 3., 4., 5.})
216 .setValues({100., 200., 300., 400., 500.})
217 .build()
218 << 5. << true << 5.;
219 QTest::newRow("minData5") << ScalarBuilder{}
220 .setX({1., 2., 3., 4., 5.})
221 .setValues({100., 200., 300., 400., 500.})
222 .build()
223 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
224 QTest::newRow("minData6") << ScalarBuilder{}.setX({}).setValues({}).build() << 1.1 << false
225 << std::numeric_limits<double>::quiet_NaN();
226 }
227
228 void TestScalarSeries::testMinXAxisData()
229 {
230 testMinXAxisData_t<ScalarSeries>();
231 }
232
233 void TestScalarSeries::testMaxXAxisData_data()
234 {
235 testMaxXAxisData_struct<ScalarSeries>();
236
237 QTest::newRow("maxData1") << ScalarBuilder{}
238 .setX({1., 2., 3., 4., 5.})
239 .setValues({100., 200., 300., 400., 500.})
240 .build()
241 << 6. << true << 5.;
242 QTest::newRow("maxData2") << ScalarBuilder{}
243 .setX({1., 2., 3., 4., 5.})
244 .setValues({100., 200., 300., 400., 500.})
245 .build()
246 << 5. << true << 5.;
247 QTest::newRow("maxData3") << ScalarBuilder{}
248 .setX({1., 2., 3., 4., 5.})
249 .setValues({100., 200., 300., 400., 500.})
250 .build()
251 << 4.9 << true << 4.;
252 QTest::newRow("maxData4") << ScalarBuilder{}
253 .setX({1., 2., 3., 4., 5.})
254 .setValues({100., 200., 300., 400., 500.})
255 .build()
256 << 1.1 << true << 1.;
257 QTest::newRow("maxData5") << ScalarBuilder{}
258 .setX({1., 2., 3., 4., 5.})
259 .setValues({100., 200., 300., 400., 500.})
260 .build()
261 << 1. << true << 1.;
262 QTest::newRow("maxData6") << ScalarBuilder{}.setX({}).setValues({}).build() << 1.1 << false
263 << std::numeric_limits<double>::quiet_NaN();
264 }
265
266 void TestScalarSeries::testMaxXAxisData()
267 {
268 testMaxXAxisData_t<ScalarSeries>();
269 }
270
271 void TestScalarSeries::testPurge_data()
272 {
273 testPurge_struct<ScalarSeries>();
274
275 QTest::newRow("purgeScalar") << ScalarBuilder{}
276 .setX({1., 2., 3., 4., 5.})
277 .setValues({100., 200., 300., 400., 500.})
278 .build()
279 << 2. << 4. << DataContainer{2., 3., 4.}
280 << std::vector<DataContainer>{{200., 300., 400.}};
281 QTest::newRow("purgeScalar1 (min/max swap)")
282 << ScalarBuilder{}
283 .setX({1., 2., 3., 4., 5.})
284 .setValues({100., 200., 300., 400., 500.})
285 .build()
286 << 4. << 2. << DataContainer{2., 3., 4.} << std::vector<DataContainer>{{200., 300., 400.}};
287 QTest::newRow("purgeScalar2") << ScalarBuilder{}
288 .setX({1., 2., 3., 4., 5.})
289 .setValues({100., 200., 300., 400., 500.})
290 .build()
291 << 0. << 2.5 << DataContainer{1., 2.}
292 << std::vector<DataContainer>{{100., 200.}};
293 QTest::newRow("purgeScalar3") << ScalarBuilder{}
294 .setX({1., 2., 3., 4., 5.})
295 .setValues({100., 200., 300., 400., 500.})
296 .build()
297 << 3.5 << 7. << DataContainer{4., 5.}
298 << std::vector<DataContainer>{{400., 500.}};
299 QTest::newRow("purgeScalar4") << ScalarBuilder{}
300 .setX({1., 2., 3., 4., 5.})
301 .setValues({100., 200., 300., 400., 500.})
302 .build()
303 << 0. << 7. << DataContainer{1., 2., 3., 4., 5.}
304 << std::vector<DataContainer>{{100., 200., 300., 400., 500.}};
305 QTest::newRow("purgeScalar5") << ScalarBuilder{}
306 .setX({1., 2., 3., 4., 5.})
307 .setValues({100., 200., 300., 400., 500.})
308 .build()
309 << 5.5 << 7. << DataContainer{} << std::vector<DataContainer>{{}};
310 }
311
312 void TestScalarSeries::testPurge()
313 {
314 testPurge_t<ScalarSeries>();
315 }
316
317 void TestScalarSeries::testXAxisRange_data()
318 {
319 testXAxisRange_struct<ScalarSeries>();
320
321 QTest::newRow("xAxisRange") << ScalarBuilder{}
322 .setX({1., 2., 3., 4., 5.})
323 .setValues({100., 200., 300., 400., 500.})
324 .build()
325 << -1. << 3.2 << DataContainer{1., 2., 3.}
326 << DataContainer{100., 200., 300.};
327 QTest::newRow("xAxisRange1 (min/max swap)")
328 << ScalarBuilder{}
329 .setX({1., 2., 3., 4., 5.})
330 .setValues({100., 200., 300., 400., 500.})
331 .build()
332 << 3.2 << -1. << DataContainer{1., 2., 3.} << DataContainer{100., 200., 300.};
333 QTest::newRow("xAxisRange2") << ScalarBuilder{}
334 .setX({1., 2., 3., 4., 5.})
335 .setValues({100., 200., 300., 400., 500.})
336 .build()
337 << 1. << 4. << DataContainer{1., 2., 3., 4.}
338 << DataContainer{100., 200., 300., 400.};
339 QTest::newRow("xAxisRange3") << ScalarBuilder{}
340 .setX({1., 2., 3., 4., 5.})
341 .setValues({100., 200., 300., 400., 500.})
342 .build()
343 << 1. << 3.9 << DataContainer{1., 2., 3.}
344 << DataContainer{100., 200., 300.};
345 QTest::newRow("xAxisRange4") << ScalarBuilder{}
346 .setX({1., 2., 3., 4., 5.})
347 .setValues({100., 200., 300., 400., 500.})
348 .build()
349 << 0. << 0.9 << DataContainer{} << DataContainer{};
350 QTest::newRow("xAxisRange5") << ScalarBuilder{}
351 .setX({1., 2., 3., 4., 5.})
352 .setValues({100., 200., 300., 400., 500.})
353 .build()
354 << 0. << 1. << DataContainer{1.} << DataContainer{100.};
355 QTest::newRow("xAxisRange6") << ScalarBuilder{}
356 .setX({1., 2., 3., 4., 5.})
357 .setValues({100., 200., 300., 400., 500.})
358 .build()
359 << 2.1 << 6. << DataContainer{3., 4., 5.}
360 << DataContainer{300., 400., 500.};
361 QTest::newRow("xAxisRange7") << ScalarBuilder{}
362 .setX({1., 2., 3., 4., 5.})
363 .setValues({100., 200., 300., 400., 500.})
364 .build()
365 << 6. << 9. << DataContainer{} << DataContainer{};
366 QTest::newRow("xAxisRange8") << ScalarBuilder{}
367 .setX({1., 2., 3., 4., 5.})
368 .setValues({100., 200., 300., 400., 500.})
369 .build()
370 << 5. << 9. << DataContainer{5.} << DataContainer{500.};
371 }
372
373 void TestScalarSeries::testXAxisRange()
374 {
375 testXAxisRange_t<ScalarSeries>();
376 }
377
378 void TestScalarSeries::testValuesBounds_data()
379 {
380 testValuesBounds_struct<ScalarSeries>();
381
382 auto nan = std::numeric_limits<double>::quiet_NaN();
383
384 QTest::newRow("scalarBounds1") << ScalarBuilder{}
385 .setX({1., 2., 3., 4., 5.})
386 .setValues({100., 200., 300., 400., 500.})
387 .build()
388 << 0. << 6. << true << 100. << 500.;
389 QTest::newRow("scalarBounds2") << ScalarBuilder{}
390 .setX({1., 2., 3., 4., 5.})
391 .setValues({100., 200., 300., 400., 500.})
392 .build()
393 << 2. << 4. << true << 200. << 400.;
394 QTest::newRow("scalarBounds3") << ScalarBuilder{}
395 .setX({1., 2., 3., 4., 5.})
396 .setValues({100., 200., 300., 400., 500.})
397 .build()
398 << 0. << 0.5 << false << nan << nan;
399 QTest::newRow("scalarBounds4") << ScalarBuilder{}
400 .setX({1., 2., 3., 4., 5.})
401 .setValues({100., 200., 300., 400., 500.})
402 .build()
403 << 5.1 << 6. << false << nan << nan;
404 QTest::newRow("scalarBounds5")
405 << ScalarBuilder{}.setX({1.}).setValues({100.}).build() << 0. << 2. << true << 100. << 100.;
406 QTest::newRow("scalarBounds6")
407 << ScalarBuilder{}.setX({}).setValues({}).build() << 0. << 2. << false << nan << nan;
408
409 // Tests with NaN values: NaN values are not included in min/max search
410 QTest::newRow("scalarBounds7") << ScalarBuilder{}
411 .setX({1., 2., 3., 4., 5.})
412 .setValues({nan, 200., 300., 400., nan})
413 .build()
414 << 0. << 6. << true << 200. << 400.;
415 QTest::newRow("scalarBounds8")
416 << ScalarBuilder{}.setX({1., 2., 3., 4., 5.}).setValues({nan, nan, nan, nan, nan}).build()
417 << 0. << 6. << true << nan << nan;
418 }
419
420 void TestScalarSeries::testValuesBounds()
421 {
422 testValuesBounds_t<ScalarSeries>();
423 }
424
425 QTEST_MAIN(TestScalarSeries)
426 #include "TestScalarSeries.moc"
@@ -0,0 +1,199
1 #include "Data/SpectrogramSeries.h"
2
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
5
6 #include <QObject>
7 #include <QtTest>
8
9 namespace {
10
11 // Aliases used to facilitate reading of test inputs
12 using X = DataContainer;
13 using Y = DataContainer;
14 using Values = DataContainer;
15 using Components = std::vector<DataContainer>;
16
17 } // namespace
18
19 /**
20 * @brief The TestSpectrogramSeries class defines unit tests on spectrogram series.
21 *
22 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
23 */
24 class TestSpectrogramSeries : public QObject {
25 Q_OBJECT
26 private slots:
27
28 /// Tests construction of a spectrogram series
29 void testCtor_data();
30 void testCtor();
31
32 /// Tests merge of two spectrogram series
33 void testMerge_data();
34 void testMerge();
35
36 /// @todo ALX: test subdataseries
37 /// Tests get subdata of a spectrogram series
38 void testSubDataSeries_data();
39 void testSubDataSeries();
40 };
41
42 void TestSpectrogramSeries::testCtor_data()
43 {
44 // x-axis data
45 QTest::addColumn<X>("xAxisData");
46 // y-axis data
47 QTest::addColumn<Y>("yAxisData");
48 // values data
49 QTest::addColumn<Values>("valuesData");
50
51 // construction expected to be valid
52 QTest::addColumn<bool>("expectOK");
53 // expected x-axis data (when construction is valid)
54 QTest::addColumn<X>("expectedXAxisData");
55 // expected components data (when construction is valid)
56 QTest::addColumn<Components>("expectedComponentsData");
57
58 QTest::newRow(
59 "invalidData (number of values by component aren't equal to the number of x-axis data)")
60 << X{1., 2., 3., 4., 5.} << Y{1., 2., 3.} << Values{1., 2., 3.} << false << X{}
61 << Components{};
62
63 QTest::newRow("invalidData (number of components aren't equal to the number of y-axis data)")
64 << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
65 << Values{1., 2., 3., 4., 5.} // 1 component
66 << false << X{} << Components{};
67
68 QTest::newRow("sortedData") << X{1., 2., 3., 4., 5.} << Y{1., 2.} // 2 y-axis data
69 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} // 2 components
70 << true << X{1., 2., 3., 4., 5.}
71 << Components{{1., 3., 5., 7., 9.}, {2., 4., 6., 8., 10.}};
72
73 QTest::newRow("unsortedData") << X{5., 4., 3., 2., 1.} << Y{1., 2.}
74 << Values{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.} << true
75 << X{1., 2., 3., 4., 5.}
76 << Components{{9., 7., 5., 3., 1.}, {10., 8., 6., 4., 2.}};
77 }
78
79 void TestSpectrogramSeries::testCtor()
80 {
81 // Creates series
82 QFETCH(X, xAxisData);
83 QFETCH(Y, yAxisData);
84 QFETCH(Values, valuesData);
85 QFETCH(bool, expectOK);
86
87 if (expectOK) {
88 auto series = SpectrogramBuilder{}
89 .setX(std::move(xAxisData))
90 .setY(std::move(yAxisData))
91 .setValues(std::move(valuesData))
92 .build();
93
94 // Validates results
95 QFETCH(X, expectedXAxisData);
96 QFETCH(Components, expectedComponentsData);
97 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedComponentsData);
98 }
99 else {
100 QVERIFY_EXCEPTION_THROWN(SpectrogramBuilder{}
101 .setX(std::move(xAxisData))
102 .setY(std::move(yAxisData))
103 .setValues(std::move(valuesData))
104 .build(),
105 std::invalid_argument);
106 }
107 }
108
109 void TestSpectrogramSeries::testMerge_data()
110 {
111 testMerge_struct<SpectrogramSeries, Components>();
112
113 QTest::newRow("sortedMerge") << SpectrogramBuilder{}
114 .setX({1., 2., 3.})
115 .setY({1., 2.})
116 .setValues({10., 11., 20., 21., 30., 31})
117 .build()
118 << SpectrogramBuilder{}
119 .setX({4., 5., 6.})
120 .setY({1., 2.})
121 .setValues({40., 41., 50., 51., 60., 61})
122 .build()
123 << DataContainer{1., 2., 3., 4., 5., 6.}
124 << Components{{10., 20., 30., 40., 50., 60.},
125 {11., 21., 31., 41., 51., 61}};
126
127 QTest::newRow(
128 "unsortedMerge (merge not made because the two data series have different y-axes)")
129 << SpectrogramBuilder{}
130 .setX({4., 5., 6.})
131 .setY({1., 2.})
132 .setValues({40., 41., 50., 51., 60., 61})
133 .build()
134 << SpectrogramBuilder{}
135 .setX({1., 2., 3.})
136 .setY({3., 4.})
137 .setValues({10., 11., 20., 21., 30., 31})
138 .build()
139 << DataContainer{4., 5., 6.} << Components{{40., 50., 60.}, {41., 51., 61}};
140
141 QTest::newRow(
142 "unsortedMerge (unsortedMerge (merge is made because the two data series have the same "
143 "y-axis)")
144 << SpectrogramBuilder{}
145 .setX({4., 5., 6.})
146 .setY({1., 2.})
147 .setValues({40., 41., 50., 51., 60., 61})
148 .build()
149 << SpectrogramBuilder{}
150 .setX({1., 2., 3.})
151 .setY({1., 2.})
152 .setValues({10., 11., 20., 21., 30., 31})
153 .build()
154 << DataContainer{1., 2., 3., 4., 5., 6.}
155 << Components{{10., 20., 30., 40., 50., 60.}, {11., 21., 31., 41., 51., 61}};
156 }
157
158 void TestSpectrogramSeries::testMerge()
159 {
160 testMerge_t<SpectrogramSeries, Components>();
161 }
162
163 void TestSpectrogramSeries::testSubDataSeries_data()
164 {
165 testSubDataSeries_struct<SpectrogramSeries, Components>();
166
167 QTest::newRow("subDataSeries (the range includes all data)")
168 << SpectrogramBuilder{}
169 .setX({1., 2., 3.})
170 .setY({1., 2.})
171 .setValues({10., 11., 20., 21., 30., 31})
172 .build()
173 << SqpRange{0., 5.} << DataContainer{1., 2., 3.}
174 << Components{{10., 20., 30.}, {11., 21., 31.}};
175
176 QTest::newRow("subDataSeries (the range includes no data)")
177 << SpectrogramBuilder{}
178 .setX({1., 2., 3.})
179 .setY({1., 2.})
180 .setValues({10., 11., 20., 21., 30., 31})
181 .build()
182 << SqpRange{4., 5.} << DataContainer{} << Components{{}, {}};
183
184 QTest::newRow("subDataSeries (the range includes some data)")
185 << SpectrogramBuilder{}
186 .setX({1., 2., 3.})
187 .setY({1., 2.})
188 .setValues({10., 11., 20., 21., 30., 31})
189 .build()
190 << SqpRange{1.1, 3} << DataContainer{2., 3.} << Components{{20., 30.}, {21., 31.}};
191 }
192
193 void TestSpectrogramSeries::testSubDataSeries()
194 {
195 testSubDataSeries_t<SpectrogramSeries, Components>();
196 }
197
198 QTEST_MAIN(TestSpectrogramSeries)
199 #include "TestSpectrogramSeries.moc"
@@ -0,0 +1,90
1 #include "Data/VectorSeries.h"
2
3 #include "DataSeriesBuilders.h"
4 #include "DataSeriesUtils.h"
5
6 #include <QObject>
7 #include <QtTest>
8
9 /**
10 * @brief The TestVectorSeries class defines unit tests on vector series.
11 *
12 * Most of these unit tests use generic tests defined for DataSeries (@sa DataSeriesUtils)
13 */
14 class TestVectorSeries : public QObject {
15 Q_OBJECT
16 private slots:
17 /// Tests purge of a vector series
18 void testPurge_data();
19 void testPurge();
20
21 /// Tests get values bounds of a vector series
22 void testValuesBounds_data();
23 void testValuesBounds();
24 };
25
26 void TestVectorSeries::testPurge_data()
27 {
28 testPurge_struct<VectorSeries>();
29
30 QTest::newRow("purgeVector") << VectorBuilder{}
31 .setX({1., 2., 3., 4., 5.})
32 .setXValues({6., 7., 8., 9., 10.})
33 .setYValues({11., 12., 13., 14., 15.})
34 .setZValues({16., 17., 18., 19., 20.})
35 .build()
36 << 2. << 4. << DataContainer{2., 3., 4.}
37 << std::vector<DataContainer>{
38 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
39 }
40
41 void TestVectorSeries::testPurge()
42 {
43 testPurge_t<VectorSeries>();
44 }
45
46 void TestVectorSeries::testValuesBounds_data()
47 {
48 testValuesBounds_struct<VectorSeries>();
49
50 auto nan = std::numeric_limits<double>::quiet_NaN();
51
52 QTest::newRow("vectorBounds1") << VectorBuilder{}
53 .setX({1., 2., 3., 4., 5.})
54 .setXValues({10., 15., 20., 13., 12.})
55 .setYValues({35., 24., 10., 9., 0.3})
56 .setZValues({13., 14., 12., 9., 24.})
57 .build()
58 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
59 QTest::newRow("vectorBounds2") << VectorBuilder{}
60 .setX({1., 2., 3., 4., 5.})
61 .setXValues({2.3, 15., 20., 13., 12.})
62 .setYValues({35., 24., 10., 9., 4.})
63 .setZValues({13., 14., 12., 9., 24.})
64 .build()
65 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
66 QTest::newRow("vectorBounds3") << VectorBuilder{}
67 .setX({1., 2., 3., 4., 5.})
68 .setXValues({2.3, 15., 20., 13., 12.})
69 .setYValues({35., 24., 10., 9., 4.})
70 .setZValues({13., 14., 12., 9., 24.})
71 .build()
72 << 2. << 3. << true << 10. << 24.;
73
74 // Tests with NaN values: NaN values are not included in min/max search
75 QTest::newRow("vectorBounds4") << VectorBuilder{}
76 .setX({1., 2.})
77 .setXValues({nan, nan})
78 .setYValues({nan, nan})
79 .setZValues({nan, nan})
80 .build()
81 << 0. << 6. << true << nan << nan;
82 }
83
84 void TestVectorSeries::testValuesBounds()
85 {
86 testValuesBounds_t<VectorSeries>();
87 }
88
89 QTEST_MAIN(TestVectorSeries)
90 #include "TestVectorSeries.moc"
@@ -94,7 +94,6 public:
94 }
94 }
95
95
96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
97 /// @todo ALX : validate
98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
97 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
98 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
100 }
99 }
@@ -8,6 +8,7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11
12
12 #include <QLoggingCategory>
13 #include <QLoggingCategory>
13 #include <QReadLocker>
14 #include <QReadLocker>
@@ -120,9 +121,62 private:
120 /**
121 /**
121 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 *
123 *
123 * It proposes to set a dimension for the values ​​data.
124 * The DataSeries represents values on one or two axes, according to these rules:
125 * - the x-axis is always defined
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
127 * below)
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
129 * distributed into components (for example, a vector defines three components)
130 * - New values can be added to the series, on the x-axis.
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
132 * - Data representing values and axes are associated with a unit
133 * - The data series is always sorted in ascending order on the x-axis.
124 *
134 *
125 * A DataSeries is always sorted on its x-axis data.
135 * Consistency checks are carried out between the axes and the values. These controls are provided
136 * throughout the DataSeries lifecycle:
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
140 * number of data on the y-axis.
141 *
142 * Examples:
143 * 1)
144 * - x-axis: [1 ; 2 ; 3]
145 * - y-axis: not defined
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
147 * => the DataSeries is valid, as x-axis and values have the same number of data
148 *
149 * 2)
150 * - x-axis: [1 ; 2 ; 3]
151 * - y-axis: not defined
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
154 *
155 * 3)
156 * - x-axis: [1 ; 2 ; 3]
157 * - y-axis: not defined
158 * - values: [10 ; 20 ; 30
159 * 40 ; 50 ; 60] (2-dim ArrayData)
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
161 * data each
162 *
163 * 4)
164 * - x-axis: [1 ; 2 ; 3]
165 * - y-axis: [1 ; 2]
166 * - values: [10 ; 20 ; 30
167 * 40 ; 50 ; 60] (2-dim ArrayData)
168 * => the DataSeries is valid, as:
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
170 * - y-axis has 2 data and values contains 2 components
171 *
172 * 5)
173 * - x-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
175 * - values: [10 ; 20 ; 30
176 * 40 ; 50 ; 60] (2-dim ArrayData)
177 * => the DataSeries is invalid, as:
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
179 * - y-axis has 3 data and values contains only 2 components
126 *
180 *
127 * @tparam Dim The dimension of the values data
181 * @tparam Dim The dimension of the values data
128 *
182 *
@@ -146,7 +200,7 public:
146 /// @sa IDataSeries::valuesUnit()
200 /// @sa IDataSeries::valuesUnit()
147 Unit valuesUnit() const override { return m_ValuesUnit; }
201 Unit valuesUnit() const override { return m_ValuesUnit; }
148
202
149 int nbPoints() const override { return m_XAxisData->totalSize() + m_ValuesData->totalSize(); }
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
150
204
151 void clear()
205 void clear()
152 {
206 {
@@ -156,7 +210,14 public:
156
210
157 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
158
212
159 /// Merges into the data series an other data series
213 /// Merges into the data series an other data series.
214 ///
215 /// The two dataseries:
216 /// - must be of the same dimension
217 /// - must have the same y-axis (if defined)
218 ///
219 /// If the prerequisites are not valid, the method does nothing
220 ///
160 /// @remarks the data series to merge with is cleared after the operation
221 /// @remarks the data series to merge with is cleared after the operation
161 void merge(IDataSeries *dataSeries) override
222 void merge(IDataSeries *dataSeries) override
162 {
223 {
@@ -164,7 +225,13 public:
164 lockWrite();
225 lockWrite();
165
226
166 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
167 DataSeriesMergeHelper::merge(*other, *this);
228 if (m_YAxis == other->m_YAxis) {
229 DataSeriesMergeHelper::merge(*other, *this);
230 }
231 else {
232 qCWarning(LOG_DataSeries())
233 << QObject::tr("Can't merge data series that have not the same y-axis");
234 }
168 }
235 }
169 else {
236 else {
170 qCWarning(LOG_DataSeries())
237 qCWarning(LOG_DataSeries())
@@ -325,18 +392,31 public:
325 virtual void unlock() { m_Lock.unlock(); }
392 virtual void unlock() { m_Lock.unlock(); }
326
393
327 protected:
394 protected:
328 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
395 /// Protected ctor (DataSeries is abstract).
329 /// DataSeries with no values will be created.
396 ///
397 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
398 /// class description for consistent rules)
330 /// @remarks data series is automatically sorted on its x-axis data
399 /// @remarks data series is automatically sorted on its x-axis data
400 /// @throws std::invalid_argument if the data are inconsistent with each other
331 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
401 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
332 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
402 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
403 OptionalAxis yAxis = OptionalAxis{})
333 : m_XAxisData{xAxisData},
404 : m_XAxisData{xAxisData},
334 m_XAxisUnit{xAxisUnit},
405 m_XAxisUnit{xAxisUnit},
335 m_ValuesData{valuesData},
406 m_ValuesData{valuesData},
336 m_ValuesUnit{valuesUnit}
407 m_ValuesUnit{valuesUnit},
408 m_YAxis{std::move(yAxis)}
337 {
409 {
338 if (m_XAxisData->size() != m_ValuesData->size()) {
410 if (m_XAxisData->size() != m_ValuesData->size()) {
339 clear();
411 throw std::invalid_argument{
412 "The number of values by component must be equal to the number of x-axis data"};
413 }
414
415 // Validates y-axis (if defined)
416 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
417 throw std::invalid_argument{
418 "As the y-axis is defined, the number of value components must be equal to the "
419 "number of y-axis data"};
340 }
420 }
341
421
342 // Sorts data if it's not the case
422 // Sorts data if it's not the case
@@ -351,12 +431,16 protected:
351 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
431 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
352 m_XAxisUnit{other.m_XAxisUnit},
432 m_XAxisUnit{other.m_XAxisUnit},
353 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
433 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
354 m_ValuesUnit{other.m_ValuesUnit}
434 m_ValuesUnit{other.m_ValuesUnit},
435 m_YAxis{other.m_YAxis}
355 {
436 {
356 // Since a series is ordered from its construction and is always ordered, it is not
437 // Since a series is ordered from its construction and is always ordered, it is not
357 // necessary to call the sort method here ('other' is sorted)
438 // necessary to call the sort method here ('other' is sorted)
358 }
439 }
359
440
441 /// @return the y-axis associated to the data series
442 OptionalAxis yAxis() const { return m_YAxis; }
443
360 /// Assignment operator
444 /// Assignment operator
361 template <int D>
445 template <int D>
362 DataSeries &operator=(DataSeries<D> other)
446 DataSeries &operator=(DataSeries<D> other)
@@ -365,6 +449,7 protected:
365 std::swap(m_XAxisUnit, other.m_XAxisUnit);
449 std::swap(m_XAxisUnit, other.m_XAxisUnit);
366 std::swap(m_ValuesData, other.m_ValuesData);
450 std::swap(m_ValuesData, other.m_ValuesData);
367 std::swap(m_ValuesUnit, other.m_ValuesUnit);
451 std::swap(m_ValuesUnit, other.m_ValuesUnit);
452 std::swap(m_YAxis, other.m_YAxis);
368
453
369 return *this;
454 return *this;
370 }
455 }
@@ -380,11 +465,17 private:
380 m_ValuesData = m_ValuesData->sort(permutation);
465 m_ValuesData = m_ValuesData->sort(permutation);
381 }
466 }
382
467
468 // x-axis
383 std::shared_ptr<ArrayData<1> > m_XAxisData;
469 std::shared_ptr<ArrayData<1> > m_XAxisData;
384 Unit m_XAxisUnit;
470 Unit m_XAxisUnit;
471
472 // values
385 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
473 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
386 Unit m_ValuesUnit;
474 Unit m_ValuesUnit;
387
475
476 // y-axis (optional)
477 OptionalAxis m_YAxis;
478
388 QReadWriteLock m_Lock;
479 QReadWriteLock m_Lock;
389 };
480 };
390
481
@@ -4,6 +4,7
4 #include <Common/MetaTypes.h>
4 #include <Common/MetaTypes.h>
5 #include <Data/DataSeriesIterator.h>
5 #include <Data/DataSeriesIterator.h>
6 #include <Data/SqpRange.h>
6 #include <Data/SqpRange.h>
7 #include <Data/Unit.h>
7
8
8 #include <memory>
9 #include <memory>
9
10
@@ -12,22 +13,6
12 template <int Dim>
13 template <int Dim>
13 class ArrayData;
14 class ArrayData;
14
15
15 struct Unit {
16 explicit Unit(const QString &name = {}, bool timeUnit = false)
17 : m_Name{name}, m_TimeUnit{timeUnit}
18 {
19 }
20
21 inline bool operator==(const Unit &other) const
22 {
23 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
24 }
25 inline bool operator!=(const Unit &other) const { return !(*this == other); }
26
27 QString m_Name; ///< Unit name
28 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
29 };
30
31 /**
16 /**
32 * @brief The IDataSeries aims to declare a data series.
17 * @brief The IDataSeries aims to declare a data series.
33 *
18 *
@@ -23,9 +23,11 core_sources = [
23 'src/Common/StringUtils.cpp',
23 'src/Common/StringUtils.cpp',
24 'src/Common/MimeTypesDef.cpp',
24 'src/Common/MimeTypesDef.cpp',
25 'src/Data/ScalarSeries.cpp',
25 'src/Data/ScalarSeries.cpp',
26 'src/Data/SpectrogramSeries.cpp',
26 'src/Data/DataSeriesIterator.cpp',
27 'src/Data/DataSeriesIterator.cpp',
27 'src/Data/ArrayDataIterator.cpp',
28 'src/Data/ArrayDataIterator.cpp',
28 'src/Data/VectorSeries.cpp',
29 'src/Data/VectorSeries.cpp',
30 'src/Data/OptionalAxis.cpp',
29 'src/DataSource/DataSourceController.cpp',
31 'src/DataSource/DataSourceController.cpp',
30 'src/DataSource/DataSourceItem.cpp',
32 'src/DataSource/DataSourceItem.cpp',
31 'src/DataSource/DataSourceItemAction.cpp',
33 'src/DataSource/DataSourceItemAction.cpp',
@@ -296,21 +296,21 void TestVariable::testNbPoints_data()
296 // ////////// //
296 // ////////// //
297 NbPointsOperations operations{};
297 NbPointsOperations operations{};
298
298
299 // Sets cache range (expected nb points = series xAxis data + series values data)
299 // Sets cache range (expected nb points = values data)
300 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 9)};
300 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 9)};
301 operations.push_back({cacheRange, dataSeries(cacheRange), 20});
301 operations.push_back({cacheRange, dataSeries(cacheRange), 10});
302
302
303 // Doubles cache but don't add data series (expected nb points don't change)
303 // Doubles cache but don't add data series (expected nb points don't change)
304 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
304 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
305 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 20});
305 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 10});
306
306
307 // Doubles cache and data series (expected nb points change)
307 // Doubles cache and data series (expected nb points change)
308 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
308 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 12, 0, 19)};
309 operations.push_back({cacheRange, dataSeries(cacheRange), 40});
309 operations.push_back({cacheRange, dataSeries(cacheRange), 20});
310
310
311 // Decreases cache (expected nb points decreases as the series is purged)
311 // Decreases cache (expected nb points decreases as the series is purged)
312 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 5), date(2017, 1, 1, 12, 0, 9)};
312 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 5), date(2017, 1, 1, 12, 0, 9)};
313 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 10});
313 operations.push_back({cacheRange, dataSeries(INVALID_RANGE), 5});
314
314
315 QTest::newRow("nbPoints1") << operations;
315 QTest::newRow("nbPoints1") << operations;
316 }
316 }
@@ -2,8 +2,11
2
2
3 tests = [
3 tests = [
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
4 [['Common/TestStringUtils.cpp'],'test_string_utils','StringUtils test'],
5 [['Data/TestDataSeries.cpp'],'test_data','DataSeries test'],
5 [['Data/TestScalarSeries.cpp'],'test_scalar','ScalarSeries test'],
6 [['Data/TestSpectrogramSeries.cpp'],'test_spectrogram','SpectrogramSeries test'],
7 [['Data/TestVectorSeries.cpp'],'test_vector','VectorSeries test'],
6 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
8 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
9 [['Data/TestOptionalAxis.cpp'],'test_optional_axis','OptionalAxis test'],
7 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
10 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
8 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
11 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
9 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
12 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (707 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now