##// END OF EJS Templates
Merge pull request 324 from SciQLop-fork develop...
leroux -
r893:f544e4109a93 merge
parent child
Show More
@@ -1,506 +1,508
1 1 #include <QObject>
2 2 #include <QtTest>
3 3
4 4 #include <memory>
5 5
6 6 #include <Data/DataProviderParameters.h>
7 7 #include <Data/IDataProvider.h>
8 8 #include <Data/ScalarSeries.h>
9 9 #include <Time/TimeController.h>
10 10 #include <Variable/Variable.h>
11 11 #include <Variable/VariableController.h>
12 12 #include <Variable/VariableModel.h>
13 13
14 14 namespace {
15 15
16 16 /// Delay after each operation on the variable before validating it (in ms)
17 17 const auto OPERATION_DELAY = 100;
18 18
19 19 /**
20 20 * Generates values according to a range. The value generated for a time t is the number of seconds
21 21 * of difference between t and a reference value (which is midnight -> 00:00:00)
22 22 *
23 23 * Example: For a range between 00:00:10 and 00:00:20, the generated values are
24 24 * {10,11,12,13,14,15,16,17,18,19,20}
25 25 */
26 26 std::vector<double> values(const SqpRange &range)
27 27 {
28 28 QTime referenceTime{0, 0};
29 29
30 30 std::vector<double> result{};
31 31
32 32 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
33 33 auto time = DateUtils::dateTime(i).time();
34 34 result.push_back(referenceTime.secsTo(time));
35 35 }
36 36
37 37 return result;
38 38 }
39 39
40 40 void validateRanges(VariableController &variableController,
41 41 const std::map<int, SqpRange> &expectedRanges)
42 42 {
43 43 for (const auto &expectedRangeEntry : expectedRanges) {
44 44 auto variableIndex = expectedRangeEntry.first;
45 45 auto expectedRange = expectedRangeEntry.second;
46 46
47 47 // Gets the variable in the controller
48 48 auto variable = variableController.variableModel()->variable(variableIndex);
49 49
50 50 // Compares variable's range to the expected range
51 51 QVERIFY(variable != nullptr);
52 52 auto range = variable->range();
53 53 qInfo() << "range vs expected range" << range << expectedRange;
54 54 QCOMPARE(range, expectedRange);
55 55
56 56 // Compares variable's data with values expected for its range
57 57 auto dataSeries = variable->dataSeries();
58 58 QVERIFY(dataSeries != nullptr);
59 59
60 60 auto it = dataSeries->xAxisRange(range.m_TStart, range.m_TEnd);
61 61 auto expectedValues = values(range);
62 62 qInfo() << std::distance(it.first, it.second) << expectedValues.size();
63 63 QVERIFY(std::equal(it.first, it.second, expectedValues.cbegin(), expectedValues.cend(),
64 64 [](const auto &dataSeriesIt, const auto &expectedValue) {
65 65 return dataSeriesIt.value() == expectedValue;
66 66 }));
67 67 }
68 68 }
69 69
70 70 /// Provider used for the tests
71 71 class TestProvider : public IDataProvider {
72 72 std::shared_ptr<IDataProvider> clone() const { return std::make_shared<TestProvider>(); }
73 73
74 74 void requestDataLoading(QUuid acqIdentifier, const DataProviderParameters &parameters) override
75 75 {
76 76 const auto &ranges = parameters.m_Times;
77 77
78 78 for (const auto &range : ranges) {
79 79 // Generates data series
80 80 auto valuesData = values(range);
81 81
82 82 std::vector<double> xAxisData{};
83 83 for (auto i = range.m_TStart; i <= range.m_TEnd; ++i) {
84 84 xAxisData.push_back(i);
85 85 }
86 86
87 87 auto dataSeries = std::make_shared<ScalarSeries>(
88 88 std::move(xAxisData), std::move(valuesData), Unit{"t", true}, Unit{});
89 89
90 90 emit dataProvided(acqIdentifier, dataSeries, range);
91 91 }
92 92 }
93 93
94 94 void requestDataAborting(QUuid acqIdentifier) override
95 95 {
96 96 // Does nothing
97 97 }
98 98 };
99 99
100 100 /**
101 101 * Interface representing an operation performed on a variable controller.
102 102 * This interface is used in tests to apply a set of operations and check the status of the
103 103 * controller after each operation
104 104 */
105 105 struct IOperation {
106 106 virtual ~IOperation() = default;
107 107 /// Executes the operation on the variable controller
108 108 virtual void exec(VariableController &variableController) const = 0;
109 109 };
110 110
111 111 /**
112 112 *Variable creation operation in the controller
113 113 */
114 114 struct Create : public IOperation {
115 115 explicit Create(int index) : m_Index{index} {}
116 116
117 117 void exec(VariableController &variableController) const override
118 118 {
119 119 auto variable = variableController.createVariable(QString::number(m_Index), {},
120 120 std::make_unique<TestProvider>());
121 121 }
122 122
123 123 int m_Index; ///< The index of the variable to create in the controller
124 124 };
125 125
126 126 /**
127 127 * Variable move/shift operation in the controller
128 128 */
129 129 struct Move : public IOperation {
130 130 explicit Move(int index, const SqpRange &newRange, bool shift = false, int delayMS = 10)
131 131 : m_Index{index}, m_NewRange{newRange}, m_Shift{shift}, m_DelayMs{delayMS}
132 132 {
133 133 }
134 134
135 135 void exec(VariableController &variableController) const override
136 136 {
137 137 if (auto variable = variableController.variableModel()->variable(m_Index)) {
138 138 variableController.onRequestDataLoading({variable}, m_NewRange, !m_Shift);
139 139 QTest::qWait(m_DelayMs);
140 140 }
141 141 }
142 142
143 143 int m_Index; ///< The index of the variable to move
144 144 SqpRange m_NewRange; ///< The new range of the variable
145 145 bool m_Shift; ///< Performs a shift (
146 146 int m_DelayMs; ///< wait the delay after running the request (
147 147 };
148 148
149 149 /**
150 150 * Variable synchronization/desynchronization operation in the controller
151 151 */
152 152 struct Synchronize : public IOperation {
153 153 explicit Synchronize(int index, QUuid syncId, bool synchronize = true)
154 154 : m_Index{index}, m_SyncId{syncId}, m_Synchronize{synchronize}
155 155 {
156 156 }
157 157
158 158 void exec(VariableController &variableController) const override
159 159 {
160 160 if (auto variable = variableController.variableModel()->variable(m_Index)) {
161 161 if (m_Synchronize) {
162 162 variableController.onAddSynchronized(variable, m_SyncId);
163 163 }
164 164 else {
165 165 variableController.desynchronize(variable, m_SyncId);
166 166 }
167 167 }
168 168 }
169 169
170 170 int m_Index; ///< The index of the variable to sync/desync
171 171 QUuid m_SyncId; ///< The synchronization group of the variable
172 172 bool m_Synchronize; ///< Performs sync or desync operation
173 173 };
174 174
175 175 /**
176 176 * Test Iteration
177 177 *
178 178 * A test iteration includes an operation to be performed, and a set of expected ranges after each
179 179 * operation. Each range is tested after the operation to ensure that:
180 180 * - the range of the variable is the expected range
181 181 * - the data of the variable are those generated for the expected range
182 182 */
183 183 struct Iteration {
184 184 std::shared_ptr<IOperation> m_Operation; ///< Operation to perform
185 185 std::map<int, SqpRange> m_ExpectedRanges; ///< Expected ranges (by variable index)
186 186 };
187 187
188 188 using Iterations = std::vector<Iteration>;
189 189
190 190 } // namespace
191 191
192 192 Q_DECLARE_METATYPE(Iterations)
193 193
194 194 class TestVariableSync : public QObject {
195 195 Q_OBJECT
196 196
197 197 private slots:
198 void initTestCase() { QSKIP("Temporarily disables TestVariableSync"); }
199
198 200 /// Input data for @sa testSync()
199 201 void testSync_data();
200 202
201 203 /// Input data for @sa testSyncOneVar()
202 204 void testSyncOneVar_data();
203 205
204 206 /// Tests synchronization between variables through several operations
205 207 void testSync();
206 208
207 209 /// Tests synchronization between variables through several operations
208 210 void testSyncOneVar();
209 211 };
210 212
211 213 namespace {
212 214
213 215 void testSyncCase1()
214 216 {
215 217 // Id used to synchronize variables in the controller
216 218 auto syncId = QUuid::createUuid();
217 219
218 220 /// Generates a range according to a start time and a end time (the date is the same)
219 221 auto range = [](const QTime &startTime, const QTime &endTime) {
220 222 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
221 223 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
222 224 };
223 225
224 226 auto initialRange = range({12, 0}, {13, 0});
225 227
226 228 Iterations iterations{};
227 229 // Creates variables var0, var1 and var2
228 230 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
229 231 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
230 232 iterations.push_back(
231 233 {std::make_shared<Create>(2), {{0, initialRange}, {1, initialRange}, {2, initialRange}}});
232 234
233 235 // Adds variables into the sync group (ranges don't need to be tested here)
234 236 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
235 237 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
236 238 iterations.push_back({std::make_shared<Synchronize>(2, syncId)});
237 239
238 240 // Moves var0: ranges of var0, var1 and var2 change
239 241 auto newRange = range({12, 30}, {13, 30});
240 242 iterations.push_back(
241 243 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
242 244
243 245 // Moves var1: ranges of var0, var1 and var2 change
244 246 newRange = range({13, 0}, {14, 0});
245 247 iterations.push_back(
246 248 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
247 249
248 250 // Moves var2: ranges of var0, var1 and var2 change
249 251 newRange = range({13, 30}, {14, 30});
250 252 iterations.push_back(
251 253 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, newRange}}});
252 254
253 255 // Desyncs var2 and moves var0:
254 256 // - ranges of var0 and var1 change
255 257 // - range of var2 doesn't change anymore
256 258 auto var2Range = newRange;
257 259 newRange = range({13, 45}, {14, 45});
258 260 iterations.push_back({std::make_shared<Synchronize>(2, syncId, false)});
259 261 iterations.push_back(
260 262 {std::make_shared<Move>(0, newRange), {{0, newRange}, {1, newRange}, {2, var2Range}}});
261 263
262 264 // Shifts var0: although var1 is synchronized with var0, its range doesn't change
263 265 auto var1Range = newRange;
264 266 newRange = range({14, 45}, {15, 45});
265 267 iterations.push_back({std::make_shared<Move>(0, newRange, true),
266 268 {{0, newRange}, {1, var1Range}, {2, var2Range}}});
267 269
268 270 // Moves var0 through several operations:
269 271 // - range of var0 changes
270 272 // - range or var1 changes according to the previous shift (one hour)
271 273 auto moveVar0 = [&iterations](const auto &var0NewRange, const auto &var1ExpectedRange) {
272 274 iterations.push_back(
273 275 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var1ExpectedRange}}});
274 276 };
275 277
276 278 // Pan left
277 279 moveVar0(range({14, 30}, {15, 30}), range({13, 30}, {14, 30}));
278 280 // Pan right
279 281 moveVar0(range({16, 0}, {17, 0}), range({15, 0}, {16, 0}));
280 282 // Zoom in
281 283 moveVar0(range({16, 30}, {16, 45}), range({15, 30}, {15, 45}));
282 284 // Zoom out
283 285 moveVar0(range({16, 15}, {17, 0}), range({15, 15}, {16, 0}));
284 286
285 287 QTest::newRow("sync1") << syncId << initialRange << std::move(iterations) << 200;
286 288 }
287 289
288 290 void testSyncCase2()
289 291 {
290 292 // Id used to synchronize variables in the controller
291 293 auto syncId = QUuid::createUuid();
292 294
293 295 /// Generates a range according to a start time and a end time (the date is the same)
294 296 auto dateTime = [](int year, int month, int day, int hours, int minutes, int seconds) {
295 297 return DateUtils::secondsSinceEpoch(
296 298 QDateTime{{year, month, day}, QTime{hours, minutes, seconds}, Qt::UTC});
297 299 };
298 300
299 301 auto initialRange = SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)};
300 302
301 303 Iterations iterations{};
302 304 // Creates variables var0 and var1
303 305 iterations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
304 306 iterations.push_back({std::make_shared<Create>(1), {{0, initialRange}, {1, initialRange}}});
305 307
306 308 // Adds variables into the sync group (ranges don't need to be tested here)
307 309 iterations.push_back({std::make_shared<Synchronize>(0, syncId)});
308 310 iterations.push_back({std::make_shared<Synchronize>(1, syncId)});
309 311
310 312
311 313 // Moves var0 through several operations:
312 314 // - range of var0 changes
313 315 // - range or var1 changes according to the previous shift (one hour)
314 316 auto moveVar0 = [&iterations](const auto &var0NewRange) {
315 317 iterations.push_back(
316 318 {std::make_shared<Move>(0, var0NewRange), {{0, var0NewRange}, {1, var0NewRange}}});
317 319 };
318 320 moveVar0(SqpRange{dateTime(2017, 1, 1, 12, 0, 0), dateTime(2017, 1, 1, 13, 0, 0)});
319 321 moveVar0(SqpRange{dateTime(2017, 1, 1, 14, 0, 0), dateTime(2017, 1, 1, 15, 0, 0)});
320 322 moveVar0(SqpRange{dateTime(2017, 1, 1, 8, 0, 0), dateTime(2017, 1, 1, 9, 0, 0)});
321 323 // moveVar0(SqpRange{dateTime(2017, 1, 1, 7, 30, 0), dateTime(2017, 1, 1, 9, 30, 0)});
322 324 moveVar0(SqpRange{dateTime(2017, 1, 1, 2, 0, 0), dateTime(2017, 1, 1, 4, 0, 0)});
323 325 moveVar0(SqpRange{dateTime(2017, 1, 1, 6, 0, 0), dateTime(2017, 1, 1, 8, 0, 0)});
324 326
325 327 moveVar0(SqpRange{dateTime(2017, 1, 10, 6, 0, 0), dateTime(2017, 1, 15, 8, 0, 0)});
326 328 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 1, 25, 8, 0, 0)});
327 329 moveVar0(SqpRange{dateTime(2017, 1, 2, 6, 0, 0), dateTime(2017, 1, 8, 8, 0, 0)});
328 330
329 331 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
330 332 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
331 333 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
332 334 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
333 335 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
334 336 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
335 337 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
336 338 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
337 339 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
338 340 moveVar0(SqpRange{dateTime(2017, 4, 10, 6, 0, 0), dateTime(2017, 6, 15, 8, 0, 0)});
339 341 moveVar0(SqpRange{dateTime(2017, 1, 17, 6, 0, 0), dateTime(2017, 2, 25, 8, 0, 0)});
340 342 moveVar0(SqpRange{dateTime(2017, 7, 2, 6, 0, 0), dateTime(2017, 10, 8, 8, 0, 0)});
341 343
342 344
343 345 QTest::newRow("sync2") << syncId << initialRange << iterations << 4000;
344 346 // QTest::newRow("sync3") << syncId << initialRange << iterations << 5000;
345 347 }
346 348
347 349 void testSyncOnVarCase1()
348 350 {
349 351 // Id used to synchronize variables in the controller
350 352 auto syncId = QUuid::createUuid();
351 353
352 354 /// Generates a range according to a start time and a end time (the date is the same)
353 355 auto range = [](const QTime &startTime, const QTime &endTime) {
354 356 return SqpRange{DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, startTime, Qt::UTC}),
355 357 DateUtils::secondsSinceEpoch(QDateTime{{2017, 1, 1}, endTime, Qt::UTC})};
356 358 };
357 359
358 360 auto initialRange = range({12, 0}, {13, 0});
359 361
360 362 Iterations creations{};
361 363 // Creates variables var0, var1 and var2
362 364 creations.push_back({std::make_shared<Create>(0), {{0, initialRange}}});
363 365
364 366 Iterations synchronization{};
365 367 // Adds variables into the sync group (ranges don't need to be tested here)
366 368 synchronization.push_back({std::make_shared<Synchronize>(0, syncId)});
367 369
368 370 Iterations iterations{};
369 371
370 372 // Moves var0 through several operations
371 373 auto moveOp = [&iterations](const auto &requestedRange, const auto &expectedRange, auto delay) {
372 374 iterations.push_back(
373 375 {std::make_shared<Move>(0, requestedRange, true, delay), {{0, expectedRange}}});
374 376 };
375 377
376 378 // we assume here 300 ms is enough to finsh a operation
377 379 int delayToFinish = 300;
378 380 // jump to right, let's the operation time to finish
379 381 moveOp(range({14, 30}, {15, 30}), range({14, 30}, {15, 30}), delayToFinish);
380 382 // pan to right, let's the operation time to finish
381 383 moveOp(range({14, 45}, {15, 45}), range({14, 45}, {15, 45}), delayToFinish);
382 384 // jump to left, let's the operation time to finish
383 385 moveOp(range({03, 30}, {04, 30}), range({03, 30}, {04, 30}), delayToFinish);
384 386 // Pan to left, let's the operation time to finish
385 387 moveOp(range({03, 10}, {04, 10}), range({03, 10}, {04, 10}), delayToFinish);
386 388 // Zoom in, let's the operation time to finish
387 389 moveOp(range({03, 30}, {04, 00}), range({03, 30}, {04, 00}), delayToFinish);
388 390 // Zoom out left, let's the operation time to finish
389 391 moveOp(range({01, 10}, {18, 10}), range({01, 10}, {18, 10}), delayToFinish);
390 392 // Go back to initial range
391 393 moveOp(initialRange, initialRange, delayToFinish);
392 394
393 395
394 396 // jump to right, let's the operation time to finish
395 397 // moveOp(range({14, 30}, {15, 30}), initialRange, delayToFinish);
396 398 // Zoom out left, let's the operation time to finish
397 399 moveOp(range({01, 10}, {18, 10}), initialRange, delayToFinish);
398 400 // Go back to initial range
399 401 moveOp(initialRange, initialRange, 300);
400 402
401 403 QTest::newRow("syncOnVarCase1") << syncId << initialRange << std::move(creations)
402 404 << std::move(iterations);
403 405 }
404 406 }
405 407
406 408 void TestVariableSync::testSync_data()
407 409 {
408 410 // ////////////// //
409 411 // Test structure //
410 412 // ////////////// //
411 413
412 414 QTest::addColumn<QUuid>("syncId");
413 415 QTest::addColumn<SqpRange>("initialRange");
414 416 QTest::addColumn<Iterations>("iterations");
415 417 QTest::addColumn<int>("operationDelay");
416 418
417 419 // ////////// //
418 420 // Test cases //
419 421 // ////////// //
420 422
421 423 testSyncCase1();
422 424 testSyncCase2();
423 425 }
424 426
425 427 void TestVariableSync::testSyncOneVar_data()
426 428 {
427 429 // ////////////// //
428 430 // Test structure //
429 431 // ////////////// //
430 432
431 433 QTest::addColumn<QUuid>("syncId");
432 434 QTest::addColumn<SqpRange>("initialRange");
433 435 QTest::addColumn<Iterations>("creations");
434 436 QTest::addColumn<Iterations>("iterations");
435 437
436 438 // ////////// //
437 439 // Test cases //
438 440 // ////////// //
439 441
440 442 testSyncOnVarCase1();
441 443 }
442 444
443 445 void TestVariableSync::testSync()
444 446 {
445 447 // Inits controllers
446 448 TimeController timeController{};
447 449 VariableController variableController{};
448 450 variableController.setTimeController(&timeController);
449 451
450 452 QFETCH(QUuid, syncId);
451 453 QFETCH(SqpRange, initialRange);
452 454 timeController.onTimeToUpdate(initialRange);
453 455
454 456 // Synchronization group used
455 457 variableController.onAddSynchronizationGroupId(syncId);
456 458
457 459 // For each iteration:
458 460 // - execute operation
459 461 // - compare the variables' state to the expected states
460 462 QFETCH(Iterations, iterations);
461 463 QFETCH(int, operationDelay);
462 464 for (const auto &iteration : iterations) {
463 465 iteration.m_Operation->exec(variableController);
464 466 QTest::qWait(operationDelay);
465 467
466 468 validateRanges(variableController, iteration.m_ExpectedRanges);
467 469 }
468 470 }
469 471
470 472 void TestVariableSync::testSyncOneVar()
471 473 {
472 474 // Inits controllers
473 475 TimeController timeController{};
474 476 VariableController variableController{};
475 477 variableController.setTimeController(&timeController);
476 478
477 479 QFETCH(QUuid, syncId);
478 480 QFETCH(SqpRange, initialRange);
479 481 timeController.onTimeToUpdate(initialRange);
480 482
481 483 // Synchronization group used
482 484 variableController.onAddSynchronizationGroupId(syncId);
483 485
484 486 // For each iteration:
485 487 // - execute operation
486 488 // - compare the variables' state to the expected states
487 489 QFETCH(Iterations, iterations);
488 490 QFETCH(Iterations, creations);
489 491
490 492 for (const auto &creation : creations) {
491 493 creation.m_Operation->exec(variableController);
492 494 QTest::qWait(300);
493 495 }
494 496
495 497 for (const auto &iteration : iterations) {
496 498 iteration.m_Operation->exec(variableController);
497 499 }
498 500
499 501 if (!iterations.empty()) {
500 502 validateRanges(variableController, iterations.back().m_ExpectedRanges);
501 503 }
502 504 }
503 505
504 506 QTEST_MAIN(TestVariableSync)
505 507
506 508 #include "TestVariableSync.moc"
General Comments 0
You need to be logged in to leave comments. Login now