##// END OF EJS Templates
add format
perrinel -
r1302:6fce638804d1
parent child
Show More
@@ -1,265 +1,265
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(VariableId variableId, const FuzzingState &fuzzingState) const override
19 19 {
20 20 // A variable can be created only if it doesn't exist yet
21 21 return fuzzingState.variableState(variableId).m_Variable == nullptr;
22 22 }
23 23
24 24 void execute(VariableId variableId, FuzzingState &fuzzingState,
25 25 VariableController &variableController,
26 26 const Properties &properties) const override
27 27 {
28 28 // Retrieves metadata pool from properties, and choose one of the metadata entries to
29 29 // associate it with the variable
30 30 auto metaDataPool = properties.value(METADATA_POOL_PROPERTY).value<MetadataPool>();
31 31 auto variableMetadata = RandomGenerator::instance().randomChoice(metaDataPool);
32 32
33 33 // Retrieves provider
34 34 auto variableProvider
35 35 = properties.value(PROVIDER_PROPERTY).value<std::shared_ptr<IDataProvider> >();
36 36
37 37 auto variableName = QString{"Var_%1"}.arg(QUuid::createUuid().toString());
38 qCInfo(LOG_FuzzingOperations()).noquote()
39 << "Creating variable" << variableName << "(metadata:" << variableMetadata << ")...";
38 qCInfo(LOG_FuzzingOperations()).noquote() << "Creating variable" << variableName
39 << "(metadata:" << variableMetadata << ")...";
40 40
41 41 auto newVariable
42 42 = variableController.createVariable(variableName, variableMetadata, variableProvider);
43 43
44 44 // Updates variable's state
45 45 auto &variableState = fuzzingState.variableState(variableId);
46 46 variableState.m_Range = properties.value(INITIAL_RANGE_PROPERTY).value<SqpRange>();
47 47 std::swap(variableState.m_Variable, newVariable);
48 48 }
49 49 };
50 50
51 51 struct DeleteOperation : public IFuzzingOperation {
52 52 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
53 53 {
54 54 // A variable can be delete only if it exists
55 55 return fuzzingState.variableState(variableId).m_Variable != nullptr;
56 56 }
57 57
58 58 void execute(VariableId variableId, FuzzingState &fuzzingState,
59 59 VariableController &variableController, const Properties &) const override
60 60 {
61 61 auto &variableState = fuzzingState.variableState(variableId);
62 62
63 qCInfo(LOG_FuzzingOperations()).noquote()
64 << "Deleting variable" << variableState.m_Variable->name() << "...";
63 qCInfo(LOG_FuzzingOperations()).noquote() << "Deleting variable"
64 << variableState.m_Variable->name() << "...";
65 65 variableController.deleteVariable(variableState.m_Variable);
66 66
67 67 // Updates variable's state
68 68 variableState.m_Range = INVALID_RANGE;
69 69 variableState.m_Variable = nullptr;
70 70
71 71 // Desynchronizes the variable if it was in a sync group
72 72 auto syncGroupId = fuzzingState.syncGroupId(variableId);
73 73 fuzzingState.desynchronizeVariable(variableId, syncGroupId);
74 74 }
75 75 };
76 76
77 77 /**
78 78 * Defines a move operation through a range.
79 79 *
80 80 * A move operation is determined by three functions:
81 81 * - Two 'move' functions, used to indicate in which direction the beginning and the end of a range
82 82 * are going during the operation. These functions will be:
83 83 * -- {<- / <-} for pan left
84 84 * -- {-> / ->} for pan right
85 85 * -- {-> / <-} for zoom in
86 86 * -- {<- / ->} for zoom out
87 87 * - One 'max move' functions, used to compute the max delta at which the operation can move a
88 88 * range, according to a max range. For exemple, for a range of {1, 5} and a max range of {0, 10},
89 89 * max deltas will be:
90 90 * -- {0, 4} for pan left
91 91 * -- {6, 10} for pan right
92 92 * -- {3, 3} for zoom in
93 93 * -- {0, 6} for zoom out (same spacing left and right)
94 94 */
95 95 struct MoveOperation : public IFuzzingOperation {
96 96 using MoveFunction = std::function<double(double currentValue, double maxValue)>;
97 97 using MaxMoveFunction = std::function<double(const SqpRange &range, const SqpRange &maxRange)>;
98 98
99 99 explicit MoveOperation(MoveFunction rangeStartMoveFun, MoveFunction rangeEndMoveFun,
100 100 MaxMoveFunction maxMoveFun,
101 101 const QString &label = QStringLiteral("Move operation"))
102 102 : m_RangeStartMoveFun{std::move(rangeStartMoveFun)},
103 103 m_RangeEndMoveFun{std::move(rangeEndMoveFun)},
104 104 m_MaxMoveFun{std::move(maxMoveFun)},
105 105 m_Label{label}
106 106 {
107 107 }
108 108
109 109 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
110 110 {
111 111 return fuzzingState.variableState(variableId).m_Variable != nullptr;
112 112 }
113 113
114 114 void execute(VariableId variableId, FuzzingState &fuzzingState,
115 115 VariableController &variableController,
116 116 const Properties &properties) const override
117 117 {
118 118 auto &variableState = fuzzingState.variableState(variableId);
119 119 auto variable = variableState.m_Variable;
120 120
121 121 // Gets the max range defined
122 122 auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE))
123 123 .value<SqpRange>();
124 124 auto variableRange = variableState.m_Range;
125 125
126 126 if (maxRange == INVALID_RANGE || variableRange.m_TStart < maxRange.m_TStart
127 127 || variableRange.m_TEnd > maxRange.m_TEnd) {
128 128 qCWarning(LOG_FuzzingOperations()) << "Can't execute operation: invalid max range";
129 129 return;
130 130 }
131 131
132 132 // Computes the max delta at which the variable can move, up to the limits of the max range
133 133 auto deltaMax = m_MaxMoveFun(variableRange, maxRange);
134 134
135 135 // Generates random delta that will be used to move variable
136 136 auto delta = RandomGenerator::instance().generateDouble(0, deltaMax);
137 137
138 138 // Moves variable to its new range
139 139 auto isSynchronized = !fuzzingState.syncGroupId(variableId).isNull();
140 140 auto newVariableRange = SqpRange{m_RangeStartMoveFun(variableRange.m_TStart, delta),
141 141 m_RangeEndMoveFun(variableRange.m_TEnd, delta)};
142 qCInfo(LOG_FuzzingOperations()).noquote()
143 << "Performing" << m_Label << "on" << variable->name() << "(from" << variableRange
142 qCInfo(LOG_FuzzingOperations()).noquote() << "Performing" << m_Label << "on"
143 << variable->name() << "(from" << variableRange
144 144 << "to" << newVariableRange << ")...";
145 145 variableController.onRequestDataLoading({variable}, newVariableRange, isSynchronized);
146 146
147 147 // Updates state
148 148 fuzzingState.updateRanges(variableId, newVariableRange);
149 149 }
150 150
151 151 MoveFunction m_RangeStartMoveFun;
152 152 MoveFunction m_RangeEndMoveFun;
153 153 MaxMoveFunction m_MaxMoveFun;
154 154 QString m_Label;
155 155 };
156 156
157 157 struct SynchronizeOperation : public IFuzzingOperation {
158 158 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
159 159 {
160 160 auto variable = fuzzingState.variableState(variableId).m_Variable;
161 161 return variable != nullptr && !fuzzingState.m_SyncGroupsPool.empty()
162 162 && fuzzingState.syncGroupId(variableId).isNull();
163 163 }
164 164
165 165 void execute(VariableId variableId, FuzzingState &fuzzingState,
166 166 VariableController &variableController, const Properties &) const override
167 167 {
168 168 auto &variableState = fuzzingState.variableState(variableId);
169 169
170 170 // Chooses a random synchronization group and adds the variable into sync group
171 171 auto syncGroupId = RandomGenerator::instance().randomChoice(fuzzingState.syncGroupsIds());
172 qCInfo(LOG_FuzzingOperations()).noquote()
173 << "Adding" << variableState.m_Variable->name() << "into synchronization group"
174 << syncGroupId << "...";
172 qCInfo(LOG_FuzzingOperations()).noquote() << "Adding" << variableState.m_Variable->name()
173 << "into synchronization group" << syncGroupId
174 << "...";
175 175 variableController.onAddSynchronized(variableState.m_Variable, syncGroupId);
176 176
177 177 // Updates state
178 178 fuzzingState.synchronizeVariable(variableId, syncGroupId);
179 179 }
180 180 };
181 181
182 182 struct DesynchronizeOperation : public IFuzzingOperation {
183 183 bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override
184 184 {
185 185 auto variable = fuzzingState.variableState(variableId).m_Variable;
186 186 return variable != nullptr && !fuzzingState.syncGroupId(variableId).isNull();
187 187 }
188 188
189 189 void execute(VariableId variableId, FuzzingState &fuzzingState,
190 190 VariableController &variableController, const Properties &) const override
191 191 {
192 192 auto &variableState = fuzzingState.variableState(variableId);
193 193
194 194 // Gets the sync group of the variable
195 195 auto syncGroupId = fuzzingState.syncGroupId(variableId);
196 196
197 qCInfo(LOG_FuzzingOperations()).noquote()
198 << "Removing" << variableState.m_Variable->name() << "from synchronization group"
199 << syncGroupId << "...";
197 qCInfo(LOG_FuzzingOperations()).noquote() << "Removing" << variableState.m_Variable->name()
198 << "from synchronization group" << syncGroupId
199 << "...";
200 200 variableController.onAddSynchronized(variableState.m_Variable, syncGroupId);
201 201
202 202 // Updates state
203 203 fuzzingState.desynchronizeVariable(variableId, syncGroupId);
204 204 }
205 205 };
206 206
207 207 struct UnknownOperation : public IFuzzingOperation {
208 208 bool canExecute(VariableId, const FuzzingState &) const override { return false; }
209 209
210 210 void execute(VariableId, FuzzingState &, VariableController &,
211 211 const Properties &) const override
212 212 {
213 213 }
214 214 };
215 215
216 216 } // namespace
217 217
218 218 std::unique_ptr<IFuzzingOperation> FuzzingOperationFactory::create(FuzzingOperationType type)
219 219 {
220 220 switch (type) {
221 221 case FuzzingOperationType::CREATE:
222 222 return std::make_unique<CreateOperation>();
223 223 case FuzzingOperationType::DELETE:
224 224 return std::make_unique<DeleteOperation>();
225 225 case FuzzingOperationType::PAN_LEFT:
226 226 return std::make_unique<MoveOperation>(
227 227 std::minus<double>(), std::minus<double>(),
228 228 [](const SqpRange &range, const SqpRange &maxRange) {
229 229 return range.m_TStart - maxRange.m_TStart;
230 230 },
231 231 QStringLiteral("Pan left operation"));
232 232 case FuzzingOperationType::PAN_RIGHT:
233 233 return std::make_unique<MoveOperation>(
234 234 std::plus<double>(), std::plus<double>(),
235 235 [](const SqpRange &range, const SqpRange &maxRange) {
236 236 return maxRange.m_TEnd - range.m_TEnd;
237 237 },
238 238 QStringLiteral("Pan right operation"));
239 239 case FuzzingOperationType::ZOOM_IN:
240 240 return std::make_unique<MoveOperation>(
241 241 std::plus<double>(), std::minus<double>(),
242 242 [](const SqpRange &range, const SqpRange &maxRange) {
243 243 Q_UNUSED(maxRange)
244 244 return range.m_TEnd - (range.m_TStart + range.m_TEnd) / 2.;
245 245 },
246 246 QStringLiteral("Zoom in operation"));
247 247 case FuzzingOperationType::ZOOM_OUT:
248 248 return std::make_unique<MoveOperation>(
249 249 std::minus<double>(), std::plus<double>(),
250 250 [](const SqpRange &range, const SqpRange &maxRange) {
251 251 return std::min(range.m_TStart - maxRange.m_TStart,
252 252 maxRange.m_TEnd - range.m_TEnd);
253 253 },
254 254 QStringLiteral("Zoom out operation"));
255 255 case FuzzingOperationType::SYNCHRONIZE:
256 256 return std::make_unique<SynchronizeOperation>();
257 257 case FuzzingOperationType::DESYNCHRONIZE:
258 258 return std::make_unique<DesynchronizeOperation>();
259 259 default:
260 260 // Default case returns unknown operation
261 261 break;
262 262 }
263 263
264 264 return std::make_unique<UnknownOperation>();
265 265 }
@@ -1,228 +1,228
1 1 #include "FuzzingValidators.h"
2 2 #include "FuzzingDefs.h"
3 3
4 4 #include <Data/DataSeries.h>
5 5 #include <Variable/Variable.h>
6 6
7 7 #include <QTest>
8 8
9 9 #include <functional>
10 10
11 11 Q_LOGGING_CATEGORY(LOG_FuzzingValidators, "FuzzingValidators")
12 12
13 13 namespace {
14 14
15 15 // ////////////// //
16 16 // DATA VALIDATOR //
17 17 // ////////////// //
18 18
19 19 /// Singleton used to validate data of a variable
20 20 class DataValidatorHelper {
21 21 public:
22 22 /// @return the single instance of the helper
23 23 static DataValidatorHelper &instance();
24 24 virtual ~DataValidatorHelper() noexcept = default;
25 25
26 26 virtual void validate(const VariableState &variableState) const = 0;
27 27 };
28 28
29 29 /**
30 30 * Default implementation of @sa DataValidatorHelper
31 31 */
32 32 class DefaultDataValidatorHelper : public DataValidatorHelper {
33 33 public:
34 34 void validate(const VariableState &variableState) const override
35 35 {
36 36 Q_UNUSED(variableState);
37 37 qCWarning(LOG_FuzzingValidators()).noquote() << "Checking variable's data... WARN: no data "
38 38 "verification is available for this server";
39 39 }
40 40 };
41 41
42 42 /// Data resolution in local server's files
43 43 const auto LOCALHOST_SERVER_RESOLUTION = 4;
44 44 /// Reference value used to generate the data on the local server (a value is the number of seconds
45 45 /// between the data date and this reference date)
46 46 const auto LOCALHOST_REFERENCE_VALUE
47 47 = DateUtils::secondsSinceEpoch(QDateTime{QDate{2000, 1, 1}, QTime{}, Qt::UTC});
48 48
49 49 /**
50 50 * Implementation of @sa DataValidatorHelper for the local AMDA server
51 51 */
52 52 class LocalhostServerDataValidatorHelper : public DataValidatorHelper {
53 53 public:
54 54 void validate(const VariableState &variableState) const override
55 55 {
56 56 // Don't check data for null variable
57 57 if (!variableState.m_Variable || variableState.m_Range == INVALID_RANGE) {
58 58 return;
59 59 }
60 60
61 61 auto message = "Checking variable's data...";
62 62 auto toDateString = [](double value) { return DateUtils::dateTime(value).toString(); };
63 63
64 64 // Checks that data are defined
65 65 auto variableDataSeries = variableState.m_Variable->dataSeries();
66 66 if (variableDataSeries == nullptr && variableState.m_Range != INVALID_RANGE) {
67 67 qCInfo(LOG_FuzzingValidators()).noquote()
68 68 << message << "FAIL: the variable has no data while a range is defined";
69 69 QFAIL("");
70 70 }
71 71
72 72 auto dataIts = variableDataSeries->xAxisRange(variableState.m_Range.m_TStart,
73 73 variableState.m_Range.m_TEnd);
74 74
75 75 // Checks that the data are well defined in the range:
76 76 // - there is at least one data
77 77 // - the data are consistent (no data holes)
78 78 if (std::distance(dataIts.first, dataIts.second) == 0) {
79 qCInfo(LOG_FuzzingValidators()).noquote()
80 << message << "FAIL: the variable has no data";
79 qCInfo(LOG_FuzzingValidators()).noquote() << message
80 << "FAIL: the variable has no data";
81 81 QFAIL("");
82 82 }
83 83
84 84 auto firstXAxisData = dataIts.first->x();
85 85 auto lastXAxisData = (dataIts.second - 1)->x();
86 86
87 87 if (std::abs(firstXAxisData - variableState.m_Range.m_TStart) > LOCALHOST_SERVER_RESOLUTION
88 88 || std::abs(lastXAxisData - variableState.m_Range.m_TEnd)
89 89 > LOCALHOST_SERVER_RESOLUTION) {
90 90 qCInfo(LOG_FuzzingValidators()).noquote()
91 91 << message << "FAIL: the data in the defined range are inconsistent (data hole "
92 92 "found at the beginning or the end)";
93 93 QFAIL("");
94 94 }
95 95
96 96 auto dataHoleIt = std::adjacent_find(
97 97 dataIts.first, dataIts.second, [](const auto &it1, const auto &it2) {
98 98 /// @todo: validate resolution
99 99 return std::abs(it1.x() - it2.x()) > 2 * (LOCALHOST_SERVER_RESOLUTION - 1);
100 100 });
101 101
102 102 if (dataHoleIt != dataIts.second) {
103 103 qCInfo(LOG_FuzzingValidators()).noquote()
104 104 << message << "FAIL: the data in the defined range are inconsistent (data hole "
105 105 "found between times "
106 106 << toDateString(dataHoleIt->x()) << "and " << toDateString((dataHoleIt + 1)->x())
107 107 << ")";
108 108 QFAIL("");
109 109 }
110 110
111 111 // Checks values
112 112 auto dataIndex = 0;
113 113 for (auto dataIt = dataIts.first; dataIt != dataIts.second; ++dataIt, ++dataIndex) {
114 114 auto xAxisData = dataIt->x();
115 115 auto valuesData = dataIt->values();
116 116 for (auto valueIndex = 0, valueEnd = valuesData.size(); valueIndex < valueEnd;
117 117 ++valueIndex) {
118 118 auto value = valuesData.at(valueIndex);
119 119 auto expectedValue = xAxisData + valueIndex * LOCALHOST_SERVER_RESOLUTION
120 120 - LOCALHOST_REFERENCE_VALUE;
121 121
122 122 if (value != expectedValue) {
123 123 qCInfo(LOG_FuzzingValidators()).noquote()
124 124 << message << "FAIL: incorrect value data at time"
125 125 << toDateString(xAxisData) << ", index" << valueIndex << "(found:" << value
126 126 << ", expected:" << expectedValue << ")";
127 127 QFAIL("");
128 128 }
129 129 }
130 130 }
131 131
132 132 // At this step validation is OK
133 133 qCInfo(LOG_FuzzingValidators()).noquote() << message << "OK";
134 134 }
135 135 };
136 136
137 137 /// Creates the @sa DataValidatorHelper according to the server passed in parameter
138 138 std::unique_ptr<DataValidatorHelper> createDataValidatorInstance(const QString &server)
139 139 {
140 140 if (server == QString{"localhost"}) {
141 141 return std::make_unique<LocalhostServerDataValidatorHelper>();
142 142 }
143 143 else {
144 144 return std::make_unique<DefaultDataValidatorHelper>();
145 145 }
146 146 }
147 147
148 148 DataValidatorHelper &DataValidatorHelper::instance()
149 149 {
150 150 // Creates instance depending on the SCIQLOP_AMDA_SERVER value at compile time
151 151 static auto instance = createDataValidatorInstance(SCIQLOP_AMDA_SERVER);
152 152 return *instance;
153 153 }
154 154
155 155 // /////////////// //
156 156 // RANGE VALIDATOR //
157 157 // /////////////// //
158 158
159 159 /**
160 160 * Checks that a range of a variable matches the expected range passed as a parameter
161 161 * @param variable the variable for which to check the range
162 162 * @param expectedRange the expected range
163 163 * @param getVariableRangeFun the function to retrieve the range from the variable
164 164 * @remarks if the variable is null, checks that the expected range is the invalid range
165 165 */
166 166 void validateRange(std::shared_ptr<Variable> variable, const SqpRange &expectedRange,
167 167 std::function<SqpRange(const Variable &)> getVariableRangeFun)
168 168 {
169 169 auto compare = [](const auto &range, const auto &expectedRange, const auto &message) {
170 170 if (range == expectedRange) {
171 171 qCInfo(LOG_FuzzingValidators()).noquote() << message << "OK";
172 172 }
173 173 else {
174 qCInfo(LOG_FuzzingValidators()).noquote()
175 << message << "FAIL (current range:" << range
176 << ", expected range:" << expectedRange << ")";
174 qCInfo(LOG_FuzzingValidators()).noquote() << message << "FAIL (current range:" << range
175 << ", expected range:" << expectedRange
176 << ")";
177 177 QFAIL("");
178 178 }
179 179 };
180 180
181 181 if (variable) {
182 182 compare(getVariableRangeFun(*variable), expectedRange, "Checking variable's range...");
183 183 }
184 184 else {
185 185 compare(INVALID_RANGE, expectedRange, "Checking that there is no range set...");
186 186 }
187 187 }
188 188
189 189 /**
190 190 * Default implementation of @sa IFuzzingValidator. This validator takes as parameter of its
191 191 * construction a function of validation which is called in the validate() method
192 192 */
193 193 class FuzzingValidator : public IFuzzingValidator {
194 194 public:
195 195 /// Signature of a validation function
196 196 using ValidationFunction = std::function<void(const VariableState &variableState)>;
197 197
198 198 explicit FuzzingValidator(ValidationFunction fun) : m_Fun(std::move(fun)) {}
199 199
200 200 void validate(const VariableState &variableState) const override { m_Fun(variableState); }
201 201
202 202 private:
203 203 ValidationFunction m_Fun;
204 204 };
205 205
206 206 } // namespace
207 207
208 208 std::unique_ptr<IFuzzingValidator> FuzzingValidatorFactory::create(FuzzingValidatorType type)
209 209 {
210 210 switch (type) {
211 211 case FuzzingValidatorType::DATA:
212 212 return std::make_unique<FuzzingValidator>([](const VariableState &variableState) {
213 213 DataValidatorHelper::instance().validate(variableState);
214 214 });
215 215 case FuzzingValidatorType::RANGE:
216 216 return std::make_unique<FuzzingValidator>([](const VariableState &variableState) {
217 217 auto getVariableRange = [](const Variable &variable) { return variable.range(); };
218 218 validateRange(variableState.m_Variable, variableState.m_Range, getVariableRange);
219 219 });
220 220 default:
221 221 // Default case returns invalid validator
222 222 break;
223 223 }
224 224
225 225 // Invalid validator
226 226 return std::make_unique<FuzzingValidator>(
227 227 [](const VariableState &) { QFAIL("Invalid validator"); });
228 228 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

Status change > Approved

You need to be logged in to leave comments. Login now