##// END OF EJS Templates
Adds the ability to force an acquisition pending for an operation (3)...
Alexandre Leroux -
r1251:6cdc10030ad4
parent child
Show More
@@ -1,388 +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 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE = QVariant::fromValue(WeightedOperationsTypes{
75 {FuzzingOperationType::CREATE, 1.},
76 {FuzzingOperationType::DELETE, 0.1}, // Delete operation is less frequent
77 {FuzzingOperationType::PAN_LEFT, 1.},
78 {FuzzingOperationType::PAN_RIGHT, 1.},
79 {FuzzingOperationType::ZOOM_IN, 1.},
80 {FuzzingOperationType::ZOOM_OUT, 1.},
81 {FuzzingOperationType::SYNCHRONIZE, 0.8},
82 {FuzzingOperationType::DESYNCHRONIZE, 0.4}});
74 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE = QVariant::fromValue(
75 OperationsTypes{{FuzzingOperationType::CREATE, {1., true}},
76 {FuzzingOperationType::DELETE, {0.1}}, // Delete operation is less frequent
77 {FuzzingOperationType::PAN_LEFT, {1.}},
78 {FuzzingOperationType::PAN_RIGHT, {1.}},
79 {FuzzingOperationType::ZOOM_IN, {1.}},
80 {FuzzingOperationType::ZOOM_OUT, {1.}},
81 {FuzzingOperationType::SYNCHRONIZE, {0.8}},
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 229 auto waitAcquisition = nextValidationCounter == 0
230 230 || operationsPool().at(fuzzingOperation).m_WaitAcquisition;
231 231
232 232 fuzzingOperation->execute(variableId, m_FuzzingState, m_VariableController,
233 233 m_Properties);
234 234
235 235 if (waitAcquisition) {
236 236 qCDebug(LOG_TestAmdaFuzzing()) << "Waiting for acquisition to finish...";
237 237 SignalWaiter{m_VariableController, SIGNAL(acquisitionFinished())}.wait(
238 238 acquisitionTimeout());
239 239
240 240 // Validates variables
241 241 if (nextValidationCounter == 0) {
242 242 validate(m_FuzzingState.m_VariablesPool, validators());
243 243 updateValidationCounter();
244 244 }
245 245 }
246 246 else {
247 247 // Delays the next operation with a randomly generated time
248 248 auto delay = RandomGenerator::instance().generateInt(operationDelays().first,
249 249 operationDelays().second);
250 250 qCDebug(LOG_TestAmdaFuzzing())
251 251 << "Waiting " << delay << "ms before the next operation...";
252 252 QTest::qWait(delay);
253 253 }
254 254 }
255 255 else {
256 256 qCInfo(LOG_TestAmdaFuzzing()).noquote()
257 257 << "No more operations are available, the execution of the test will stop...";
258 258 }
259 259 }
260 260
261 261 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Execution of the test completed.";
262 262 }
263 263
264 264 private:
265 265 OperationsPool operationsPool() const
266 266 {
267 267 static auto result = createOperationsPool(
268 268 m_Properties.value(AVAILABLE_OPERATIONS_PROPERTY, AVAILABLE_OPERATIONS_DEFAULT_VALUE)
269 269 .value<OperationsTypes>());
270 270 return result;
271 271 }
272 272
273 273 Validators validators() const
274 274 {
275 275 static auto result
276 276 = createValidators(m_Properties.value(VALIDATORS_PROPERTY, VALIDATORS_DEFAULT_VALUE)
277 277 .value<ValidatorsTypes>());
278 278 return result;
279 279 }
280 280
281 281 DECLARE_PROPERTY_GETTER(nbMaxOperations, NB_MAX_OPERATIONS, int)
282 282 DECLARE_PROPERTY_GETTER(nbMaxSyncGroups, NB_MAX_SYNC_GROUPS, int)
283 283 DECLARE_PROPERTY_GETTER(nbMaxVariables, NB_MAX_VARIABLES, int)
284 284 DECLARE_PROPERTY_GETTER(operationDelays, OPERATION_DELAY_BOUNDS, IntPair)
285 285 DECLARE_PROPERTY_GETTER(validationFrequencies, VALIDATION_FREQUENCY_BOUNDS, IntPair)
286 286 DECLARE_PROPERTY_GETTER(acquisitionTimeout, ACQUISITION_TIMEOUT, int)
287 287
288 288 VariableController &m_VariableController;
289 289 Properties m_Properties;
290 290 FuzzingState m_FuzzingState;
291 291 };
292 292
293 293 } // namespace
294 294
295 295 Q_DECLARE_METATYPE(OperationsTypes)
296 296
297 297 class TestAmdaFuzzing : public QObject {
298 298 Q_OBJECT
299 299
300 300 private slots:
301 301 /// Input data for @sa testFuzzing()
302 302 void testFuzzing_data();
303 303 void testFuzzing();
304 304 };
305 305
306 306 void TestAmdaFuzzing::testFuzzing_data()
307 307 {
308 308 // ////////////// //
309 309 // Test structure //
310 310 // ////////////// //
311 311
312 312 QTest::addColumn<Properties>("properties"); // Properties for random test
313 313
314 314 // ////////// //
315 315 // Test cases //
316 316 // ////////// //
317 317
318 318 auto maxRange = SqpRange::fromDateTime({2017, 1, 1}, {0, 0}, {2017, 1, 5}, {0, 0});
319 319 MetadataPool metadataPool{{{"dataType", "vector"}, {"xml:id", "imf"}}};
320 320
321 321 // Note: we don't use auto here as we want to pass std::shared_ptr<IDataProvider> as is in the
322 322 // QVariant
323 323 std::shared_ptr<IDataProvider> provider = std::make_shared<AmdaProvider>();
324 324
325 325 QTest::newRow("fuzzingTest") << Properties{
326 326 {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
327 327 {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
328 328 {PROVIDER_PROPERTY, QVariant::fromValue(provider)}};
329 329 }
330 330
331 331 void TestAmdaFuzzing::testFuzzing()
332 332 {
333 333 QFETCH(Properties, properties);
334 334
335 335 // Sets cache property
336 336 QSettings settings{};
337 337 auto cacheTolerance = properties.value(CACHE_TOLERANCE_PROPERTY, CACHE_TOLERANCE_DEFAULT_VALUE);
338 338 settings.setValue(GENERAL_TOLERANCE_AT_INIT_KEY, cacheTolerance);
339 339 settings.setValue(GENERAL_TOLERANCE_AT_UPDATE_KEY, cacheTolerance);
340 340
341 341 auto &variableController = sqpApp->variableController();
342 342 auto &timeController = sqpApp->timeController();
343 343
344 344 // Generates random initial range (bounded to max range)
345 345 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
346 346 .value<SqpRange>();
347 347
348 348 QVERIFY(maxRange != INVALID_RANGE);
349 349
350 350 auto initialRangeStart
351 351 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
352 352 auto initialRangeEnd
353 353 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
354 354 if (initialRangeStart > initialRangeEnd) {
355 355 std::swap(initialRangeStart, initialRangeEnd);
356 356 }
357 357
358 358 // Sets initial range on time controller
359 359 SqpRange initialRange{initialRangeStart, initialRangeEnd};
360 360 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Setting initial range to" << initialRange << "...";
361 361 timeController.onTimeToUpdate(initialRange);
362 362 properties.insert(INITIAL_RANGE_PROPERTY, QVariant::fromValue(initialRange));
363 363
364 364 FuzzingTest test{variableController, properties};
365 365 test.execute();
366 366 }
367 367
368 368 int main(int argc, char *argv[])
369 369 {
370 370 QLoggingCategory::setFilterRules(
371 371 "*.warning=false\n"
372 372 "*.info=false\n"
373 373 "*.debug=false\n"
374 374 "FuzzingOperations.info=true\n"
375 375 "FuzzingValidators.info=true\n"
376 376 "TestAmdaFuzzing.info=true\n");
377 377
378 378 SqpApplication app{argc, argv};
379 379 SqpApplication::setOrganizationName("LPP");
380 380 SqpApplication::setOrganizationDomain("lpp.fr");
381 381 SqpApplication::setApplicationName("SciQLop-TestFuzzing");
382 382 app.setAttribute(Qt::AA_Use96Dpi, true);
383 383 TestAmdaFuzzing testObject{};
384 384 QTEST_SET_MAIN_SOURCE_PATH
385 385 return QTest::qExec(&testObject, argc, argv);
386 386 }
387 387
388 388 #include "TestAmdaFuzzing.moc"
General Comments 0
You need to be logged in to leave comments. Login now