##// END OF EJS Templates
Merge branch 'feature/TestVarSync' into develop
Alexandre Leroux -
r718:6eaff3375beb merge
parent child
Show More
@@ -0,0 +1,310
1 #include <QObject>
2 #include <QtTest>
3
4 #include <memory>
5
6 #include <Data/DataProviderParameters.h>
7 #include <Data/IDataProvider.h>
8 #include <Data/ScalarSeries.h>
9 #include <Time/TimeController.h>
10 #include <Variable/Variable.h>
11 #include <Variable/VariableController.h>
12 #include <Variable/VariableModel.h>
13
14 namespace {
15
16 /// Delay after each operation on the variable before validating it (in ms)
17 const auto OPERATION_DELAY = 100;
18
19 /**
20 * Generates values according to a range. The value generated for a time t is the number of seconds
21 * of difference between t and a reference value (which is midnight -> 00:00:00)
22 *
23 * Example: For a range between 00:00:10 and 00:00:20, the generated values are
24 * {10,11,12,13,14,15,16,17,18,19,20}
25 */
26 std::vector<double> values(const SqpRange &range)
27 {
28 QTime referenceTime{0, 0};
29
30 std::vector<double> result{};
31
32 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
33 auto time = DateUtils::dateTime(i).time();
34 result.push_back(referenceTime.secsTo(time));
35 }
36
37 return result;
38 }
39
40 /// Provider used for the tests
41 class TestProvider : public IDataProvider {
42 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
43
44 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
45 {
46 const auto &ranges = parameters.m_Times;
47
48 for (const auto &range : ranges) {
49 // Generates data series
50 auto valuesData = values(range);
51
52 std::vector<double> xAxisData{};
53 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
54 xAxisData.push_back(i);
55 }
56
57 auto dataSeries = std::make_shared<ScalarSeries>(
58 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
59
60 emit dataProvided(acqIdentifier, dataSeries, range);
61 }
62 }
63
64 void requestDataAborting(QUuid acqIdentifier) override
65 {
66 // Does nothing
67 }
68 };
69
70 /**
71 * Interface representing an operation performed on a variable controller.
72 * This interface is used in tests to apply a set of operations and check the status of the
73 * controller after each operation
74 */
75 struct IOperation {
76 virtual ~IOperation() = default;
77 /// Executes the operation on the variable controller
78 virtual void exec(VariableController &variableController) const = 0;
79 };
80
81 /**
82 *Variable creation operation in the controller
83 */
84 struct Create : public IOperation {
85 explicit Create(int index) : m_Index{index} {}
86
87 void exec(VariableController &variableController) const override
88 {
89 auto variable = variableController.createVariable(QString::number(m_Index), {},
90 std::make_unique<TestProvider>());
91 }
92
93 int m_Index; ///< The index of the variable to create in the controller
94 };
95
96 /**
97 * Variable move/shift operation in the controller
98 */
99 struct Move : public IOperation {
100 explicit Move(int index, const SqpRange &newRange, bool shift = false)
101 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}
102 {
103 }
104
105 void exec(VariableController &variableController) const override
106 {
107 if (auto variable = variableController.variableModel()->variable(m_Index)) {
108 variableController.onRequestDataLoading({variable}, m_NewRange, variable->range(),
109 !m_Shift);
110 }
111 }
112
113 int m_Index; ///< The index of the variable to move
114 SqpRange m_NewRange; ///< The new range of the variable
115 bool m_Shift; ///< Performs a shift (
116 };
117
118 /**
119 * Variable synchronization/desynchronization operation in the controller
120 */
121 struct Synchronize : public IOperation {
122 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
123 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
124 {
125 }
126
127 void exec(VariableController &variableController) const override
128 {
129 if (auto variable = variableController.variableModel()->variable(m_Index)) {
130 if (m_Synchronize) {
131 variableController.onAddSynchronized(variable, m_SyncId);
132 }
133 else {
134 variableController.desynchronize(variable, m_SyncId);
135 }
136 }
137 }
138
139 int m_Index; ///< The index of the variable to sync/desync
140 QUuid m_SyncId; ///< The synchronization group of the variable
141 bool m_Synchronize; ///< Performs sync or desync operation
142 };
143
144 /**
145 * Test Iteration
146 *
147 * A test iteration includes an operation to be performed, and a set of expected ranges after each
148 * operation. Each range is tested after the operation to ensure that:
149 * - the range of the variable is the expected range
150 * - the data of the variable are those generated for the expected range
151 */
152 struct Iteration {
153 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
154 std::map<int, SqpRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
155 };
156
157 using Iterations = std::vector<Iteration>;
158
159 } // namespace
160
161 Q_DECLARE_METATYPE(Iterations)
162
163 class TestVariableSync : public QObject {
164 Q_OBJECT
165
166 private slots:
167 /// Input data for @sa testSync()
168 void testSync_data();
169
170 /// Tests synchronization between variables through several operations
171 void testSync();
172 };
173
174 void TestVariableSync::testSync_data()
175 {
176 // ////////////// //
177 // Test structure //
178 // ////////////// //
179
180 QTest::addColumn<QUuid>("syncId");
181 QTest::addColumn<SqpRange>("initialRange");
182 QTest::addColumn<Iterations>("iterations");
183
184 // ////////// //
185 // Test cases //
186 // ////////// //
187
188 // Id used to synchronize variables in the controller
189 auto syncId = QUuid::createUuid();
190
191 /// Generates a range according to a start time and a end time (the date is the same)
192 auto range = [](const QTime &startTime, const QTime &endTime) {
193 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
194 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
195 };
196
197 auto initialRange = range({12, 0}, {13, 0});
198
199 Iterations iterations{};
200 // Creates variables var0, var1 and var2
201 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
202 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
203 iterations.push_back(
204 {std::make_shared<Create>(2), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
205
206 // Adds variables into the sync group (ranges don't need to be tested here)
207 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
208 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
209 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
210
211 // Moves var0: ranges of var0, var1 and var2 change
212 auto newRange = range({12, 30}, {13, 30});
213 iterations.push_back(
214 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
215
216 // Moves var1: ranges of var0, var1 and var2 change
217 newRange = range({13, 0}, {14, 0});
218 iterations.push_back(
219 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
220
221 // Moves var2: ranges of var0, var1 and var2 change
222 newRange = range({13, 30}, {14, 30});
223 iterations.push_back(
224 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
225
226 // Desyncs var2 and moves var0:
227 // - ranges of var0 and var1 change
228 // - range of var2 doesn't change anymore
229 auto var2Range = newRange;
230 newRange = range({13, 45}, {14, 45});
231 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
232 iterations.push_back(
233 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
234
235 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
236 auto var1Range = newRange;
237 newRange = range({14, 45}, {15, 45});
238 iterations.push_back({std::make_shared<Move>(0, newRange, true),
239 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
240
241 // Moves var0 through several operations:
242 // - range of var0 changes
243 // - range or var1 changes according to the previous shift (one hour)
244 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
245 iterations.push_back(
246 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
247 };
248 // Pan left
249 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
250 // Pan right
251 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
252 // Zoom in
253 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
254 // Zoom out
255 moveVar0(range({12, 0}, {18, 0}), range({11, 0}, {17, 0}));
256
257 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations);
258 }
259
260 void TestVariableSync::testSync()
261 {
262 // Inits controllers
263 TimeController timeController{};
264 VariableController variableController{};
265 variableController.setTimeController(&timeController);
266
267 QFETCH(QUuid, syncId);
268 QFETCH(SqpRange, initialRange);
269 timeController.onTimeToUpdate(initialRange);
270
271 // Synchronization group used
272 variableController.onAddSynchronizationGroupId(syncId);
273
274 // For each iteration:
275 // - execute operation
276 // - compare the variables' state to the expected states
277 QFETCH(Iterations, iterations);
278 for (const auto &iteration : iterations) {
279 iteration.m_Operation->exec(variableController);
280 QTest::qWait(OPERATION_DELAY);
281
282 for (const auto &expectedRangeEntry : iteration.m_ExpectedRanges) {
283 auto variableIndex = expectedRangeEntry.first;
284 auto expectedRange = expectedRangeEntry.second;
285
286 // Gets the variable in the controller
287 auto variable = variableController.variableModel()->variable(variableIndex);
288
289 // Compares variable's range to the expected range
290 QVERIFY(variable != nullptr);
291 auto range = variable->range();
292 QCOMPARE(range, expectedRange);
293
294 // Compares variable's data with values expected for its range
295 auto dataSeries = variable->dataSeries();
296 QVERIFY(dataSeries != nullptr);
297
298 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
299 auto expectedValues = values(range);
300 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
301 [](const auto &dataSeriesIt, const auto &expectedValue) {
302 return dataSeriesIt.value() == expectedValue;
303 }));
304 }
305 }
306 }
307
308 QTEST_MAIN(TestVariableSync)
309
310 #include "TestVariableSync.moc"
@@ -1,19 +1,20
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/TestDataSeries.cpp'],'test_data','DataSeries test'],
6 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
6 [['Data/TestOneDimArrayData.cpp'],'test_1d','One Dim Array test'],
7 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
7 [['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
8 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
8 [['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
9 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
9 [['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
10 [['Variable/TestVariable.cpp'],'test_variable','Variable test']
10 [['Variable/TestVariable.cpp'],'test_variable','Variable test'],
11 [['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
11 ]
12 ]
12
13
13 foreach unit_test : tests
14 foreach unit_test : tests
14 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
15 test_moc_files = qt5.preprocess(moc_sources : unit_test[0])
15 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
16 test_exe = executable(unit_test[1],unit_test[0] , test_moc_files,
16 dependencies : [sciqlop_core, qt5test])
17 dependencies : [sciqlop_core, qt5test])
17 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
18 test(unit_test[2], test_exe, args: ['-teamcity', '-o', '@0@.teamcity.txt'.format(unit_test[1])])
18 endforeach
19 endforeach
19
20
General Comments 0
You need to be logged in to leave comments. Login now