##// END OF EJS Templates
Merge branch 'feature/SpectrogramSeries' into develop
Alexandre Leroux -
r873: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"
@@ -1,374 +1,373
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include "Data/ArrayDataIterator.h"
4 #include "Data/ArrayDataIterator.h"
5 #include <Common/SortUtils.h>
5 #include <Common/SortUtils.h>
6
6
7 #include <QReadLocker>
7 #include <QReadLocker>
8 #include <QReadWriteLock>
8 #include <QReadWriteLock>
9 #include <QVector>
9 #include <QVector>
10
10
11 #include <memory>
11 #include <memory>
12
12
13 template <int Dim>
13 template <int Dim>
14 class ArrayData;
14 class ArrayData;
15
15
16 using DataContainer = std::vector<double>;
16 using DataContainer = std::vector<double>;
17
17
18 namespace arraydata_detail {
18 namespace arraydata_detail {
19
19
20 /// Struct used to sort ArrayData
20 /// Struct used to sort ArrayData
21 template <int Dim>
21 template <int Dim>
22 struct Sort {
22 struct Sort {
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
24 const std::vector<int> &sortPermutation)
24 const std::vector<int> &sortPermutation)
25 {
25 {
26 return std::make_shared<ArrayData<Dim> >(
26 return std::make_shared<ArrayData<Dim> >(
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
28 }
28 }
29 };
29 };
30
30
31 /// Specialization for uni-dimensional ArrayData
31 /// Specialization for uni-dimensional ArrayData
32 template <>
32 template <>
33 struct Sort<1> {
33 struct Sort<1> {
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
35 const std::vector<int> &sortPermutation)
35 const std::vector<int> &sortPermutation)
36 {
36 {
37 Q_UNUSED(nbComponents)
37 Q_UNUSED(nbComponents)
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
39 }
39 }
40 };
40 };
41
41
42 template <int Dim, bool IsConst>
42 template <int Dim, bool IsConst>
43 class IteratorValue;
43 class IteratorValue;
44
44
45 template <int Dim, bool IsConst>
45 template <int Dim, bool IsConst>
46 struct IteratorValueBuilder {
46 struct IteratorValueBuilder {
47 };
47 };
48
48
49 template <int Dim>
49 template <int Dim>
50 struct IteratorValueBuilder<Dim, true> {
50 struct IteratorValueBuilder<Dim, true> {
51 using DataContainerIterator = DataContainer::const_iterator;
51 using DataContainerIterator = DataContainer::const_iterator;
52
52
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
54 };
54 };
55
55
56 template <int Dim>
56 template <int Dim>
57 struct IteratorValueBuilder<Dim, false> {
57 struct IteratorValueBuilder<Dim, false> {
58 using DataContainerIterator = DataContainer::iterator;
58 using DataContainerIterator = DataContainer::iterator;
59
59
60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
61 {
61 {
62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
63 std::iter_swap(o1.m_It + i, o2.m_It + i);
63 std::iter_swap(o1.m_It + i, o2.m_It + i);
64 }
64 }
65 }
65 }
66 };
66 };
67
67
68 template <int Dim, bool IsConst>
68 template <int Dim, bool IsConst>
69 class IteratorValue : public ArrayDataIteratorValue::Impl {
69 class IteratorValue : public ArrayDataIteratorValue::Impl {
70 public:
70 public:
71 friend class ArrayData<Dim>;
71 friend class ArrayData<Dim>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
73
73
74 using DataContainerIterator =
74 using DataContainerIterator =
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
76
76
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
80 {
80 {
81 }
81 }
82
82
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
86 {
86 {
87 }
87 }
88
88
89 IteratorValue(const IteratorValue &other) = default;
89 IteratorValue(const IteratorValue &other) = default;
90
90
91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
92 {
92 {
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
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 }
101 catch (const std::bad_cast &) {
100 catch (const std::bad_cast &) {
102 return 0;
101 return 0;
103 }
102 }
104
103
105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
104 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
105 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
106 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
108 }
107 }
109 catch (const std::bad_cast &) {
108 catch (const std::bad_cast &) {
110 return false;
109 return false;
111 }
110 }
112
111
113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
112 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
113 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
115 return m_It < otherImpl.m_It;
114 return m_It < otherImpl.m_It;
116 }
115 }
117 catch (const std::bad_cast &) {
116 catch (const std::bad_cast &) {
118 return false;
117 return false;
119 }
118 }
120
119
121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
120 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
122 {
121 {
123 auto result = clone();
122 auto result = clone();
124 result->next(offset);
123 result->next(offset);
125 return result;
124 return result;
126 }
125 }
127
126
128 void next(int offset) override { std::advance(m_It, offset * m_NbComponents); }
127 void next(int offset) override { std::advance(m_It, offset * m_NbComponents); }
129 void prev() override { std::advance(m_It, -m_NbComponents); }
128 void prev() override { std::advance(m_It, -m_NbComponents); }
130
129
131 double at(int componentIndex) const override { return *(m_It + componentIndex); }
130 double at(int componentIndex) const override { return *(m_It + componentIndex); }
132 double first() const override { return *m_It; }
131 double first() const override { return *m_It; }
133 double min() const override
132 double min() const override
134 {
133 {
135 auto values = this->values();
134 auto values = this->values();
136 auto end = values.cend();
135 auto end = values.cend();
137 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
136 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
138 return SortUtils::minCompareWithNaN(v1, v2);
137 return SortUtils::minCompareWithNaN(v1, v2);
139 });
138 });
140
139
141 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
140 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
142 }
141 }
143 double max() const override
142 double max() const override
144 {
143 {
145 auto values = this->values();
144 auto values = this->values();
146 auto end = values.cend();
145 auto end = values.cend();
147 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
146 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
148 return SortUtils::maxCompareWithNaN(v1, v2);
147 return SortUtils::maxCompareWithNaN(v1, v2);
149 });
148 });
150 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
149 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
151 }
150 }
152
151
153 QVector<double> values() const override
152 QVector<double> values() const override
154 {
153 {
155 auto result = QVector<double>{};
154 auto result = QVector<double>{};
156 for (auto i = 0; i < m_NbComponents; ++i) {
155 for (auto i = 0; i < m_NbComponents; ++i) {
157 result.push_back(*(m_It + i));
156 result.push_back(*(m_It + i));
158 }
157 }
159
158
160 return result;
159 return result;
161 }
160 }
162
161
163 void swap(ArrayDataIteratorValue::Impl &other) override
162 void swap(ArrayDataIteratorValue::Impl &other) override
164 {
163 {
165 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
164 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
166 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
165 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
167 }
166 }
168
167
169 private:
168 private:
170 DataContainerIterator m_It;
169 DataContainerIterator m_It;
171 int m_NbComponents;
170 int m_NbComponents;
172 };
171 };
173
172
174 } // namespace arraydata_detail
173 } // namespace arraydata_detail
175
174
176 /**
175 /**
177 * @brief The ArrayData class represents a dataset for a data series.
176 * @brief The ArrayData class represents a dataset for a data series.
178 *
177 *
179 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
178 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
180 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
179 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
181 * number of values
180 * number of values
182 *
181 *
183 * @tparam Dim the dimension of the ArrayData (one or two)
182 * @tparam Dim the dimension of the ArrayData (one or two)
184 * @sa IDataSeries
183 * @sa IDataSeries
185 */
184 */
186 template <int Dim>
185 template <int Dim>
187 class ArrayData {
186 class ArrayData {
188 public:
187 public:
189 // ///// //
188 // ///// //
190 // Ctors //
189 // Ctors //
191 // ///// //
190 // ///// //
192
191
193 /**
192 /**
194 * Ctor for a unidimensional ArrayData
193 * Ctor for a unidimensional ArrayData
195 * @param data the data the ArrayData will hold
194 * @param data the data the ArrayData will hold
196 */
195 */
197 template <int D = Dim, typename = std::enable_if_t<D == 1> >
196 template <int D = Dim, typename = std::enable_if_t<D == 1> >
198 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
197 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
199 {
198 {
200 }
199 }
201
200
202 /**
201 /**
203 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
202 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
204 * greater than 2 and must be a divisor of the total number of data in the vector
203 * greater than 2 and must be a divisor of the total number of data in the vector
205 * @param data the data the ArrayData will hold
204 * @param data the data the ArrayData will hold
206 * @param nbComponents the number of components
205 * @param nbComponents the number of components
207 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
206 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
208 * of the size of the data
207 * of the size of the data
209 */
208 */
210 template <int D = Dim, typename = std::enable_if_t<D == 2> >
209 template <int D = Dim, typename = std::enable_if_t<D == 2> >
211 explicit ArrayData(DataContainer data, int nbComponents)
210 explicit ArrayData(DataContainer data, int nbComponents)
212 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
211 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
213 {
212 {
214 if (nbComponents < 2) {
213 if (nbComponents < 2) {
215 throw std::invalid_argument{
214 throw std::invalid_argument{
216 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
215 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
217 .arg(nbComponents)
216 .arg(nbComponents)
218 .toStdString()};
217 .toStdString()};
219 }
218 }
220
219
221 if (m_Data.size() % m_NbComponents != 0) {
220 if (m_Data.size() % m_NbComponents != 0) {
222 throw std::invalid_argument{QString{
221 throw std::invalid_argument{QString{
223 "The number of components (%1) is inconsistent with the total number of data (%2)"}
222 "The number of components (%1) is inconsistent with the total number of data (%2)"}
224 .arg(m_Data.size(), nbComponents)
223 .arg(m_Data.size(), nbComponents)
225 .toStdString()};
224 .toStdString()};
226 }
225 }
227 }
226 }
228
227
229 /// Copy ctor
228 /// Copy ctor
230 explicit ArrayData(const ArrayData &other)
229 explicit ArrayData(const ArrayData &other)
231 {
230 {
232 QReadLocker otherLocker{&other.m_Lock};
231 QReadLocker otherLocker{&other.m_Lock};
233 m_Data = other.m_Data;
232 m_Data = other.m_Data;
234 m_NbComponents = other.m_NbComponents;
233 m_NbComponents = other.m_NbComponents;
235 }
234 }
236
235
237 // /////////////// //
236 // /////////////// //
238 // General methods //
237 // General methods //
239 // /////////////// //
238 // /////////////// //
240
239
241 /**
240 /**
242 * Merges into the array data an other array data. The two array datas must have the same number
241 * Merges into the array data an other array data. The two array datas must have the same number
243 * of components so the merge can be done
242 * of components so the merge can be done
244 * @param other the array data to merge with
243 * @param other the array data to merge with
245 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
244 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
246 * inserted at the end
245 * inserted at the end
247 */
246 */
248 void add(const ArrayData<Dim> &other, bool prepend = false)
247 void add(const ArrayData<Dim> &other, bool prepend = false)
249 {
248 {
250 QWriteLocker locker{&m_Lock};
249 QWriteLocker locker{&m_Lock};
251 QReadLocker otherLocker{&other.m_Lock};
250 QReadLocker otherLocker{&other.m_Lock};
252
251
253 if (m_NbComponents != other.componentCount()) {
252 if (m_NbComponents != other.componentCount()) {
254 return;
253 return;
255 }
254 }
256
255
257 insert(other.cbegin(), other.cend(), prepend);
256 insert(other.cbegin(), other.cend(), prepend);
258 }
257 }
259
258
260 void clear()
259 void clear()
261 {
260 {
262 QWriteLocker locker{&m_Lock};
261 QWriteLocker locker{&m_Lock};
263 m_Data.clear();
262 m_Data.clear();
264 }
263 }
265
264
266 int componentCount() const noexcept { return m_NbComponents; }
265 int componentCount() const noexcept { return m_NbComponents; }
267
266
268 /// @return the size (i.e. number of values) of a single component
267 /// @return the size (i.e. number of values) of a single component
269 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
268 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
270 int size() const
269 int size() const
271 {
270 {
272 QReadLocker locker{&m_Lock};
271 QReadLocker locker{&m_Lock};
273 return m_Data.size() / m_NbComponents;
272 return m_Data.size() / m_NbComponents;
274 }
273 }
275
274
276 /// @return the total size (i.e. number of values) of the array data
275 /// @return the total size (i.e. number of values) of the array data
277 int totalSize() const
276 int totalSize() const
278 {
277 {
279 QReadLocker locker{&m_Lock};
278 QReadLocker locker{&m_Lock};
280 return m_Data.size();
279 return m_Data.size();
281 }
280 }
282
281
283 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
282 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
284 {
283 {
285 QReadLocker locker{&m_Lock};
284 QReadLocker locker{&m_Lock};
286 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
285 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
287 }
286 }
288
287
289 // ///////// //
288 // ///////// //
290 // Iterators //
289 // Iterators //
291 // ///////// //
290 // ///////// //
292
291
293 ArrayDataIterator begin()
292 ArrayDataIterator begin()
294 {
293 {
295 return ArrayDataIterator{
294 return ArrayDataIterator{
296 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
295 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
297 m_Data, m_NbComponents, true)}};
296 m_Data, m_NbComponents, true)}};
298 }
297 }
299
298
300 ArrayDataIterator end()
299 ArrayDataIterator end()
301 {
300 {
302 return ArrayDataIterator{
301 return ArrayDataIterator{
303 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
302 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
304 m_Data, m_NbComponents, false)}};
303 m_Data, m_NbComponents, false)}};
305 }
304 }
306
305
307 ArrayDataIterator cbegin() const
306 ArrayDataIterator cbegin() const
308 {
307 {
309 return ArrayDataIterator{
308 return ArrayDataIterator{
310 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
309 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
311 m_Data, m_NbComponents, true)}};
310 m_Data, m_NbComponents, true)}};
312 }
311 }
313
312
314 ArrayDataIterator cend() const
313 ArrayDataIterator cend() const
315 {
314 {
316 return ArrayDataIterator{
315 return ArrayDataIterator{
317 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
316 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
318 m_Data, m_NbComponents, false)}};
317 m_Data, m_NbComponents, false)}};
319 }
318 }
320
319
321 void erase(ArrayDataIterator first, ArrayDataIterator last)
320 void erase(ArrayDataIterator first, ArrayDataIterator last)
322 {
321 {
323 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
322 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
324 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
323 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
325
324
326 if (firstImpl && lastImpl) {
325 if (firstImpl && lastImpl) {
327 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
326 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
328 }
327 }
329 }
328 }
330
329
331 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
330 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
332 {
331 {
333 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
332 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
334 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
333 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
335
334
336 if (firstImpl && lastImpl) {
335 if (firstImpl && lastImpl) {
337 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
336 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
338
337
339 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
338 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
340 }
339 }
341 }
340 }
342
341
343 /**
342 /**
344 * @return the data at a specified index
343 * @return the data at a specified index
345 * @remarks index must be a valid position
344 * @remarks index must be a valid position
346 */
345 */
347 double at(int index) const noexcept
346 double at(int index) const noexcept
348 {
347 {
349 QReadLocker locker{&m_Lock};
348 QReadLocker locker{&m_Lock};
350 return m_Data.at(index);
349 return m_Data.at(index);
351 }
350 }
352
351
353 // ///////////// //
352 // ///////////// //
354 // 1-dim methods //
353 // 1-dim methods //
355 // ///////////// //
354 // ///////////// //
356
355
357 /**
356 /**
358 * @return the data as a vector, as a const reference
357 * @return the data as a vector, as a const reference
359 * @remarks this method is only available for a unidimensional ArrayData
358 * @remarks this method is only available for a unidimensional ArrayData
360 */
359 */
361 template <int D = Dim, typename = std::enable_if_t<D == 1> >
360 template <int D = Dim, typename = std::enable_if_t<D == 1> >
362 DataContainer cdata() const noexcept
361 DataContainer cdata() const noexcept
363 {
362 {
364 return m_Data;
363 return m_Data;
365 }
364 }
366
365
367 private:
366 private:
368 DataContainer m_Data;
367 DataContainer m_Data;
369 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
368 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
370 int m_NbComponents;
369 int m_NbComponents;
371 mutable QReadWriteLock m_Lock;
370 mutable QReadWriteLock m_Lock;
372 };
371 };
373
372
374 #endif // SCIQLOP_ARRAYDATA_H
373 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,391 +1,482
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
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>
14 #include <QReadWriteLock>
15 #include <QReadWriteLock>
15 #include <memory>
16 #include <memory>
16
17
17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
19 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
20 {
21 {
21 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
22 return category;
23 return category;
23 }
24 }
24
25
25 template <int Dim>
26 template <int Dim>
26 class DataSeries;
27 class DataSeries;
27
28
28 namespace dataseries_detail {
29 namespace dataseries_detail {
29
30
30 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 public:
33 public:
33 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
34
35
35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 {
40 {
40 }
41 }
41
42
42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 : dataSeries.valuesData()->cend())
47 : dataSeries.valuesData()->cend())
47 {
48 {
48 }
49 }
49
50
50 IteratorValue(const IteratorValue &other) = default;
51 IteratorValue(const IteratorValue &other) = default;
51
52
52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 {
54 {
54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 }
56 }
56
57
57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 return m_XIt->distance(*otherImpl.m_XIt);
60 return m_XIt->distance(*otherImpl.m_XIt);
60 }
61 }
61 catch (const std::bad_cast &) {
62 catch (const std::bad_cast &) {
62 return 0;
63 return 0;
63 }
64 }
64
65
65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 }
69 }
69 catch (const std::bad_cast &) {
70 catch (const std::bad_cast &) {
70 return false;
71 return false;
71 }
72 }
72
73
73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 }
77 }
77 catch (const std::bad_cast &) {
78 catch (const std::bad_cast &) {
78 return false;
79 return false;
79 }
80 }
80
81
81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 {
83 {
83 auto result = clone();
84 auto result = clone();
84 result->next(offset);
85 result->next(offset);
85 return result;
86 return result;
86 }
87 }
87
88
88 void next(int offset) override
89 void next(int offset) override
89 {
90 {
90 m_XIt->next(offset);
91 m_XIt->next(offset);
91 m_ValuesIt->next(offset);
92 m_ValuesIt->next(offset);
92 }
93 }
93
94
94 void prev() override
95 void prev() override
95 {
96 {
96 --m_XIt;
97 --m_XIt;
97 --m_ValuesIt;
98 --m_ValuesIt;
98 }
99 }
99
100
100 double x() const override { return m_XIt->at(0); }
101 double x() const override { return m_XIt->at(0); }
101 double value() const override { return m_ValuesIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
102 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 double minValue() const override { return m_ValuesIt->min(); }
104 double minValue() const override { return m_ValuesIt->min(); }
104 double maxValue() const override { return m_ValuesIt->max(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
105 QVector<double> values() const override { return m_ValuesIt->values(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
106
107
107 void swap(DataSeriesIteratorValue::Impl &other) override
108 void swap(DataSeriesIteratorValue::Impl &other) override
108 {
109 {
109 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 }
113 }
113
114
114 private:
115 private:
115 ArrayDataIterator m_XIt;
116 ArrayDataIterator m_XIt;
116 ArrayDataIterator m_ValuesIt;
117 ArrayDataIterator m_ValuesIt;
117 };
118 };
118 } // namespace dataseries_detail
119 } // namespace dataseries_detail
119
120
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 *
129 */
183 */
130 template <int Dim>
184 template <int Dim>
131 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
132 friend class DataSeriesMergeHelper;
186 friend class DataSeriesMergeHelper;
133
187
134 public:
188 public:
135 /// @sa IDataSeries::xAxisData()
189 /// @sa IDataSeries::xAxisData()
136 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
137 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
138
192
139 /// @sa IDataSeries::xAxisUnit()
193 /// @sa IDataSeries::xAxisUnit()
140 Unit xAxisUnit() const override { return m_XAxisUnit; }
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
141
195
142 /// @return the values dataset
196 /// @return the values dataset
143 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
144 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
145
199
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 {
153 m_XAxisData->clear();
207 m_XAxisData->clear();
154 m_ValuesData->clear();
208 m_ValuesData->clear();
155 }
209 }
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 {
163 dataSeries->lockWrite();
224 dataSeries->lockWrite();
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())
171 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
172 }
239 }
173 unlock();
240 unlock();
174 dataSeries->unlock();
241 dataSeries->unlock();
175 }
242 }
176
243
177 void purge(double min, double max) override
244 void purge(double min, double max) override
178 {
245 {
179 // Nothing to purge if series is empty
246 // Nothing to purge if series is empty
180 if (isEmpty()) {
247 if (isEmpty()) {
181 return;
248 return;
182 }
249 }
183
250
184 if (min > max) {
251 if (min > max) {
185 std::swap(min, max);
252 std::swap(min, max);
186 }
253 }
187
254
188 // Nothing to purge if series min/max are inside purge range
255 // Nothing to purge if series min/max are inside purge range
189 auto xMin = cbegin()->x();
256 auto xMin = cbegin()->x();
190 auto xMax = (--cend())->x();
257 auto xMax = (--cend())->x();
191 if (xMin >= min && xMax <= max) {
258 if (xMin >= min && xMax <= max) {
192 return;
259 return;
193 }
260 }
194
261
195 auto lowerIt = std::lower_bound(
262 auto lowerIt = std::lower_bound(
196 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
197 erase(begin(), lowerIt);
264 erase(begin(), lowerIt);
198 auto upperIt = std::upper_bound(
265 auto upperIt = std::upper_bound(
199 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
200 erase(upperIt, end());
267 erase(upperIt, end());
201 }
268 }
202
269
203 // ///////// //
270 // ///////// //
204 // Iterators //
271 // Iterators //
205 // ///////// //
272 // ///////// //
206
273
207 DataSeriesIterator begin() override
274 DataSeriesIterator begin() override
208 {
275 {
209 return DataSeriesIterator{DataSeriesIteratorValue{
276 return DataSeriesIterator{DataSeriesIteratorValue{
210 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
211 }
278 }
212
279
213 DataSeriesIterator end() override
280 DataSeriesIterator end() override
214 {
281 {
215 return DataSeriesIterator{DataSeriesIteratorValue{
282 return DataSeriesIterator{DataSeriesIteratorValue{
216 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
217 }
284 }
218
285
219 DataSeriesIterator cbegin() const override
286 DataSeriesIterator cbegin() const override
220 {
287 {
221 return DataSeriesIterator{DataSeriesIteratorValue{
288 return DataSeriesIterator{DataSeriesIteratorValue{
222 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
223 }
290 }
224
291
225 DataSeriesIterator cend() const override
292 DataSeriesIterator cend() const override
226 {
293 {
227 return DataSeriesIterator{DataSeriesIteratorValue{
294 return DataSeriesIterator{DataSeriesIteratorValue{
228 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
229 }
296 }
230
297
231 void erase(DataSeriesIterator first, DataSeriesIterator last)
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
232 {
299 {
233 auto firstImpl
300 auto firstImpl
234 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
235 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
236
303
237 if (firstImpl && lastImpl) {
304 if (firstImpl && lastImpl) {
238 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
239 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
240 }
307 }
241 }
308 }
242
309
243 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
244 {
311 {
245 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
246 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
247
314
248 if (firstImpl && lastImpl) {
315 if (firstImpl && lastImpl) {
249 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
250 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
251 }
318 }
252 }
319 }
253
320
254 /// @sa IDataSeries::minXAxisData()
321 /// @sa IDataSeries::minXAxisData()
255 DataSeriesIterator minXAxisData(double minXAxisData) const override
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
256 {
323 {
257 return std::lower_bound(
324 return std::lower_bound(
258 cbegin(), cend(), minXAxisData,
325 cbegin(), cend(), minXAxisData,
259 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
260 }
327 }
261
328
262 /// @sa IDataSeries::maxXAxisData()
329 /// @sa IDataSeries::maxXAxisData()
263 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
264 {
331 {
265 // Gets the first element that greater than max value
332 // Gets the first element that greater than max value
266 auto it = std::upper_bound(
333 auto it = std::upper_bound(
267 cbegin(), cend(), maxXAxisData,
334 cbegin(), cend(), maxXAxisData,
268 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
269
336
270 return it == cbegin() ? cend() : --it;
337 return it == cbegin() ? cend() : --it;
271 }
338 }
272
339
273 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
274 double maxXAxisData) const override
341 double maxXAxisData) const override
275 {
342 {
276 if (minXAxisData > maxXAxisData) {
343 if (minXAxisData > maxXAxisData) {
277 std::swap(minXAxisData, maxXAxisData);
344 std::swap(minXAxisData, maxXAxisData);
278 }
345 }
279
346
280 auto begin = cbegin();
347 auto begin = cbegin();
281 auto end = cend();
348 auto end = cend();
282
349
283 auto lowerIt = std::lower_bound(
350 auto lowerIt = std::lower_bound(
284 begin, end, minXAxisData,
351 begin, end, minXAxisData,
285 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
286 auto upperIt = std::upper_bound(
353 auto upperIt = std::upper_bound(
287 lowerIt, end, maxXAxisData,
354 lowerIt, end, maxXAxisData,
288 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
289
356
290 return std::make_pair(lowerIt, upperIt);
357 return std::make_pair(lowerIt, upperIt);
291 }
358 }
292
359
293 std::pair<DataSeriesIterator, DataSeriesIterator>
360 std::pair<DataSeriesIterator, DataSeriesIterator>
294 valuesBounds(double minXAxisData, double maxXAxisData) const override
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
295 {
362 {
296 // Places iterators to the correct x-axis range
363 // Places iterators to the correct x-axis range
297 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
298
365
299 // Returns end iterators if the range is empty
366 // Returns end iterators if the range is empty
300 if (xAxisRangeIts.first == xAxisRangeIts.second) {
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
301 return std::make_pair(cend(), cend());
368 return std::make_pair(cend(), cend());
302 }
369 }
303
370
304 // Gets the iterator on the min of all values data
371 // Gets the iterator on the min of all values data
305 auto minIt = std::min_element(
372 auto minIt = std::min_element(
306 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
307 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
308 });
375 });
309
376
310 // Gets the iterator on the max of all values data
377 // Gets the iterator on the max of all values data
311 auto maxIt = std::max_element(
378 auto maxIt = std::max_element(
312 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
313 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
314 });
381 });
315
382
316 return std::make_pair(minIt, maxIt);
383 return std::make_pair(minIt, maxIt);
317 }
384 }
318
385
319 // /////// //
386 // /////// //
320 // Mutexes //
387 // Mutexes //
321 // /////// //
388 // /////// //
322
389
323 virtual void lockRead() { m_Lock.lockForRead(); }
390 virtual void lockRead() { m_Lock.lockForRead(); }
324 virtual void lockWrite() { m_Lock.lockForWrite(); }
391 virtual void lockWrite() { m_Lock.lockForWrite(); }
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
343 const auto &xAxisCData = m_XAxisData->cdata();
423 const auto &xAxisCData = m_XAxisData->cdata();
344 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
424 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
345 sort();
425 sort();
346 }
426 }
347 }
427 }
348
428
349 /// Copy ctor
429 /// Copy ctor
350 explicit DataSeries(const DataSeries<Dim> &other)
430 explicit DataSeries(const DataSeries<Dim> &other)
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)
363 {
447 {
364 std::swap(m_XAxisData, other.m_XAxisData);
448 std::swap(m_XAxisData, other.m_XAxisData);
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 }
371
456
372 private:
457 private:
373 /**
458 /**
374 * Sorts data series on its x-axis data
459 * Sorts data series on its x-axis data
375 */
460 */
376 void sort() noexcept
461 void sort() noexcept
377 {
462 {
378 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
463 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
379 m_XAxisData = m_XAxisData->sort(permutation);
464 m_XAxisData = m_XAxisData->sort(permutation);
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
391 #endif // SCIQLOP_DATASERIES_H
482 #endif // SCIQLOP_DATASERIES_H
@@ -1,111 +1,96
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
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
10 #include <QString>
11 #include <QString>
11
12
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 *
34 * A data series is an entity that contains at least :
19 * A data series is an entity that contains at least :
35 * - one dataset representing the x-axis
20 * - one dataset representing the x-axis
36 * - one dataset representing the values
21 * - one dataset representing the values
37 *
22 *
38 * Each dataset is represented by an ArrayData, and is associated with a unit.
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
39 *
24 *
40 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
41 * IDataSeries. The x-axis dataset is always unidimensional.
26 * IDataSeries. The x-axis dataset is always unidimensional.
42 *
27 *
43 * @sa ArrayData
28 * @sa ArrayData
44 */
29 */
45 class IDataSeries {
30 class IDataSeries {
46 public:
31 public:
47 virtual ~IDataSeries() noexcept = default;
32 virtual ~IDataSeries() noexcept = default;
48
33
49 /// Returns the x-axis dataset
34 /// Returns the x-axis dataset
50 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
51
36
52 /// Returns the x-axis dataset (as const)
37 /// Returns the x-axis dataset (as const)
53 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
54
39
55 virtual Unit xAxisUnit() const = 0;
40 virtual Unit xAxisUnit() const = 0;
56
41
57 virtual Unit valuesUnit() const = 0;
42 virtual Unit valuesUnit() const = 0;
58
43
59 virtual void merge(IDataSeries *dataSeries) = 0;
44 virtual void merge(IDataSeries *dataSeries) = 0;
60 /// Removes from data series all entries whose value on the x-axis is not between min and max
45 /// Removes from data series all entries whose value on the x-axis is not between min and max
61 virtual void purge(double min, double max) = 0;
46 virtual void purge(double min, double max) = 0;
62
47
63 /// @todo Review the name and signature of this method
48 /// @todo Review the name and signature of this method
64 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
49 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
65
50
66 virtual std::unique_ptr<IDataSeries> clone() const = 0;
51 virtual std::unique_ptr<IDataSeries> clone() const = 0;
67
52
68 /// @return the total number of points contained in the data series
53 /// @return the total number of points contained in the data series
69 virtual int nbPoints() const = 0;
54 virtual int nbPoints() const = 0;
70
55
71 // ///////// //
56 // ///////// //
72 // Iterators //
57 // Iterators //
73 // ///////// //
58 // ///////// //
74
59
75 virtual DataSeriesIterator cbegin() const = 0;
60 virtual DataSeriesIterator cbegin() const = 0;
76 virtual DataSeriesIterator cend() const = 0;
61 virtual DataSeriesIterator cend() const = 0;
77 virtual DataSeriesIterator begin() = 0;
62 virtual DataSeriesIterator begin() = 0;
78 virtual DataSeriesIterator end() = 0;
63 virtual DataSeriesIterator end() = 0;
79
64
80 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
65 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
81 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
66 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
82 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
67 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
83
68
84 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
69 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
85 /// equal to the value passed in parameter, or the end iterator if there is no matching value
70 /// equal to the value passed in parameter, or the end iterator if there is no matching value
86 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
71 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
87
72
88 /// @return the iterators pointing to the range of data whose x-axis values are between min and
73 /// @return the iterators pointing to the range of data whose x-axis values are between min and
89 /// max passed in parameters
74 /// max passed in parameters
90 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
75 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
91 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
76 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
92
77
93 /// @return two iterators pointing to the data that have respectively the min and the max value
78 /// @return two iterators pointing to the data that have respectively the min and the max value
94 /// data of a data series' range. The search is performed for a given x-axis range.
79 /// data of a data series' range. The search is performed for a given x-axis range.
95 /// @sa xAxisRange()
80 /// @sa xAxisRange()
96 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
97 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
82 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
98
83
99 // /////// //
84 // /////// //
100 // Mutexes //
85 // Mutexes //
101 // /////// //
86 // /////// //
102
87
103 virtual void lockRead() = 0;
88 virtual void lockRead() = 0;
104 virtual void lockWrite() = 0;
89 virtual void lockWrite() = 0;
105 virtual void unlock() = 0;
90 virtual void unlock() = 0;
106 };
91 };
107
92
108 // Required for using shared_ptr in signals/slots
93 // Required for using shared_ptr in signals/slots
109 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
94 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
110
95
111 #endif // SCIQLOP_IDATASERIES_H
96 #endif // SCIQLOP_IDATASERIES_H
@@ -1,63 +1,65
1
1
2 core_moc_headers = [
2 core_moc_headers = [
3 'include/Data/IDataProvider.h',
3 'include/Data/IDataProvider.h',
4 'include/DataSource/DataSourceController.h',
4 'include/DataSource/DataSourceController.h',
5 'include/DataSource/DataSourceItemAction.h',
5 'include/DataSource/DataSourceItemAction.h',
6 'include/Network/NetworkController.h',
6 'include/Network/NetworkController.h',
7 'include/Time/TimeController.h',
7 'include/Time/TimeController.h',
8 'include/Variable/Variable.h',
8 'include/Variable/Variable.h',
9 'include/Variable/VariableCacheController.h',
9 'include/Variable/VariableCacheController.h',
10 'include/Variable/VariableController.h',
10 'include/Variable/VariableController.h',
11 'include/Variable/VariableAcquisitionWorker.h',
11 'include/Variable/VariableAcquisitionWorker.h',
12 'include/Variable/VariableCacheStrategy.h',
12 'include/Variable/VariableCacheStrategy.h',
13 'include/Variable/VariableSynchronizationGroup.h',
13 'include/Variable/VariableSynchronizationGroup.h',
14 'include/Variable/VariableModel.h',
14 'include/Variable/VariableModel.h',
15 'include/Visualization/VisualizationController.h'
15 'include/Visualization/VisualizationController.h'
16 ]
16 ]
17
17
18
18
19 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
19 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
20
20
21 core_sources = [
21 core_sources = [
22 'src/Common/DateUtils.cpp',
22 'src/Common/DateUtils.cpp',
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',
32 'src/Network/NetworkController.cpp',
34 'src/Network/NetworkController.cpp',
33 'src/Plugin/PluginManager.cpp',
35 'src/Plugin/PluginManager.cpp',
34 'src/Settings/SqpSettingsDefs.cpp',
36 'src/Settings/SqpSettingsDefs.cpp',
35 'src/Time/TimeController.cpp',
37 'src/Time/TimeController.cpp',
36 'src/Variable/Variable.cpp',
38 'src/Variable/Variable.cpp',
37 'src/Variable/VariableCacheController.cpp',
39 'src/Variable/VariableCacheController.cpp',
38 'src/Variable/VariableController.cpp',
40 'src/Variable/VariableController.cpp',
39 'src/Variable/VariableAcquisitionWorker.cpp',
41 'src/Variable/VariableAcquisitionWorker.cpp',
40 'src/Variable/VariableSynchronizationGroup.cpp',
42 'src/Variable/VariableSynchronizationGroup.cpp',
41 'src/Variable/VariableModel.cpp',
43 'src/Variable/VariableModel.cpp',
42 'src/Visualization/VisualizationController.cpp'
44 'src/Visualization/VisualizationController.cpp'
43 ]
45 ]
44
46
45 core_inc = include_directories(['include', '../plugin/include'])
47 core_inc = include_directories(['include', '../plugin/include'])
46
48
47 sciqlop_core_lib = library('sciqlopcore',
49 sciqlop_core_lib = library('sciqlopcore',
48 core_sources,
50 core_sources,
49 core_moc_files,
51 core_moc_files,
50 cpp_args : '-DCORE_LIB',
52 cpp_args : '-DCORE_LIB',
51 include_directories : core_inc,
53 include_directories : core_inc,
52 dependencies : [qt5core, qt5network],
54 dependencies : [qt5core, qt5network],
53 install : true
55 install : true
54 )
56 )
55
57
56
58
57 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
59 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
58 include_directories : core_inc,
60 include_directories : core_inc,
59 dependencies : [qt5core, qt5network])
61 dependencies : [qt5core, qt5network])
60
62
61
63
62 subdir('tests')
64 subdir('tests')
63
65
@@ -1,409 +1,409
1 #include <Variable/Variable.h>
1 #include <Variable/Variable.h>
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4
4
5 #include <QObject>
5 #include <QObject>
6 #include <QtTest>
6 #include <QtTest>
7
7
8 #include <memory>
8 #include <memory>
9
9
10 namespace {
10 namespace {
11
11
12 /// Generates a date in double
12 /// Generates a date in double
13 auto date = [](int year, int month, int day, int hours, int minutes, int seconds) {
13 auto date = [](int year, int month, int day, int hours, int minutes, int seconds) {
14 return DateUtils::secondsSinceEpoch(
14 return DateUtils::secondsSinceEpoch(
15 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
15 QDateTime{{year, month, day}, {hours, minutes, seconds}, Qt::UTC});
16 };
16 };
17
17
18 /// Generates a series of test data for a range
18 /// Generates a series of test data for a range
19 std::shared_ptr<ScalarSeries> dataSeries(const SqpRange &range)
19 std::shared_ptr<ScalarSeries> dataSeries(const SqpRange &range)
20 {
20 {
21 auto xAxisData = std::vector<double>{};
21 auto xAxisData = std::vector<double>{};
22 auto valuesData = std::vector<double>{};
22 auto valuesData = std::vector<double>{};
23
23
24 auto value = 0;
24 auto value = 0;
25 for (auto x = range.m_TStart; x <= range.m_TEnd; ++x, ++value) {
25 for (auto x = range.m_TStart; x <= range.m_TEnd; ++x, ++value) {
26 xAxisData.push_back(x);
26 xAxisData.push_back(x);
27 valuesData.push_back(value);
27 valuesData.push_back(value);
28 }
28 }
29
29
30 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
30 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
31 Unit{});
31 Unit{});
32 }
32 }
33
33
34 } // namespace
34 } // namespace
35
35
36 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
36 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
37
37
38 class TestVariable : public QObject {
38 class TestVariable : public QObject {
39 Q_OBJECT
39 Q_OBJECT
40
40
41 private slots:
41 private slots:
42 void testClone_data();
42 void testClone_data();
43 void testClone();
43 void testClone();
44
44
45 void testNotInCacheRangeList();
45 void testNotInCacheRangeList();
46 void testInCacheRangeList();
46 void testInCacheRangeList();
47
47
48 void testNbPoints_data();
48 void testNbPoints_data();
49 void testNbPoints();
49 void testNbPoints();
50
50
51 void testRealRange_data();
51 void testRealRange_data();
52 void testRealRange();
52 void testRealRange();
53 };
53 };
54
54
55 void TestVariable::testClone_data()
55 void TestVariable::testClone_data()
56 {
56 {
57 // ////////////// //
57 // ////////////// //
58 // Test structure //
58 // Test structure //
59 // ////////////// //
59 // ////////////// //
60
60
61 QTest::addColumn<QString>("name");
61 QTest::addColumn<QString>("name");
62 QTest::addColumn<QVariantHash>("metadata");
62 QTest::addColumn<QVariantHash>("metadata");
63 QTest::addColumn<SqpRange>("range");
63 QTest::addColumn<SqpRange>("range");
64 QTest::addColumn<SqpRange>("cacheRange");
64 QTest::addColumn<SqpRange>("cacheRange");
65 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
65 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
66
66
67 // ////////// //
67 // ////////// //
68 // Test cases //
68 // Test cases //
69 // ////////// //
69 // ////////// //
70
70
71 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
71 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
72 QTest::newRow("clone1") << QStringLiteral("var1")
72 QTest::newRow("clone1") << QStringLiteral("var1")
73 << QVariantHash{{"data1", 1}, {"data2", "abc"}}
73 << QVariantHash{{"data1", 1}, {"data2", "abc"}}
74 << SqpRange{date(2017, 1, 1, 12, 30, 0), (date(2017, 1, 1, 12, 45, 0))}
74 << SqpRange{date(2017, 1, 1, 12, 30, 0), (date(2017, 1, 1, 12, 45, 0))}
75 << cacheRange << dataSeries(cacheRange);
75 << cacheRange << dataSeries(cacheRange);
76 }
76 }
77
77
78 void TestVariable::testClone()
78 void TestVariable::testClone()
79 {
79 {
80 // Creates variable
80 // Creates variable
81 QFETCH(QString, name);
81 QFETCH(QString, name);
82 QFETCH(QVariantHash, metadata);
82 QFETCH(QVariantHash, metadata);
83 QFETCH(SqpRange, range);
83 QFETCH(SqpRange, range);
84 QFETCH(SqpRange, cacheRange);
84 QFETCH(SqpRange, cacheRange);
85 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
85 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
86
86
87 Variable variable{name, metadata};
87 Variable variable{name, metadata};
88 variable.setRange(range);
88 variable.setRange(range);
89 variable.setCacheRange(cacheRange);
89 variable.setCacheRange(cacheRange);
90 variable.mergeDataSeries(dataSeries);
90 variable.mergeDataSeries(dataSeries);
91
91
92 // Clones variable
92 // Clones variable
93 auto clone = variable.clone();
93 auto clone = variable.clone();
94
94
95 // Checks cloned variable's state
95 // Checks cloned variable's state
96 QCOMPARE(clone->name(), name);
96 QCOMPARE(clone->name(), name);
97 QCOMPARE(clone->metadata(), metadata);
97 QCOMPARE(clone->metadata(), metadata);
98 QCOMPARE(clone->range(), range);
98 QCOMPARE(clone->range(), range);
99 QCOMPARE(clone->cacheRange(), cacheRange);
99 QCOMPARE(clone->cacheRange(), cacheRange);
100
100
101 // Compares data series
101 // Compares data series
102 if (dataSeries != nullptr) {
102 if (dataSeries != nullptr) {
103 QVERIFY(clone->dataSeries() != nullptr);
103 QVERIFY(clone->dataSeries() != nullptr);
104 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), clone->dataSeries()->cbegin(),
104 QVERIFY(std::equal(dataSeries->cbegin(), dataSeries->cend(), clone->dataSeries()->cbegin(),
105 clone->dataSeries()->cend(), [](const auto &it1, const auto &it2) {
105 clone->dataSeries()->cend(), [](const auto &it1, const auto &it2) {
106 return it1.x() == it2.x() && it1.value() == it2.value();
106 return it1.x() == it2.x() && it1.value() == it2.value();
107 }));
107 }));
108 }
108 }
109 else {
109 else {
110 QVERIFY(clone->dataSeries() == nullptr);
110 QVERIFY(clone->dataSeries() == nullptr);
111 }
111 }
112 }
112 }
113
113
114 void TestVariable::testNotInCacheRangeList()
114 void TestVariable::testNotInCacheRangeList()
115 {
115 {
116 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
116 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
117 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
117 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
118
118
119 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
119 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
120
120
121 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
121 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
122 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
122 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
123
123
124 auto sqpCR
124 auto sqpCR
125 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
125 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
126
126
127 Variable var{"Var test"};
127 Variable var{"Var test"};
128 var.setRange(sqpR);
128 var.setRange(sqpR);
129 var.setCacheRange(sqpCR);
129 var.setCacheRange(sqpCR);
130
130
131 // 1: [ts,te] < varTS
131 // 1: [ts,te] < varTS
132 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
132 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
133 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
133 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
134 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
134 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
135
135
136 auto notInCach = var.provideNotInCacheRangeList(sqp);
136 auto notInCach = var.provideNotInCacheRangeList(sqp);
137
137
138 QCOMPARE(notInCach.size(), 1);
138 QCOMPARE(notInCach.size(), 1);
139
139
140 auto notInCachRange = notInCach.first();
140 auto notInCachRange = notInCach.first();
141
141
142 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
142 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
143 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
143 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
144
144
145 // 2: ts < varTS < te < varTE
145 // 2: ts < varTS < te < varTE
146 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
146 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
147 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
147 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
148 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
148 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
149 notInCach = var.provideNotInCacheRangeList(sqp);
149 notInCach = var.provideNotInCacheRangeList(sqp);
150 QCOMPARE(notInCach.size(), 1);
150 QCOMPARE(notInCach.size(), 1);
151 notInCachRange = notInCach.first();
151 notInCachRange = notInCach.first();
152 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
152 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
153 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
153 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
154
154
155 // 3: varTS < ts < te < varTE
155 // 3: varTS < ts < te < varTE
156 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
156 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
157 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
157 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
158 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
158 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
159 notInCach = var.provideNotInCacheRangeList(sqp);
159 notInCach = var.provideNotInCacheRangeList(sqp);
160 QCOMPARE(notInCach.size(), 0);
160 QCOMPARE(notInCach.size(), 0);
161
161
162
162
163 // 4: varTS < ts < varTE < te
163 // 4: varTS < ts < varTE < te
164 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
164 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
165 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
165 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
166 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
166 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
167 notInCach = var.provideNotInCacheRangeList(sqp);
167 notInCach = var.provideNotInCacheRangeList(sqp);
168 QCOMPARE(notInCach.size(), 1);
168 QCOMPARE(notInCach.size(), 1);
169 notInCachRange = notInCach.first();
169 notInCachRange = notInCach.first();
170 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
170 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
171 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
171 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
172
172
173 // 5: varTS < varTE < ts < te
173 // 5: varTS < varTE < ts < te
174 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
174 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
175 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
175 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
176 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
176 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
177 notInCach = var.provideNotInCacheRangeList(sqp);
177 notInCach = var.provideNotInCacheRangeList(sqp);
178 QCOMPARE(notInCach.size(), 1);
178 QCOMPARE(notInCach.size(), 1);
179 notInCachRange = notInCach.first();
179 notInCachRange = notInCach.first();
180 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
180 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
181 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
181 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
182
182
183 // 6: ts <varTS < varTE < te
183 // 6: ts <varTS < varTE < te
184 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
184 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
185 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
185 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
186 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
186 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
187 notInCach = var.provideNotInCacheRangeList(sqp);
187 notInCach = var.provideNotInCacheRangeList(sqp);
188 QCOMPARE(notInCach.size(), 2);
188 QCOMPARE(notInCach.size(), 2);
189 notInCachRange = notInCach.first();
189 notInCachRange = notInCach.first();
190 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
190 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
191 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
191 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRS));
192 notInCachRange = notInCach[1];
192 notInCachRange = notInCach[1];
193 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
193 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRE));
194 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
194 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
195 }
195 }
196
196
197
197
198 void TestVariable::testInCacheRangeList()
198 void TestVariable::testInCacheRangeList()
199 {
199 {
200 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
200 auto varRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
201 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
201 auto varRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 40, 0}};
202
202
203 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
203 auto sqpR = SqpRange{DateUtils::secondsSinceEpoch(varRS), DateUtils::secondsSinceEpoch(varRE)};
204
204
205 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
205 auto varCRS = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 0, 0}};
206 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
206 auto varCRE = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 0, 0}};
207 auto sqpCR
207 auto sqpCR
208 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
208 = SqpRange{DateUtils::secondsSinceEpoch(varCRS), DateUtils::secondsSinceEpoch(varCRE)};
209
209
210 Variable var{"Var test"};
210 Variable var{"Var test"};
211 var.setRange(sqpR);
211 var.setRange(sqpR);
212 var.setCacheRange(sqpCR);
212 var.setCacheRange(sqpCR);
213
213
214 // 1: [ts,te] < varTS
214 // 1: [ts,te] < varTS
215 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
215 auto ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
216 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
216 auto te = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
217 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
217 auto sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
218
218
219 auto notInCach = var.provideInCacheRangeList(sqp);
219 auto notInCach = var.provideInCacheRangeList(sqp);
220
220
221 QCOMPARE(notInCach.size(), 0);
221 QCOMPARE(notInCach.size(), 0);
222
222
223 // 2: ts < varTS < te < varTE
223 // 2: ts < varTS < te < varTE
224 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
224 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 0, 0, 0}};
225 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
225 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
226 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
226 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
227 notInCach = var.provideInCacheRangeList(sqp);
227 notInCach = var.provideInCacheRangeList(sqp);
228 QCOMPARE(notInCach.size(), 1);
228 QCOMPARE(notInCach.size(), 1);
229 auto notInCachRange = notInCach.first();
229 auto notInCachRange = notInCach.first();
230 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
230 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
231 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
231 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
232
232
233 // 3: varTS < ts < te < varTE
233 // 3: varTS < ts < te < varTE
234 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
234 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
235 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
235 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 30, 0}};
236 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
236 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
237 notInCach = var.provideInCacheRangeList(sqp);
237 notInCach = var.provideInCacheRangeList(sqp);
238 QCOMPARE(notInCach.size(), 1);
238 QCOMPARE(notInCach.size(), 1);
239 notInCachRange = notInCach.first();
239 notInCachRange = notInCach.first();
240 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
240 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
241 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
241 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(te));
242
242
243 // 4: varTS < ts < varTE < te
243 // 4: varTS < ts < varTE < te
244 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
244 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 3, 20, 0}};
245 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
245 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
246 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
246 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
247 notInCach = var.provideInCacheRangeList(sqp);
247 notInCach = var.provideInCacheRangeList(sqp);
248 QCOMPARE(notInCach.size(), 1);
248 QCOMPARE(notInCach.size(), 1);
249 notInCachRange = notInCach.first();
249 notInCachRange = notInCach.first();
250 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
250 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(ts));
251 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
251 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
252
252
253 // 5: varTS < varTE < ts < te
253 // 5: varTS < varTE < ts < te
254 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
254 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 4, 20, 0}};
255 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
255 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
256 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
256 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
257 notInCach = var.provideInCacheRangeList(sqp);
257 notInCach = var.provideInCacheRangeList(sqp);
258 QCOMPARE(notInCach.size(), 0);
258 QCOMPARE(notInCach.size(), 0);
259
259
260 // 6: ts <varTS < varTE < te
260 // 6: ts <varTS < varTE < te
261 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
261 ts = QDateTime{QDate{2017, 01, 01}, QTime{2, 1, 0, 0}};
262 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
262 te = QDateTime{QDate{2017, 01, 01}, QTime{2, 5, 0, 0}};
263 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
263 sqp = SqpRange{DateUtils::secondsSinceEpoch(ts), DateUtils::secondsSinceEpoch(te)};
264 notInCach = var.provideInCacheRangeList(sqp);
264 notInCach = var.provideInCacheRangeList(sqp);
265 QCOMPARE(notInCach.size(), 1);
265 QCOMPARE(notInCach.size(), 1);
266 notInCachRange = notInCach.first();
266 notInCachRange = notInCach.first();
267 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
267 QCOMPARE(notInCachRange.m_TStart, DateUtils::secondsSinceEpoch(varCRS));
268 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
268 QCOMPARE(notInCachRange.m_TEnd, DateUtils::secondsSinceEpoch(varCRE));
269 }
269 }
270
270
271 namespace {
271 namespace {
272
272
273 /// Struct used to represent an operation for @sa TestVariable::testNbPoints()
273 /// Struct used to represent an operation for @sa TestVariable::testNbPoints()
274 struct NbPointsOperation {
274 struct NbPointsOperation {
275 SqpRange m_CacheRange; /// Range to set for the variable
275 SqpRange m_CacheRange; /// Range to set for the variable
276 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
276 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
277 int m_ExpectedNbPoints; /// Number of points in the variable expected after operation
277 int m_ExpectedNbPoints; /// Number of points in the variable expected after operation
278 };
278 };
279
279
280 using NbPointsOperations = std::vector<NbPointsOperation>;
280 using NbPointsOperations = std::vector<NbPointsOperation>;
281
281
282 } // namespace
282 } // namespace
283
283
284 Q_DECLARE_METATYPE(NbPointsOperations)
284 Q_DECLARE_METATYPE(NbPointsOperations)
285
285
286 void TestVariable::testNbPoints_data()
286 void TestVariable::testNbPoints_data()
287 {
287 {
288 // ////////////// //
288 // ////////////// //
289 // Test structure //
289 // Test structure //
290 // ////////////// //
290 // ////////////// //
291
291
292 QTest::addColumn<NbPointsOperations>("operations");
292 QTest::addColumn<NbPointsOperations>("operations");
293
293
294 // ////////// //
294 // ////////// //
295 // Test cases //
295 // Test cases //
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 }
317
317
318 void TestVariable::testNbPoints()
318 void TestVariable::testNbPoints()
319 {
319 {
320 // Creates variable
320 // Creates variable
321 Variable variable{"var"};
321 Variable variable{"var"};
322 QCOMPARE(variable.nbPoints(), 0);
322 QCOMPARE(variable.nbPoints(), 0);
323
323
324 QFETCH(NbPointsOperations, operations);
324 QFETCH(NbPointsOperations, operations);
325 for (const auto &operation : operations) {
325 for (const auto &operation : operations) {
326 // Sets cache range and merge data series
326 // Sets cache range and merge data series
327 variable.setCacheRange(operation.m_CacheRange);
327 variable.setCacheRange(operation.m_CacheRange);
328 if (operation.m_DataSeries != nullptr) {
328 if (operation.m_DataSeries != nullptr) {
329 variable.mergeDataSeries(operation.m_DataSeries);
329 variable.mergeDataSeries(operation.m_DataSeries);
330 }
330 }
331
331
332 // Checks nb points
332 // Checks nb points
333 QCOMPARE(variable.nbPoints(), operation.m_ExpectedNbPoints);
333 QCOMPARE(variable.nbPoints(), operation.m_ExpectedNbPoints);
334 }
334 }
335 }
335 }
336
336
337 namespace {
337 namespace {
338
338
339 /// Struct used to represent a range operation on a variable
339 /// Struct used to represent a range operation on a variable
340 /// @sa TestVariable::testRealRange()
340 /// @sa TestVariable::testRealRange()
341 struct RangeOperation {
341 struct RangeOperation {
342 SqpRange m_CacheRange; /// Range to set for the variable
342 SqpRange m_CacheRange; /// Range to set for the variable
343 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
343 std::shared_ptr<ScalarSeries> m_DataSeries; /// Series to merge in the variable
344 SqpRange m_ExpectedRealRange; /// Real Range expected after operation on the variable
344 SqpRange m_ExpectedRealRange; /// Real Range expected after operation on the variable
345 };
345 };
346
346
347 using RangeOperations = std::vector<RangeOperation>;
347 using RangeOperations = std::vector<RangeOperation>;
348
348
349 } // namespace
349 } // namespace
350
350
351 Q_DECLARE_METATYPE(RangeOperations)
351 Q_DECLARE_METATYPE(RangeOperations)
352
352
353 void TestVariable::testRealRange_data()
353 void TestVariable::testRealRange_data()
354 {
354 {
355 // ////////////// //
355 // ////////////// //
356 // Test structure //
356 // Test structure //
357 // ////////////// //
357 // ////////////// //
358
358
359 QTest::addColumn<RangeOperations>("operations");
359 QTest::addColumn<RangeOperations>("operations");
360
360
361 // ////////// //
361 // ////////// //
362 // Test cases //
362 // Test cases //
363 // ////////// //
363 // ////////// //
364 RangeOperations operations{};
364 RangeOperations operations{};
365
365
366 // Inits cache range and data series (expected real range = cache range)
366 // Inits cache range and data series (expected real range = cache range)
367 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
367 auto cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 13, 0, 0)};
368 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
368 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
369
369
370 // Changes cache range and updates data series (expected real range = cache range)
370 // Changes cache range and updates data series (expected real range = cache range)
371 cacheRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
371 cacheRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
372 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
372 operations.push_back({cacheRange, dataSeries(cacheRange), cacheRange});
373
373
374 // Changes cache range and update data series but with a lower range (expected real range =
374 // Changes cache range and update data series but with a lower range (expected real range =
375 // data series range)
375 // data series range)
376 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 16, 0, 0)};
376 cacheRange = SqpRange{date(2017, 1, 1, 12, 0, 0), date(2017, 1, 1, 16, 0, 0)};
377 auto dataSeriesRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
377 auto dataSeriesRange = SqpRange{date(2017, 1, 1, 14, 0, 0), date(2017, 1, 1, 15, 0, 0)};
378 operations.push_back({cacheRange, dataSeries(dataSeriesRange), dataSeriesRange});
378 operations.push_back({cacheRange, dataSeries(dataSeriesRange), dataSeriesRange});
379
379
380 // Changes cache range but DON'T update data series (expected real range = cache range
380 // Changes cache range but DON'T update data series (expected real range = cache range
381 // before operation)
381 // before operation)
382 cacheRange = SqpRange{date(2017, 1, 1, 10, 0, 0), date(2017, 1, 1, 17, 0, 0)};
382 cacheRange = SqpRange{date(2017, 1, 1, 10, 0, 0), date(2017, 1, 1, 17, 0, 0)};
383 operations.push_back({cacheRange, nullptr, dataSeriesRange});
383 operations.push_back({cacheRange, nullptr, dataSeriesRange});
384
384
385 QTest::newRow("realRange1") << operations;
385 QTest::newRow("realRange1") << operations;
386 }
386 }
387
387
388 void TestVariable::testRealRange()
388 void TestVariable::testRealRange()
389 {
389 {
390 // Creates variable (real range is invalid)
390 // Creates variable (real range is invalid)
391 Variable variable{"var"};
391 Variable variable{"var"};
392 QCOMPARE(variable.realRange(), INVALID_RANGE);
392 QCOMPARE(variable.realRange(), INVALID_RANGE);
393
393
394 QFETCH(RangeOperations, operations);
394 QFETCH(RangeOperations, operations);
395 for (const auto &operation : operations) {
395 for (const auto &operation : operations) {
396 // Sets cache range and merge data series
396 // Sets cache range and merge data series
397 variable.setCacheRange(operation.m_CacheRange);
397 variable.setCacheRange(operation.m_CacheRange);
398 if (operation.m_DataSeries != nullptr) {
398 if (operation.m_DataSeries != nullptr) {
399 variable.mergeDataSeries(operation.m_DataSeries);
399 variable.mergeDataSeries(operation.m_DataSeries);
400 }
400 }
401
401
402 // Checks real range
402 // Checks real range
403 QCOMPARE(variable.realRange(), operation.m_ExpectedRealRange);
403 QCOMPARE(variable.realRange(), operation.m_ExpectedRealRange);
404 }
404 }
405 }
405 }
406
406
407
407
408 QTEST_MAIN(TestVariable)
408 QTEST_MAIN(TestVariable)
409 #include "TestVariable.moc"
409 #include "TestVariable.moc"
@@ -1,20 +1,23
1
1
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'],
10 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
13 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
11 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
14 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
12 ]
15 ]
13
16
14 foreach unit_test : tests
17 foreach unit_test : tests
15 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
18 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
16 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
19 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
17 dependencies : [sciqlop_core, qt5test])
20 dependencies : [sciqlop_core, qt5test])
18 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
21 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
19 endforeach
22 endforeach
20
23
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