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