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