##// END OF EJS Templates
Wait for the end of an acquisition to validate an operation (1)...
Alexandre Leroux -
r1246:2785fa3e9772
parent child
Show More
@@ -0,0 +1,38
1 #ifndef SCIQLOP_SIGNALWAITER_H
2 #define SCIQLOP_SIGNALWAITER_H
3
4 #include "CoreGlobal.h"
5
6 #include <QEventLoop>
7
8 /**
9 * Class for synchronously waiting for the reception of a signal. The signal to wait is passed to
10 * the construction of the object. When starting the wait, a timeout can be set to exit if the
11 * signal has not been sent
12 */
13 class SCIQLOP_CORE_EXPORT SignalWaiter : public QObject {
14 Q_OBJECT
15 public:
16 /**
17 * Ctor
18 * @param object the sender of the signal
19 * @param signal the signal to listen
20 */
21 explicit SignalWaiter(QObject &sender, const char *signal);
22
23 /**
24 * Starts the signal and leaves after the signal has been received, or after the timeout
25 * @param timeout the timeout set (if 0, uses a default timeout)
26 * @return true if the signal was sent, false if the timeout occured
27 */
28 bool wait(int timeout);
29
30 private:
31 bool m_Timeout;
32 QEventLoop m_EventLoop;
33
34 private slots:
35 void timeout();
36 };
37
38 #endif // SCIQLOP_SIGNALWAITER_H
@@ -0,0 +1,36
1 #include "Common/SignalWaiter.h"
2
3 #include <QTimer>
4
5 namespace {
6
7 const auto DEFAULT_TIMEOUT = 30000;
8
9 } // namespace
10
11 SignalWaiter::SignalWaiter(QObject &sender, const char *signal) : m_Timeout{false}
12 {
13 connect(&sender, signal, &m_EventLoop, SLOT(quit()));
14 }
15
16 bool SignalWaiter::wait(int timeout)
17 {
18 if (timeout == 0) {
19 timeout = DEFAULT_TIMEOUT;
20 }
21
22 QTimer timer{};
23 timer.setInterval(timeout);
24 timer.start();
25 connect(&timer, &QTimer::timeout, this, &SignalWaiter::timeout);
26
27 m_EventLoop.exec();
28
29 return !m_Timeout;
30 }
31
32 void SignalWaiter::timeout()
33 {
34 m_Timeout = true;
35 m_EventLoop.quit();
36 }
@@ -1,73 +1,75
1 1
2 2 qxorm_dep = dependency('QxOrm', required : true, fallback:['QxOrm','qxorm_dep'])
3 3 catalogueapi_dep = dependency('CatalogueAPI', required : true, fallback:['CatalogueAPI','CatalogueAPI_dep'])
4 4
5 5
6 6 core_moc_headers = [
7 7 'include/Catalogue/CatalogueController.h',
8 'include/Common/SignalWaiter.h',
8 9 'include/Data/IDataProvider.h',
9 10 'include/DataSource/DataSourceController.h',
10 11 'include/DataSource/DataSourceItemAction.h',
11 12 'include/Network/NetworkController.h',
12 13 'include/Time/TimeController.h',
13 14 'include/Variable/Variable.h',
14 15 'include/Variable/VariableCacheController.h',
15 16 'include/Variable/VariableController.h',
16 17 'include/Variable/VariableAcquisitionWorker.h',
17 18 'include/Variable/VariableSynchronizationGroup.h',
18 19 'include/Variable/VariableModel.h',
19 20 'include/Visualization/VisualizationController.h'
20 21 ]
21 22
22 23
23 24 core_moc_files = qt5.preprocess(moc_headers : core_moc_headers)
24 25
25 26 core_sources = [
26 27 'src/Common/DateUtils.cpp',
28 'src/Common/SignalWaiter.cpp',
27 29 'src/Common/StringUtils.cpp',
28 30 'src/Common/MimeTypesDef.cpp',
29 31 'src/Catalogue/CatalogueController.cpp',
30 32 'src/Data/ScalarSeries.cpp',
31 33 'src/Data/SpectrogramSeries.cpp',
32 34 'src/Data/DataSeriesIterator.cpp',
33 35 'src/Data/ArrayDataIterator.cpp',
34 36 'src/Data/VectorSeries.cpp',
35 37 'src/Data/OptionalAxis.cpp',
36 38 'src/Data/DataSeriesUtils.cpp',
37 39 'src/DataSource/DataSourceController.cpp',
38 40 'src/DataSource/DataSourceItem.cpp',
39 41 'src/DataSource/DataSourceItemAction.cpp',
40 42 'src/DataSource/DataSourceItemMergeHelper.cpp',
41 43 'src/Network/NetworkController.cpp',
42 44 'src/Plugin/PluginManager.cpp',
43 45 'src/Settings/SqpSettingsDefs.cpp',
44 46 'src/Time/TimeController.cpp',
45 47 'src/Variable/Variable.cpp',
46 48 'src/Variable/VariableCacheController.cpp',
47 49 'src/Variable/VariableController.cpp',
48 50 'src/Variable/VariableAcquisitionWorker.cpp',
49 51 'src/Variable/VariableSynchronizationGroup.cpp',
50 52 'src/Variable/VariableModel.cpp',
51 53 'src/Visualization/VisualizationController.cpp'
52 54 ]
53 55
54 56 core_inc = include_directories(['include', '../plugin/include'])
55 57
56 58 sciqlop_core_lib = library('sciqlopcore',
57 59 core_sources,
58 60 core_moc_files,
59 61 cpp_args : '-DCORE_LIB',
60 62 include_directories : core_inc,
61 63 dependencies : [qt5core, qt5network, catalogueapi_dep],
62 64 install : true
63 65 )
64 66
65 67
66 68 sciqlop_core = declare_dependency(link_with : sciqlop_core_lib,
67 69 include_directories : core_inc,
68 70 dependencies : [qt5core, qt5network, catalogueapi_dep])
69 71
70 72
71 73
72 74 subdir('tests')
73 75
@@ -1,368 +1,369
1 1 #include "FuzzingDefs.h"
2 2 #include "FuzzingOperations.h"
3 3 #include "FuzzingUtils.h"
4 4 #include "FuzzingValidators.h"
5 5
6 6 #include "AmdaProvider.h"
7 7
8 #include <Common/SignalWaiter.h>
8 9 #include <Network/NetworkController.h>
9 10 #include <Settings/SqpSettingsDefs.h>
10 11 #include <SqpApplication.h>
11 12 #include <Time/TimeController.h>
12 13 #include <Variable/Variable.h>
13 14 #include <Variable/VariableController.h>
14 15
15 16 #include <QLoggingCategory>
16 17 #include <QObject>
17 18 #include <QtTest>
18 19
19 20 #include <memory>
20 21
21 22 Q_LOGGING_CATEGORY(LOG_TestAmdaFuzzing, "TestAmdaFuzzing")
22 23
23 24 /**
24 25 * Macro used to generate a getter for a property in @sa FuzzingTest. The macro generates a static
25 26 * attribute that is initialized by searching in properties the property and use a default value if
26 27 * it's not present. Macro arguments are:
27 28 * - GETTER_NAME : name of the getter
28 29 * - PROPERTY_NAME: used to generate constants for property's name ({PROPERTY_NAME}_PROPERTY) and
29 30 * default value ({PROPERTY_NAME}_DEFAULT_VALUE)
30 31 * - TYPE : return type of the getter
31 32 */
32 33 // clang-format off
33 34 #define DECLARE_PROPERTY_GETTER(GETTER_NAME, PROPERTY_NAME, TYPE) \
34 35 TYPE GETTER_NAME() const \
35 36 { \
36 37 static auto result = m_Properties.value(PROPERTY_NAME##_PROPERTY, PROPERTY_NAME##_DEFAULT_VALUE).value<TYPE>(); \
37 38 return result; \
38 39 } \
39 40 // clang-format on
40 41
41 42 namespace {
42 43
43 44 // /////// //
44 45 // Aliases //
45 46 // /////// //
46 47
47 48 using IntPair = std::pair<int, int>;
48 49 using Weight = double;
49 50 using Weights = std::vector<Weight>;
50 51
51 52 using VariableOperation = std::pair<VariableId, std::shared_ptr<IFuzzingOperation> >;
52 53 using VariablesOperations = std::vector<VariableOperation>;
53 54
54 55 using WeightedOperationsPool = std::map<std::shared_ptr<IFuzzingOperation>, Weight>;
55 56 using Validators = std::vector<std::shared_ptr<IFuzzingValidator> >;
56 57
57 58 // ///////// //
58 59 // Constants //
59 60 // ///////// //
60 61
61 62 // Defaults values used when the associated properties have not been set for the test
62 63 const auto NB_MAX_OPERATIONS_DEFAULT_VALUE = 100;
63 64 const auto NB_MAX_SYNC_GROUPS_DEFAULT_VALUE = 1;
64 65 const auto NB_MAX_VARIABLES_DEFAULT_VALUE = 1;
65 66 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE = QVariant::fromValue(WeightedOperationsTypes{
66 67 {FuzzingOperationType::CREATE, 1.},
67 68 {FuzzingOperationType::DELETE, 0.1}, // Delete operation is less frequent
68 69 {FuzzingOperationType::PAN_LEFT, 1.},
69 70 {FuzzingOperationType::PAN_RIGHT, 1.},
70 71 {FuzzingOperationType::ZOOM_IN, 1.},
71 72 {FuzzingOperationType::ZOOM_OUT, 1.},
72 73 {FuzzingOperationType::SYNCHRONIZE, 0.8},
73 74 {FuzzingOperationType::DESYNCHRONIZE, 0.4}});
74 75 const auto CACHE_TOLERANCE_DEFAULT_VALUE = 0.2;
75 76
76 77 /// Min/max delays between each operation (in ms)
77 78 const auto OPERATION_DELAY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(100, 3000));
78 79
79 80 /// Validators for the tests (executed in the order in which they're defined)
80 81 const auto VALIDATORS_DEFAULT_VALUE = QVariant::fromValue(
81 82 ValidatorsTypes{{FuzzingValidatorType::RANGE, FuzzingValidatorType::DATA}});
82 83
83 84 /// Min/max number of operations to execute before calling validation
84 85 const auto VALIDATION_FREQUENCY_BOUNDS_DEFAULT_VALUE = QVariant::fromValue(std::make_pair(1, 10));
85 86
86 87 // /////// //
87 88 // Methods //
88 89 // /////// //
89 90
90 91 /// Goes through the variables pool and operations pool to determine the set of {variable/operation}
91 92 /// pairs that are valid (i.e. operation that can be executed on variable)
92 93 std::pair<VariablesOperations, Weights>
93 94 availableOperations(const FuzzingState &fuzzingState, const WeightedOperationsPool &operationsPool)
94 95 {
95 96 VariablesOperations result{};
96 97 Weights weights{};
97 98
98 99 for (const auto &variablesPoolEntry : fuzzingState.m_VariablesPool) {
99 100 auto variableId = variablesPoolEntry.first;
100 101
101 102 for (const auto &operationsPoolEntry : operationsPool) {
102 103 auto operation = operationsPoolEntry.first;
103 104 auto weight = operationsPoolEntry.second;
104 105
105 106 // A pair is valid if the current operation can be executed on the current variable
106 107 if (operation->canExecute(variableId, fuzzingState)) {
107 108 result.push_back({variableId, operation});
108 109 weights.push_back(weight);
109 110 }
110 111 }
111 112 }
112 113
113 114 return {result, weights};
114 115 }
115 116
116 117 WeightedOperationsPool createOperationsPool(const WeightedOperationsTypes &types)
117 118 {
118 119 WeightedOperationsPool result{};
119 120
120 121 std::transform(
121 122 types.cbegin(), types.cend(), std::inserter(result, result.end()), [](const auto &type) {
122 123 return std::make_pair(FuzzingOperationFactory::create(type.first), type.second);
123 124 });
124 125
125 126 return result;
126 127 }
127 128
128 129 Validators createValidators(const ValidatorsTypes &types)
129 130 {
130 131 Validators result{};
131 132
132 133 std::transform(types.cbegin(), types.cend(), std::inserter(result, result.end()),
133 134 [](const auto &type) { return FuzzingValidatorFactory::create(type); });
134 135
135 136 return result;
136 137 }
137 138
138 139 /**
139 140 * Validates all the variables' states passed in parameter, according to a set of validators
140 141 * @param variablesPool the variables' states
141 142 * @param validators the validators used for validation
142 143 */
143 144 void validate(const VariablesPool &variablesPool, const Validators &validators)
144 145 {
145 146 for (const auto &variablesPoolEntry : variablesPool) {
146 147 auto variableId = variablesPoolEntry.first;
147 148 const auto &variableState = variablesPoolEntry.second;
148 149
149 150 auto variableMessage = variableState.m_Variable ? variableState.m_Variable->name()
150 151 : QStringLiteral("null variable");
151 152 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Validating state of variable at index"
152 153 << variableId << "(" << variableMessage << ")...";
153 154
154 155 for (const auto &validator : validators) {
155 156 validator->validate(VariableState{variableState});
156 157 }
157 158
158 159 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Validation completed.";
159 160 }
160 161 }
161 162
162 163 /**
163 164 * Class to run random tests
164 165 */
165 166 class FuzzingTest {
166 167 public:
167 168 explicit FuzzingTest(VariableController &variableController, Properties properties)
168 169 : m_VariableController{variableController},
169 170 m_Properties{std::move(properties)},
170 171 m_FuzzingState{}
171 172 {
172 173 // Inits variables pool: at init, all variables are null
173 174 for (auto variableId = 0; variableId < nbMaxVariables(); ++variableId) {
174 175 m_FuzzingState.m_VariablesPool[variableId] = VariableState{};
175 176 }
176 177
177 178 // Inits sync groups and registers them into the variable controller
178 179 for (auto i = 0; i < nbMaxSyncGroups(); ++i) {
179 180 auto syncGroupId = SyncGroupId::createUuid();
180 181 variableController.onAddSynchronizationGroupId(syncGroupId);
181 182 m_FuzzingState.m_SyncGroupsPool[syncGroupId] = SyncGroup{};
182 183 }
183 184 }
184 185
185 186 void execute()
186 187 {
187 188 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Running" << nbMaxOperations() << "operations on"
188 189 << nbMaxVariables() << "variable(s)...";
189 190
190 191
191 192 // Inits the count of the number of operations before the next validation
192 193 int nextValidationCounter = 0;
193 194 auto updateValidationCounter = [this, &nextValidationCounter]() {
194 195 nextValidationCounter = RandomGenerator::instance().generateInt(
195 196 validationFrequencies().first, validationFrequencies().second);
196 197 qCInfo(LOG_TestAmdaFuzzing()).noquote()
197 198 << "Next validation in " << nextValidationCounter << "operations...";
198 199 };
199 200 updateValidationCounter();
200 201
201 202 auto canExecute = true;
202 203 for (auto i = 0; i < nbMaxOperations() && canExecute; ++i) {
203 204 // Retrieves all operations that can be executed in the current context
204 205 VariablesOperations variableOperations{};
205 206 Weights weights{};
206 207 std::tie(variableOperations, weights)
207 208 = availableOperations(m_FuzzingState, operationsPool());
208 209
209 210 canExecute = !variableOperations.empty();
210 211 if (canExecute) {
211 212 --nextValidationCounter;
212 213
213 214 // Of the operations available, chooses a random operation and executes it
214 215 auto variableOperation
215 216 = RandomGenerator::instance().randomChoice(variableOperations, weights);
216 217
217 218 auto variableId = variableOperation.first;
218 219 auto fuzzingOperation = variableOperation.second;
219 220
220 221 fuzzingOperation->execute(variableId, m_FuzzingState, m_VariableController,
221 222 m_Properties);
222 223
223 224 // Delays the next operation with a randomly generated time
224 225 auto delay = RandomGenerator::instance().generateInt(operationDelays().first,
225 226 operationDelays().second);
226 227 qCDebug(LOG_TestAmdaFuzzing())
227 228 << "Waiting " << delay << "ms before the next operation...";
228 229 QTest::qWait(delay);
229 230
230 231 // Validates variables
231 232 if (nextValidationCounter == 0) {
232 233 validate(m_FuzzingState.m_VariablesPool, validators());
233 234 updateValidationCounter();
234 235 }
235 236 }
236 237 else {
237 238 qCInfo(LOG_TestAmdaFuzzing()).noquote()
238 239 << "No more operations are available, the execution of the test will stop...";
239 240 }
240 241 }
241 242
242 243 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Execution of the test completed.";
243 244 }
244 245
245 246 private:
246 247
247 248 WeightedOperationsPool operationsPool() const
248 249 {
249 250 static auto result = createOperationsPool(
250 251 m_Properties.value(AVAILABLE_OPERATIONS_PROPERTY, AVAILABLE_OPERATIONS_DEFAULT_VALUE)
251 252 .value<WeightedOperationsTypes>());
252 253 return result;
253 254 }
254 255
255 256 Validators validators() const
256 257 {
257 258 static auto result
258 259 = createValidators(m_Properties.value(VALIDATORS_PROPERTY, VALIDATORS_DEFAULT_VALUE)
259 260 .value<ValidatorsTypes>());
260 261 return result;
261 262 }
262 263
263 264 DECLARE_PROPERTY_GETTER(nbMaxOperations, NB_MAX_OPERATIONS, int)
264 265 DECLARE_PROPERTY_GETTER(nbMaxSyncGroups, NB_MAX_SYNC_GROUPS, int)
265 266 DECLARE_PROPERTY_GETTER(nbMaxVariables, NB_MAX_VARIABLES, int)
266 267 DECLARE_PROPERTY_GETTER(operationDelays, OPERATION_DELAY_BOUNDS, IntPair)
267 268 DECLARE_PROPERTY_GETTER(validationFrequencies, VALIDATION_FREQUENCY_BOUNDS, IntPair)
268 269 DECLARE_PROPERTY_GETTER(acquisitionTimeout, ACQUISITION_TIMEOUT, int)
269 270
270 271 VariableController &m_VariableController;
271 272 Properties m_Properties;
272 273 FuzzingState m_FuzzingState;
273 274 };
274 275
275 276 } // namespace
276 277
277 278 class TestAmdaFuzzing : public QObject {
278 279 Q_OBJECT
279 280
280 281 private slots:
281 282 /// Input data for @sa testFuzzing()
282 283 void testFuzzing_data();
283 284 void testFuzzing();
284 285 };
285 286
286 287 void TestAmdaFuzzing::testFuzzing_data()
287 288 {
288 289 // ////////////// //
289 290 // Test structure //
290 291 // ////////////// //
291 292
292 293 QTest::addColumn<Properties>("properties"); // Properties for random test
293 294
294 295 // ////////// //
295 296 // Test cases //
296 297 // ////////// //
297 298
298 299 auto maxRange = SqpRange::fromDateTime({2017, 1, 1}, {0, 0}, {2017, 1, 5}, {0, 0});
299 300 MetadataPool metadataPool{{{"dataType", "vector"}, {"xml:id", "imf"}}};
300 301
301 302 // Note: we don't use auto here as we want to pass std::shared_ptr<IDataProvider> as is in the
302 303 // QVariant
303 304 std::shared_ptr<IDataProvider> provider = std::make_shared<AmdaProvider>();
304 305
305 306 QTest::newRow("fuzzingTest") << Properties{
306 307 {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
307 308 {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
308 309 {PROVIDER_PROPERTY, QVariant::fromValue(provider)}};
309 310 }
310 311
311 312 void TestAmdaFuzzing::testFuzzing()
312 313 {
313 314 QFETCH(Properties, properties);
314 315
315 316 // Sets cache property
316 317 QSettings settings{};
317 318 auto cacheTolerance = properties.value(CACHE_TOLERANCE_PROPERTY, CACHE_TOLERANCE_DEFAULT_VALUE);
318 319 settings.setValue(GENERAL_TOLERANCE_AT_INIT_KEY, cacheTolerance);
319 320 settings.setValue(GENERAL_TOLERANCE_AT_UPDATE_KEY, cacheTolerance);
320 321
321 322 auto &variableController = sqpApp->variableController();
322 323 auto &timeController = sqpApp->timeController();
323 324
324 325 // Generates random initial range (bounded to max range)
325 326 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
326 327 .value<SqpRange>();
327 328
328 329 QVERIFY(maxRange != INVALID_RANGE);
329 330
330 331 auto initialRangeStart
331 332 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
332 333 auto initialRangeEnd
333 334 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
334 335 if (initialRangeStart > initialRangeEnd) {
335 336 std::swap(initialRangeStart, initialRangeEnd);
336 337 }
337 338
338 339 // Sets initial range on time controller
339 340 SqpRange initialRange{initialRangeStart, initialRangeEnd};
340 341 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Setting initial range to" << initialRange << "...";
341 342 timeController.onTimeToUpdate(initialRange);
342 343 properties.insert(INITIAL_RANGE_PROPERTY, QVariant::fromValue(initialRange));
343 344
344 345 FuzzingTest test{variableController, properties};
345 346 test.execute();
346 347 }
347 348
348 349 int main(int argc, char *argv[])
349 350 {
350 351 QLoggingCategory::setFilterRules(
351 352 "*.warning=false\n"
352 353 "*.info=false\n"
353 354 "*.debug=false\n"
354 355 "FuzzingOperations.info=true\n"
355 356 "FuzzingValidators.info=true\n"
356 357 "TestAmdaFuzzing.info=true\n");
357 358
358 359 SqpApplication app{argc, argv};
359 360 SqpApplication::setOrganizationName("LPP");
360 361 SqpApplication::setOrganizationDomain("lpp.fr");
361 362 SqpApplication::setApplicationName("SciQLop-TestFuzzing");
362 363 app.setAttribute(Qt::AA_Use96Dpi, true);
363 364 TestAmdaFuzzing testObject{};
364 365 QTEST_SET_MAIN_SOURCE_PATH
365 366 return QTest::qExec(&testObject, argc, argv);
366 367 }
367 368
368 369 #include "TestAmdaFuzzing.moc"
General Comments 0
You need to be logged in to leave comments. Login now