FuzzingOperations.cpp
268 lines
| 11.4 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r1202 | #include "FuzzingOperations.h" | ||
Alexandre Leroux
|
r1208 | #include "FuzzingUtils.h" | ||
#include <Data/IDataProvider.h> | ||||
Alexandre Leroux
|
r1202 | |||
#include <Variable/Variable.h> | ||||
#include <Variable/VariableController.h> | ||||
Alexandre Leroux
|
r1208 | #include <QUuid> | ||
Alexandre Leroux
|
r1217 | #include <functional> | ||
Alexandre Leroux
|
r1202 | Q_LOGGING_CATEGORY(LOG_FuzzingOperations, "FuzzingOperations") | ||
namespace { | ||||
struct CreateOperation : public IFuzzingOperation { | ||||
Alexandre Leroux
|
r1236 | bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override | ||
Alexandre Leroux
|
r1211 | { | ||
Alexandre Leroux
|
r1208 | // A variable can be created only if it doesn't exist yet | ||
Alexandre Leroux
|
r1236 | return fuzzingState.variableState(variableId).m_Variable == nullptr; | ||
Alexandre Leroux
|
r1202 | } | ||
Alexandre Leroux
|
r1236 | void execute(VariableId variableId, FuzzingState &fuzzingState, | ||
VariableController &variableController, | ||||
Alexandre Leroux
|
r1211 | const Properties &properties) const override | ||
{ | ||||
// Retrieves metadata pool from properties, and choose one of the metadata entries to | ||||
// associate it with the variable | ||||
Alexandre Leroux
|
r1208 | auto metaDataPool = properties.value(METADATA_POOL_PROPERTY).value<MetadataPool>(); | ||
auto variableMetadata = RandomGenerator::instance().randomChoice(metaDataPool); | ||||
// Retrieves provider | ||||
Alexandre Leroux
|
r1211 | auto variableProvider | ||
= properties.value(PROVIDER_PROPERTY).value<std::shared_ptr<IDataProvider> >(); | ||||
Alexandre Leroux
|
r1208 | |||
auto variableName = QString{"Var_%1"}.arg(QUuid::createUuid().toString()); | ||||
r1302 | qCInfo(LOG_FuzzingOperations()).noquote() << "Creating variable" << variableName | |||
<< "(metadata:" << variableMetadata << ")..."; | ||||
Alexandre Leroux
|
r1208 | |||
Alexandre Leroux
|
r1211 | auto newVariable | ||
= variableController.createVariable(variableName, variableMetadata, variableProvider); | ||||
Alexandre Leroux
|
r1223 | |||
// Updates variable's state | ||||
Alexandre Leroux
|
r1236 | auto &variableState = fuzzingState.variableState(variableId); | ||
Alexandre Leroux
|
r1223 | variableState.m_Range = properties.value(INITIAL_RANGE_PROPERTY).value<SqpRange>(); | ||
Alexandre Leroux
|
r1221 | std::swap(variableState.m_Variable, newVariable); | ||
Alexandre Leroux
|
r1202 | } | ||
}; | ||||
Alexandre Leroux
|
r1225 | struct DeleteOperation : public IFuzzingOperation { | ||
Alexandre Leroux
|
r1236 | bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override | ||
Alexandre Leroux
|
r1225 | { | ||
// A variable can be delete only if it exists | ||||
Alexandre Leroux
|
r1236 | return fuzzingState.variableState(variableId).m_Variable != nullptr; | ||
Alexandre Leroux
|
r1225 | } | ||
Alexandre Leroux
|
r1236 | void execute(VariableId variableId, FuzzingState &fuzzingState, | ||
VariableController &variableController, const Properties &) const override | ||||
Alexandre Leroux
|
r1225 | { | ||
Alexandre Leroux
|
r1236 | auto &variableState = fuzzingState.variableState(variableId); | ||
Alexandre Leroux
|
r1225 | |||
r1302 | qCInfo(LOG_FuzzingOperations()).noquote() << "Deleting variable" | |||
<< variableState.m_Variable->name() << "..."; | ||||
Alexandre Leroux
|
r1225 | variableController.deleteVariable(variableState.m_Variable); | ||
// Updates variable's state | ||||
variableState.m_Range = INVALID_RANGE; | ||||
variableState.m_Variable = nullptr; | ||||
Alexandre Leroux
|
r1241 | |||
// Desynchronizes the variable if it was in a sync group | ||||
auto syncGroupId = fuzzingState.syncGroupId(variableId); | ||||
fuzzingState.desynchronizeVariable(variableId, syncGroupId); | ||||
Alexandre Leroux
|
r1225 | } | ||
}; | ||||
Alexandre Leroux
|
r1217 | /** | ||
* Defines a move operation through a range. | ||||
* | ||||
* A move operation is determined by three functions: | ||||
* - Two 'move' functions, used to indicate in which direction the beginning and the end of a range | ||||
* are going during the operation. These functions will be: | ||||
* -- {<- / <-} for pan left | ||||
* -- {-> / ->} for pan right | ||||
* -- {-> / <-} for zoom in | ||||
* -- {<- / ->} for zoom out | ||||
* - One 'max move' functions, used to compute the max delta at which the operation can move a | ||||
* range, according to a max range. For exemple, for a range of {1, 5} and a max range of {0, 10}, | ||||
* max deltas will be: | ||||
* -- {0, 4} for pan left | ||||
* -- {6, 10} for pan right | ||||
* -- {3, 3} for zoom in | ||||
* -- {0, 6} for zoom out (same spacing left and right) | ||||
*/ | ||||
struct MoveOperation : public IFuzzingOperation { | ||||
using MoveFunction = std::function<double(double currentValue, double maxValue)>; | ||||
using MaxMoveFunction = std::function<double(const SqpRange &range, const SqpRange &maxRange)>; | ||||
explicit MoveOperation(MoveFunction rangeStartMoveFun, MoveFunction rangeEndMoveFun, | ||||
MaxMoveFunction maxMoveFun, | ||||
const QString &label = QStringLiteral("Move operation")) | ||||
: m_RangeStartMoveFun{std::move(rangeStartMoveFun)}, | ||||
m_RangeEndMoveFun{std::move(rangeEndMoveFun)}, | ||||
m_MaxMoveFun{std::move(maxMoveFun)}, | ||||
m_Label{label} | ||||
{ | ||||
} | ||||
Alexandre Leroux
|
r1236 | bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override | ||
Alexandre Leroux
|
r1217 | { | ||
Alexandre Leroux
|
r1236 | return fuzzingState.variableState(variableId).m_Variable != nullptr; | ||
Alexandre Leroux
|
r1217 | } | ||
Alexandre Leroux
|
r1236 | void execute(VariableId variableId, FuzzingState &fuzzingState, | ||
VariableController &variableController, | ||||
Alexandre Leroux
|
r1217 | const Properties &properties) const override | ||
{ | ||||
Alexandre Leroux
|
r1236 | auto &variableState = fuzzingState.variableState(variableId); | ||
Alexandre Leroux
|
r1221 | auto variable = variableState.m_Variable; | ||
Alexandre Leroux
|
r1218 | // Gets the max range defined | ||
auto maxRange = properties.value(MAX_RANGE_PROPERTY, QVariant::fromValue(INVALID_RANGE)) | ||||
.value<SqpRange>(); | ||||
Alexandre Leroux
|
r1252 | auto variableRange = variableState.m_Range; | ||
Alexandre Leroux
|
r1218 | |||
if (maxRange == INVALID_RANGE || variableRange.m_TStart < maxRange.m_TStart | ||||
|| variableRange.m_TEnd > maxRange.m_TEnd) { | ||||
qCWarning(LOG_FuzzingOperations()) << "Can't execute operation: invalid max range"; | ||||
return; | ||||
} | ||||
// Computes the max delta at which the variable can move, up to the limits of the max range | ||||
Alexandre Leroux
|
r1252 | auto deltaMax = m_MaxMoveFun(variableRange, maxRange); | ||
Alexandre Leroux
|
r1218 | |||
// Generates random delta that will be used to move variable | ||||
auto delta = RandomGenerator::instance().generateDouble(0, deltaMax); | ||||
// Moves variable to its new range | ||||
Alexandre Leroux
|
r1242 | auto isSynchronized = !fuzzingState.syncGroupId(variableId).isNull(); | ||
Alexandre Leroux
|
r1218 | auto newVariableRange = SqpRange{m_RangeStartMoveFun(variableRange.m_TStart, delta), | ||
m_RangeEndMoveFun(variableRange.m_TEnd, delta)}; | ||||
r1302 | qCInfo(LOG_FuzzingOperations()).noquote() << "Performing" << m_Label << "on" | |||
<< variable->name() << "(from" << variableRange | ||||
<< "to" << newVariableRange << ")..."; | ||||
Alexandre Leroux
|
r1242 | variableController.onRequestDataLoading({variable}, newVariableRange, isSynchronized); | ||
Alexandre Leroux
|
r1221 | |||
Alexandre Leroux
|
r1242 | // Updates state | ||
fuzzingState.updateRanges(variableId, newVariableRange); | ||||
Alexandre Leroux
|
r1217 | } | ||
MoveFunction m_RangeStartMoveFun; | ||||
MoveFunction m_RangeEndMoveFun; | ||||
MaxMoveFunction m_MaxMoveFun; | ||||
QString m_Label; | ||||
}; | ||||
Alexandre Leroux
|
r1238 | struct SynchronizeOperation : public IFuzzingOperation { | ||
bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override | ||||
{ | ||||
Alexandre Leroux
|
r1239 | auto variable = fuzzingState.variableState(variableId).m_Variable; | ||
return variable != nullptr && !fuzzingState.m_SyncGroupsPool.empty() | ||||
&& fuzzingState.syncGroupId(variableId).isNull(); | ||||
Alexandre Leroux
|
r1238 | } | ||
void execute(VariableId variableId, FuzzingState &fuzzingState, | ||||
VariableController &variableController, const Properties &) const override | ||||
{ | ||||
Alexandre Leroux
|
r1240 | auto &variableState = fuzzingState.variableState(variableId); | ||
// Chooses a random synchronization group and adds the variable into sync group | ||||
auto syncGroupId = RandomGenerator::instance().randomChoice(fuzzingState.syncGroupsIds()); | ||||
r1302 | qCInfo(LOG_FuzzingOperations()).noquote() << "Adding" << variableState.m_Variable->name() | |||
<< "into synchronization group" << syncGroupId | ||||
<< "..."; | ||||
Alexandre Leroux
|
r1240 | variableController.onAddSynchronized(variableState.m_Variable, syncGroupId); | ||
// Updates state | ||||
fuzzingState.synchronizeVariable(variableId, syncGroupId); | ||||
Alexandre Leroux
|
r1328 | |||
variableController.onRequestDataLoading({variableState.m_Variable}, variableState.m_Range, | ||||
false); | ||||
Alexandre Leroux
|
r1238 | } | ||
}; | ||||
struct DesynchronizeOperation : public IFuzzingOperation { | ||||
bool canExecute(VariableId variableId, const FuzzingState &fuzzingState) const override | ||||
{ | ||||
Alexandre Leroux
|
r1239 | auto variable = fuzzingState.variableState(variableId).m_Variable; | ||
return variable != nullptr && !fuzzingState.syncGroupId(variableId).isNull(); | ||||
Alexandre Leroux
|
r1238 | } | ||
void execute(VariableId variableId, FuzzingState &fuzzingState, | ||||
VariableController &variableController, const Properties &) const override | ||||
{ | ||||
Alexandre Leroux
|
r1241 | auto &variableState = fuzzingState.variableState(variableId); | ||
// Gets the sync group of the variable | ||||
auto syncGroupId = fuzzingState.syncGroupId(variableId); | ||||
r1302 | qCInfo(LOG_FuzzingOperations()).noquote() << "Removing" << variableState.m_Variable->name() | |||
<< "from synchronization group" << syncGroupId | ||||
<< "..."; | ||||
Alexandre Leroux
|
r1328 | variableController.desynchronize(variableState.m_Variable, syncGroupId); | ||
Alexandre Leroux
|
r1241 | |||
// Updates state | ||||
fuzzingState.desynchronizeVariable(variableId, syncGroupId); | ||||
} | ||||
}; | ||||
Alexandre Leroux
|
r1202 | struct UnknownOperation : public IFuzzingOperation { | ||
Alexandre Leroux
|
r1236 | bool canExecute(VariableId, const FuzzingState &) const override { return false; } | ||
Alexandre Leroux
|
r1202 | |||
Alexandre Leroux
|
r1236 | void execute(VariableId, FuzzingState &, VariableController &, | ||
const Properties &) const override | ||||
Alexandre Leroux
|
r1211 | { | ||
Alexandre Leroux
|
r1202 | } | ||
}; | ||||
} // namespace | ||||
std::unique_ptr<IFuzzingOperation> FuzzingOperationFactory::create(FuzzingOperationType type) | ||||
{ | ||||
switch (type) { | ||||
Alexandre Leroux
|
r1211 | case FuzzingOperationType::CREATE: | ||
return std::make_unique<CreateOperation>(); | ||||
Alexandre Leroux
|
r1225 | case FuzzingOperationType::DELETE: | ||
return std::make_unique<DeleteOperation>(); | ||||
Alexandre Leroux
|
r1217 | case FuzzingOperationType::PAN_LEFT: | ||
return std::make_unique<MoveOperation>( | ||||
std::minus<double>(), std::minus<double>(), | ||||
[](const SqpRange &range, const SqpRange &maxRange) { | ||||
return range.m_TStart - maxRange.m_TStart; | ||||
}, | ||||
QStringLiteral("Pan left operation")); | ||||
case FuzzingOperationType::PAN_RIGHT: | ||||
return std::make_unique<MoveOperation>( | ||||
std::plus<double>(), std::plus<double>(), | ||||
[](const SqpRange &range, const SqpRange &maxRange) { | ||||
return maxRange.m_TEnd - range.m_TEnd; | ||||
}, | ||||
QStringLiteral("Pan right operation")); | ||||
case FuzzingOperationType::ZOOM_IN: | ||||
return std::make_unique<MoveOperation>( | ||||
std::plus<double>(), std::minus<double>(), | ||||
[](const SqpRange &range, const SqpRange &maxRange) { | ||||
Q_UNUSED(maxRange) | ||||
return range.m_TEnd - (range.m_TStart + range.m_TEnd) / 2.; | ||||
}, | ||||
QStringLiteral("Zoom in operation")); | ||||
case FuzzingOperationType::ZOOM_OUT: | ||||
return std::make_unique<MoveOperation>( | ||||
std::minus<double>(), std::plus<double>(), | ||||
[](const SqpRange &range, const SqpRange &maxRange) { | ||||
return std::min(range.m_TStart - maxRange.m_TStart, | ||||
maxRange.m_TEnd - range.m_TEnd); | ||||
}, | ||||
QStringLiteral("Zoom out operation")); | ||||
Alexandre Leroux
|
r1238 | case FuzzingOperationType::SYNCHRONIZE: | ||
return std::make_unique<SynchronizeOperation>(); | ||||
case FuzzingOperationType::DESYNCHRONIZE: | ||||
return std::make_unique<DesynchronizeOperation>(); | ||||
Alexandre Leroux
|
r1211 | default: | ||
// Default case returns unknown operation | ||||
break; | ||||
Alexandre Leroux
|
r1202 | } | ||
return std::make_unique<UnknownOperation>(); | ||||
} | ||||