##// END OF EJS Templates
Adds delete operation
Alexandre Leroux -
r1192:96867eb9a6ed
parent child
Show More
@@ -1,186 +1,210
1 1 #include "FuzzingOperations.h"
2 2 #include "FuzzingUtils.h"
3 3
4 4 #include <Data/IDataProvider.h>
5 5
6 6 #include <Variable/Variable.h>
7 7 #include <Variable/VariableController.h>
8 8
9 9 #include <QUuid>
10 10
11 11 #include <functional>
12 12
13 13 Q_LOGGING_CATEGORY(LOG_FuzzingOperations, "FuzzingOperations")
14 14
15 15 namespace {
16 16
17 17 struct CreateOperation : public IFuzzingOperation {
18 18 bool canExecute(const VariableState &variableState) const override
19 19 {
20 20 // A variable can be created only if it doesn't exist yet
21 21 return variableState.m_Variable == nullptr;
22 22 }
23 23
24 24 void execute(VariableState &variableState, VariableController &variableController,
25 25 const Properties &properties) const override
26 26 {
27 27 // Retrieves metadata pool from properties, and choose one of the metadata entries to
28 28 // associate it with the variable
29 29 auto metaDataPool = properties.value(METADATA_POOL_PROPERTY).value<MetadataPool>();
30 30 auto variableMetadata = RandomGenerator::instance().randomChoice(metaDataPool);
31 31
32 32 // Retrieves provider
33 33 auto variableProvider
34 34 = properties.value(PROVIDER_PROPERTY).value<std::shared_ptr<IDataProvider> >();
35 35
36 36 auto variableName = QString{"Var_%1"}.arg(QUuid::createUuid().toString());
37 37 qCInfo(LOG_FuzzingOperations()).noquote()
38 << "Creating variable" << variableName << "(metadata:" << variableMetadata << ")";
38 << "Creating variable" << variableName << "(metadata:" << variableMetadata << ")...";
39 39
40 40 auto newVariable
41 41 = variableController.createVariable(variableName, variableMetadata, variableProvider);
42 42
43 43 // Updates variable's state
44 44 variableState.m_Range = properties.value(INITIAL_RANGE_PROPERTY).value<SqpRange>();
45 45 std::swap(variableState.m_Variable, newVariable);
46 46 }
47 47 };
48 48
49 struct DeleteOperation : public IFuzzingOperation {
50 bool canExecute(const VariableState &variableState) const override
51 {
52 // A variable can be delete only if it exists
53 return variableState.m_Variable != nullptr;
54 }
55
56 void execute(VariableState &variableState, VariableController &variableController,
57 const Properties &properties) const override
58 {
59 Q_UNUSED(properties);
60
61 qCInfo(LOG_FuzzingOperations()).noquote()
62 << "Deleting variable" << variableState.m_Variable->name() << "...";
63 variableController.deleteVariable(variableState.m_Variable);
64
65 // Updates variable's state
66 variableState.m_Range = INVALID_RANGE;
67 variableState.m_Variable = nullptr;
68 }
69 };
70
49 71 /**
50 72 * Defines a move operation through a range.
51 73 *
52 74 * A move operation is determined by three functions:
53 75 * - Two 'move' functions, used to indicate in which direction the beginning and the end of a range
54 76 * are going during the operation. These functions will be:
55 77 * -- {<- / <-} for pan left
56 78 * -- {-> / ->} for pan right
57 79 * -- {-> / <-} for zoom in
58 80 * -- {<- / ->} for zoom out
59 81 * - One 'max move' functions, used to compute the max delta at which the operation can move a
60 82 * range, according to a max range. For exemple, for a range of {1, 5} and a max range of {0, 10},
61 83 * max deltas will be:
62 84 * -- {0, 4} for pan left
63 85 * -- {6, 10} for pan right
64 86 * -- {3, 3} for zoom in
65 87 * -- {0, 6} for zoom out (same spacing left and right)
66 88 */
67 89 struct MoveOperation : public IFuzzingOperation {
68 90 using MoveFunction = std::function<double(double currentValue, double maxValue)>;
69 91 using MaxMoveFunction = std::function<double(const SqpRange &range, const SqpRange &maxRange)>;
70 92
71 93 explicit MoveOperation(MoveFunction rangeStartMoveFun, MoveFunction rangeEndMoveFun,
72 94 MaxMoveFunction maxMoveFun,
73 95 const QString &label = QStringLiteral("Move operation"))
74 96 : m_RangeStartMoveFun{std::move(rangeStartMoveFun)},
75 97 m_RangeEndMoveFun{std::move(rangeEndMoveFun)},
76 98 m_MaxMoveFun{std::move(maxMoveFun)},
77 99 m_Label{label}
78 100 {
79 101 }
80 102
81 103 bool canExecute(const VariableState &variableState) const override
82 104 {
83 105 return variableState.m_Variable != nullptr;
84 106 }
85 107
86 108 void execute(VariableState &variableState, VariableController &variableController,
87 109 const Properties &properties) const override
88 110 {
89 111 auto variable = variableState.m_Variable;
90 112
91 113 // Gets the max range defined
92 114 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
93 115 .value<SqpRange>();
94 116 auto variableRange = variable->range();
95 117
96 118 if (maxRange == INVALID_RANGE || variableRange.m_TStart < maxRange.m_TStart
97 119 || variableRange.m_TEnd > maxRange.m_TEnd) {
98 120 qCWarning(LOG_FuzzingOperations()) << "Can't execute operation: invalid max range";
99 121 return;
100 122 }
101 123
102 124 // Computes the max delta at which the variable can move, up to the limits of the max range
103 125 auto deltaMax = m_MaxMoveFun(variable->range(), maxRange);
104 126
105 127 // Generates random delta that will be used to move variable
106 128 auto delta = RandomGenerator::instance().generateDouble(0, deltaMax);
107 129
108 130 // Moves variable to its new range
109 131 auto newVariableRange = SqpRange{m_RangeStartMoveFun(variableRange.m_TStart, delta),
110 132 m_RangeEndMoveFun(variableRange.m_TEnd, delta)};
111 133 qCInfo(LOG_FuzzingOperations()).noquote()
112 134 << "Performing" << m_Label << "on" << variable->name() << "(from" << variableRange
113 135 << "to" << newVariableRange << ")...";
114 136 variableController.onRequestDataLoading({variable}, newVariableRange, false);
115 137
116 138 // Updates variable's state
117 139 variableState.m_Range = newVariableRange;
118 140 }
119 141
120 142 MoveFunction m_RangeStartMoveFun;
121 143 MoveFunction m_RangeEndMoveFun;
122 144 MaxMoveFunction m_MaxMoveFun;
123 145 QString m_Label;
124 146 };
125 147
126 148 struct UnknownOperation : public IFuzzingOperation {
127 149 bool canExecute(const VariableState &variableState) const override
128 150 {
129 151 Q_UNUSED(variableState);
130 152 return false;
131 153 }
132 154
133 155 void execute(VariableState &variableState, VariableController &variableController,
134 156 const Properties &properties) const override
135 157 {
136 158 Q_UNUSED(variableState);
137 159 Q_UNUSED(variableController);
138 160 Q_UNUSED(properties);
139 161 // Does nothing
140 162 }
141 163 };
142 164
143 165 } // namespace
144 166
145 167 std::unique_ptr<IFuzzingOperation> FuzzingOperationFactory::create(FuzzingOperationType type)
146 168 {
147 169 switch (type) {
148 170 case FuzzingOperationType::CREATE:
149 171 return std::make_unique<CreateOperation>();
172 case FuzzingOperationType::DELETE:
173 return std::make_unique<DeleteOperation>();
150 174 case FuzzingOperationType::PAN_LEFT:
151 175 return std::make_unique<MoveOperation>(
152 176 std::minus<double>(), std::minus<double>(),
153 177 [](const SqpRange &range, const SqpRange &maxRange) {
154 178 return range.m_TStart - maxRange.m_TStart;
155 179 },
156 180 QStringLiteral("Pan left operation"));
157 181 case FuzzingOperationType::PAN_RIGHT:
158 182 return std::make_unique<MoveOperation>(
159 183 std::plus<double>(), std::plus<double>(),
160 184 [](const SqpRange &range, const SqpRange &maxRange) {
161 185 return maxRange.m_TEnd - range.m_TEnd;
162 186 },
163 187 QStringLiteral("Pan right operation"));
164 188 case FuzzingOperationType::ZOOM_IN:
165 189 return std::make_unique<MoveOperation>(
166 190 std::plus<double>(), std::minus<double>(),
167 191 [](const SqpRange &range, const SqpRange &maxRange) {
168 192 Q_UNUSED(maxRange)
169 193 return range.m_TEnd - (range.m_TStart + range.m_TEnd) / 2.;
170 194 },
171 195 QStringLiteral("Zoom in operation"));
172 196 case FuzzingOperationType::ZOOM_OUT:
173 197 return std::make_unique<MoveOperation>(
174 198 std::minus<double>(), std::plus<double>(),
175 199 [](const SqpRange &range, const SqpRange &maxRange) {
176 200 return std::min(range.m_TStart - maxRange.m_TStart,
177 201 maxRange.m_TEnd - range.m_TEnd);
178 202 },
179 203 QStringLiteral("Zoom out operation"));
180 204 default:
181 205 // Default case returns unknown operation
182 206 break;
183 207 }
184 208
185 209 return std::make_unique<UnknownOperation>();
186 210 }
@@ -1,48 +1,48
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 enum class FuzzingOperationType { CREATE, PAN_LEFT, PAN_RIGHT, ZOOM_IN, ZOOM_OUT };
19 enum class FuzzingOperationType { CREATE, DELETE, PAN_LEFT, PAN_RIGHT, ZOOM_IN, ZOOM_OUT };
20 20
21 21 /// Interface that represents an operation that can be executed during a fuzzing test
22 22 struct IFuzzingOperation {
23 23 virtual ~IFuzzingOperation() noexcept = default;
24 24
25 25 /// Checks if the operation can be executed according to the current variable state passed in
26 26 /// parameter
27 27 virtual bool canExecute(const VariableState &variableState) const = 0;
28 28 /// Executes the operation on the variable state passed in parameter
29 29 /// @param variableState the variable state on which to execute the operation
30 30 /// @param variableController the controller associated to the operation
31 31 /// @param properties properties that can be used to configure the operation
32 32 /// @remarks variableState is passed as a reference because, according to the operation, it can
33 33 /// be
34 34 /// modified (in/out parameter)
35 35 virtual void execute(VariableState &variableState, VariableController &variableController,
36 36 const Properties &properties = {}) const = 0;
37 37 };
38 38
39 39 /// Factory of @sa IFuzzingOperation
40 40 struct FuzzingOperationFactory {
41 41 /// Creates a fuzzing operation from a type
42 42 static std::unique_ptr<IFuzzingOperation> create(FuzzingOperationType type);
43 43 };
44 44
45 45 using WeightedOperationsTypes = std::map<FuzzingOperationType, double>;
46 46 Q_DECLARE_METATYPE(WeightedOperationsTypes)
47 47
48 48 #endif // SCIQLOP_FUZZINGOPERATIONS_H
@@ -1,280 +1,280
1 1 #include "FuzzingDefs.h"
2 2 #include "FuzzingOperations.h"
3 3 #include "FuzzingUtils.h"
4 4
5 5 #include "AmdaProvider.h"
6 6
7 7 #include <Network/NetworkController.h>
8 8 #include <Settings/SqpSettingsDefs.h>
9 9 #include <SqpApplication.h>
10 10 #include <Time/TimeController.h>
11 11 #include <Variable/Variable.h>
12 12 #include <Variable/VariableController.h>
13 13
14 14 #include <QLoggingCategory>
15 15 #include <QObject>
16 16 #include <QtTest>
17 17
18 18 #include <memory>
19 19
20 20 Q_LOGGING_CATEGORY(LOG_TestAmdaFuzzing, "TestAmdaFuzzing")
21 21
22 22 namespace {
23 23
24 24 // /////// //
25 25 // Aliases //
26 26 // /////// //
27 27
28 28 using VariableId = int;
29 29 using Weight = double;
30 30 using Weights = std::vector<Weight>;
31 31
32 32 using VariableOperation = std::pair<VariableId, std::shared_ptr<IFuzzingOperation> >;
33 33 using VariablesOperations = std::vector<VariableOperation>;
34 34
35 35 using WeightedOperationsPool = std::map<std::shared_ptr<IFuzzingOperation>, Weight>;
36 36 using VariablesPool = std::map<VariableId, VariableState>;
37 37
38 38 // ///////// //
39 39 // Constants //
40 40 // ///////// //
41 41
42 42 // Defaults values used when the associated properties have not been set for the test
43 43 const auto NB_MAX_OPERATIONS_DEFAULT_VALUE = 100;
44 44 const auto NB_MAX_VARIABLES_DEFAULT_VALUE = 1;
45 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE
46 = QVariant::fromValue(WeightedOperationsTypes{{FuzzingOperationType::CREATE, 1.},
45 const auto AVAILABLE_OPERATIONS_DEFAULT_VALUE = QVariant::fromValue(WeightedOperationsTypes{
46 {FuzzingOperationType::CREATE, 1.},
47 {FuzzingOperationType::DELETE, 0.1}, // Delete operation is less frequent
47 48 {FuzzingOperationType::PAN_LEFT, 1.},
48 49 {FuzzingOperationType::PAN_RIGHT, 1.},
49 50 {FuzzingOperationType::ZOOM_IN, 1.},
50 51 {FuzzingOperationType::ZOOM_OUT, 1.}});
51 52 const auto CACHE_TOLERANCE_DEFAULT_VALUE = 0.2;
52 53
53 54 /// Delay between each operation (in ms)
54 55 const auto OPERATION_DELAY_DEFAULT_VALUE = 3000;
55 56
56 57 // /////// //
57 58 // Methods //
58 59 // /////// //
59 60
60 61 /// Goes through the variables pool and operations pool to determine the set of {variable/operation}
61 62 /// pairs that are valid (i.e. operation that can be executed on variable)
62 63 std::pair<VariablesOperations, Weights>
63 64 availableOperations(const VariablesPool &variablesPool,
64 65 const WeightedOperationsPool &operationsPool)
65 66 {
66 67 VariablesOperations result{};
67 68 Weights weights{};
68 69
69 70 for (const auto &variablesPoolEntry : variablesPool) {
70 71 auto variableId = variablesPoolEntry.first;
71 72 const auto &variableState = variablesPoolEntry.second;
72 73
73 74 for (const auto &operationsPoolEntry : operationsPool) {
74 75 auto operation = operationsPoolEntry.first;
75 76 auto weight = operationsPoolEntry.second;
76 77
77 78 // A pair is valid if the current operation can be executed on the current variable
78 79 if (operation->canExecute(variableState)) {
79 80 result.push_back({variableId, operation});
80 81 weights.push_back(weight);
81 82 }
82 83 }
83 84 }
84 85
85 86 return {result, weights};
86 87 }
87 88
88 89 WeightedOperationsPool createOperationsPool(const WeightedOperationsTypes &types)
89 90 {
90 91 WeightedOperationsPool result{};
91 92
92 93 std::transform(
93 94 types.cbegin(), types.cend(), std::inserter(result, result.end()), [](const auto &type) {
94 95 return std::make_pair(FuzzingOperationFactory::create(type.first), type.second);
95 96 });
96 97
97 98 return result;
98 99 }
99 100
100 101 /**
101 102 * Class to run random tests
102 103 */
103 104 class FuzzingTest {
104 105 public:
105 106 explicit FuzzingTest(VariableController &variableController, Properties properties)
106 107 : m_VariableController{variableController},
107 108 m_Properties{std::move(properties)},
108 109 m_VariablesPool{}
109 110 {
110 111 // Inits variables pool: at init, all variables are null
111 112 for (auto variableId = 0; variableId < nbMaxVariables(); ++variableId) {
112 113 m_VariablesPool[variableId] = VariableState{};
113 114 }
114 115 }
115 116
116 117 void execute()
117 118 {
118 119 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Running" << nbMaxOperations() << "operations on"
119 120 << nbMaxVariables() << "variable(s)...";
120 121
121 122 auto canExecute = true;
122 123 for (auto i = 0; i < nbMaxOperations() && canExecute; ++i) {
123 124 // Retrieves all operations that can be executed in the current context
124 125 VariablesOperations variableOperations{};
125 126 Weights weights{};
126 127 std::tie(variableOperations, weights)
127 128 = availableOperations(m_VariablesPool, operationsPool());
128 129
129 130 canExecute = !variableOperations.empty();
130 131 if (canExecute) {
131 132 // Of the operations available, chooses a random operation and executes it
132 133 auto variableOperation
133 134 = RandomGenerator::instance().randomChoice(variableOperations, weights);
134 135
135 136 auto variableId = variableOperation.first;
136 137 auto &variableState = m_VariablesPool.at(variableId);
137 138 auto fuzzingOperation = variableOperation.second;
138 139
139 140 fuzzingOperation->execute(variableState, m_VariableController, m_Properties);
140 141 QTest::qWait(operationDelay());
141
142 142 }
143 143 else {
144 144 qCInfo(LOG_TestAmdaFuzzing()).noquote()
145 145 << "No more operations are available, the execution of the test will stop...";
146 146 }
147 147 }
148 148
149 149 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Execution of the test completed.";
150 150 }
151 151
152 152 private:
153 153 int nbMaxOperations() const
154 154 {
155 155 static auto result
156 156 = m_Properties.value(NB_MAX_OPERATIONS_PROPERTY, NB_MAX_OPERATIONS_DEFAULT_VALUE)
157 157 .toInt();
158 158 return result;
159 159 }
160 160
161 161 int nbMaxVariables() const
162 162 {
163 163 static auto result
164 164 = m_Properties.value(NB_MAX_VARIABLES_PROPERTY, NB_MAX_VARIABLES_DEFAULT_VALUE).toInt();
165 165 return result;
166 166 }
167 167
168 168 int operationDelay() const
169 169 {
170 170 static auto result
171 171 = m_Properties.value(OPERATION_DELAY_PROPERTY, OPERATION_DELAY_DEFAULT_VALUE).toInt();
172 172 return result;
173 173 }
174 174
175 175 WeightedOperationsPool operationsPool() const
176 176 {
177 177 static auto result = createOperationsPool(
178 178 m_Properties.value(AVAILABLE_OPERATIONS_PROPERTY, AVAILABLE_OPERATIONS_DEFAULT_VALUE)
179 179 .value<WeightedOperationsTypes>());
180 180 return result;
181 181 }
182 182
183 183 VariableController &m_VariableController;
184 184 Properties m_Properties;
185 185 VariablesPool m_VariablesPool;
186 186 };
187 187
188 188 } // namespace
189 189
190 190 class TestAmdaFuzzing : public QObject {
191 191 Q_OBJECT
192 192
193 193 private slots:
194 194 /// Input data for @sa testFuzzing()
195 195 void testFuzzing_data();
196 196 void testFuzzing();
197 197 };
198 198
199 199 void TestAmdaFuzzing::testFuzzing_data()
200 200 {
201 201 // ////////////// //
202 202 // Test structure //
203 203 // ////////////// //
204 204
205 205 QTest::addColumn<Properties>("properties"); // Properties for random test
206 206
207 207 // ////////// //
208 208 // Test cases //
209 209 // ////////// //
210 210
211 211 auto maxRange = SqpRange::fromDateTime({2017, 1, 1}, {0, 0}, {2017, 1, 5}, {0, 0});
212 212 MetadataPool metadataPool{{{"dataType", "vector"}, {"xml:id", "imf"}}};
213 213
214 214 // Note: we don't use auto here as we want to pass std::shared_ptr<IDataProvider> as is in the
215 215 // QVariant
216 216 std::shared_ptr<IDataProvider> provider = std::make_shared<AmdaProvider>();
217 217
218 218 QTest::newRow("fuzzingTest") << Properties{
219 219 {MAX_RANGE_PROPERTY, QVariant::fromValue(maxRange)},
220 220 {METADATA_POOL_PROPERTY, QVariant::fromValue(metadataPool)},
221 221 {PROVIDER_PROPERTY, QVariant::fromValue(provider)}};
222 222 }
223 223
224 224 void TestAmdaFuzzing::testFuzzing()
225 225 {
226 226 QFETCH(Properties, properties);
227 227
228 228 // Sets cache property
229 229 QSettings settings{};
230 230 auto cacheTolerance = properties.value(CACHE_TOLERANCE_PROPERTY, CACHE_TOLERANCE_DEFAULT_VALUE);
231 231 settings.setValue(GENERAL_TOLERANCE_AT_INIT_KEY, cacheTolerance);
232 232 settings.setValue(GENERAL_TOLERANCE_AT_UPDATE_KEY, cacheTolerance);
233 233
234 234 auto &variableController = sqpApp->variableController();
235 235 auto &timeController = sqpApp->timeController();
236 236
237 237 // Generates random initial range (bounded to max range)
238 238 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
239 239 .value<SqpRange>();
240 240
241 241 QVERIFY(maxRange != INVALID_RANGE);
242 242
243 243 auto initialRangeStart
244 244 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
245 245 auto initialRangeEnd
246 246 = RandomGenerator::instance().generateDouble(maxRange.m_TStart, maxRange.m_TEnd);
247 247 if (initialRangeStart > initialRangeEnd) {
248 248 std::swap(initialRangeStart, initialRangeEnd);
249 249 }
250 250
251 251 // Sets initial range on time controller
252 252 SqpRange initialRange{initialRangeStart, initialRangeEnd};
253 253 qCInfo(LOG_TestAmdaFuzzing()).noquote() << "Setting initial range to" << initialRange << "...";
254 254 timeController.onTimeToUpdate(initialRange);
255 255 properties.insert(INITIAL_RANGE_PROPERTY, QVariant::fromValue(initialRange));
256 256
257 257 FuzzingTest test{variableController, properties};
258 258 test.execute();
259 259 }
260 260
261 261 int main(int argc, char *argv[])
262 262 {
263 263 QLoggingCategory::setFilterRules(
264 264 "*.warning=false\n"
265 265 "*.info=false\n"
266 266 "*.debug=false\n"
267 267 "FuzzingOperations.info=true\n"
268 268 "TestAmdaFuzzing.info=true\n");
269 269
270 270 SqpApplication app{argc, argv};
271 271 SqpApplication::setOrganizationName("LPP");
272 272 SqpApplication::setOrganizationDomain("lpp.fr");
273 273 SqpApplication::setApplicationName("SciQLop-TestFuzzing");
274 274 app.setAttribute(Qt::AA_Use96Dpi, true);
275 275 TestAmdaFuzzing testObject{};
276 276 QTEST_SET_MAIN_SOURCE_PATH
277 277 return QTest::qExec(&testObject, argc, argv);
278 278 }
279 279
280 280 #include "TestAmdaFuzzing.moc"
General Comments 0
You need to be logged in to leave comments. Login now