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