Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Migrations/
/core-tests/e2e-tests/spring/spring-web/target/
/core-tests/e2e-tests/spring/spring-rest-mongo/target/
/core-tests/e2e-tests/spring/spring-rest-opensearch/target/
/core-tests/e2e-tests/spring/spring-rest-redis/target/
/core-tests/client-java/target/
/core-tests/client-java/dependencies/target/
/core-tests/client-java/sql-dto/target/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ public class ExtraHeuristicEntryDto implements Serializable {

/**
* The type of extra heuristic.
* Note: for the moment, we only have heuristics on SQL, MONGO and OPENSEARCH commands
* Note: for the moment, we only have heuristics on SQL, MONGO, OPENSEARCH and REDIS commands
*/
public enum Type {SQL, MONGO, OPENSEARCH}
public enum Type {SQL, MONGO, OPENSEARCH, REDIS}

/**
* Should we try to minimize or maximize the heuristic?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionDto;
import org.evomaster.client.java.controller.api.dto.database.operations.MongoInsertionResultsDto;
import org.evomaster.client.java.controller.api.dto.problem.rpc.ScheduleTaskInvocationResultDto;
import org.evomaster.client.java.controller.redis.ReflectionBasedRedisClient;
import org.evomaster.client.java.sql.DbCleaner;
import org.evomaster.client.java.sql.DbSpecification;

Expand Down Expand Up @@ -181,7 +182,7 @@ default void extractRPCSchema(){}

default Object getOpenSearchConnection() {return null;}

default Object getRedisConnection() {return null;}
default ReflectionBasedRedisClient getRedisConnection() {return null;}

/**
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ public Response runSut(SutRunDto dto, @Context HttpServletRequest httpServletReq
noKillSwitch(() -> sutController.registerOrExecuteInitSqlCommandsIfNeeded(true));
noKillSwitch(() -> sutController.initMongoHandler());
noKillSwitch(() -> sutController.initOpenSearchHandler());
noKillSwitch(() -> sutController.initRedisHandler());
} else {
//TODO as starting should be blocking, need to check
//if initialized, and wait if not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import org.evomaster.client.java.controller.api.dto.problem.rpc.*;
import org.evomaster.client.java.controller.api.dto.problem.rpc.RPCTestDto;
import org.evomaster.client.java.controller.internal.db.OpenSearchHandler;
import org.evomaster.client.java.controller.internal.db.redis.RedisHandler;
import org.evomaster.client.java.controller.redis.ReflectionBasedRedisClient;
import org.evomaster.client.java.sql.DbCleaner;
import org.evomaster.client.java.sql.SqlScriptRunner;
import org.evomaster.client.java.sql.SqlScriptRunnerCached;
Expand Down Expand Up @@ -90,6 +92,8 @@ public abstract class SutController implements SutHandler, CustomizationHandler

private final OpenSearchHandler openSearchHandler = new OpenSearchHandler();

private final RedisHandler redisHandler = new RedisHandler();

private Server controllerServer;

/**
Expand Down Expand Up @@ -346,6 +350,17 @@ public final void initOpenSearchHandler() {
}
}

public final void initRedisHandler() {
ReflectionBasedRedisClient connection = getRedisConnection();
redisHandler.setRedisClient(connection);

List<AdditionalInfo> list = getAdditionalInfoList();
if (!list.isEmpty()) {
AdditionalInfo last = list.get(list.size() - 1);
last.getRedisCommandData().forEach(redisHandler::handle);
}
}

/**
* TODO further handle multiple connections
* @return sql connection if there exists
Expand Down Expand Up @@ -388,7 +403,8 @@ public final ExtraHeuristicsDto computeExtraHeuristics(boolean queryFromDatabase

ExtraHeuristicsDto dto = new ExtraHeuristicsDto();

if (isSQLHeuristicsComputationAllowed() || isMongoHeuristicsComputationAllowed() || isOpenSearchHeuristicsComputationAllowed()) {
if (isSQLHeuristicsComputationAllowed() || isMongoHeuristicsComputationAllowed()
|| isOpenSearchHeuristicsComputationAllowed() || isRedisHeuristicsComputationAllowed()) {
List<AdditionalInfo> additionalInfoList = getAdditionalInfoList();

if (isSQLHeuristicsComputationAllowed()) {
Expand All @@ -397,10 +413,12 @@ public final ExtraHeuristicsDto computeExtraHeuristics(boolean queryFromDatabase
if (isMongoHeuristicsComputationAllowed()) {
computeMongoHeuristics(dto, additionalInfoList);
}

if (isOpenSearchHeuristicsComputationAllowed()) {
computeOpenSearchHeuristics(dto, additionalInfoList);
}
if (isRedisHeuristicsComputationAllowed()) {
computeRedisHeuristics(dto, additionalInfoList);
}
}
return dto;
}
Expand All @@ -417,6 +435,10 @@ private boolean isOpenSearchHeuristicsComputationAllowed() {
return openSearchHandler.isCalculateHeuristics();
}

private boolean isRedisHeuristicsComputationAllowed() {
return redisHandler.isCalculateHeuristics();
}

private void computeSQLHeuristics(ExtraHeuristicsDto dto, List<AdditionalInfo> additionalInfoList, boolean queryFromDatabase) {
/*
TODO refactor, once we move SQL analysis into Core
Expand Down Expand Up @@ -528,6 +550,34 @@ public final void computeOpenSearchHeuristics(ExtraHeuristicsDto dto, List<Addit

}

public final void computeRedisHeuristics(ExtraHeuristicsDto dto, List<AdditionalInfo> additionalInfoList){
if(redisHandler.isCalculateHeuristics()){
if(!additionalInfoList.isEmpty()) {
AdditionalInfo last = additionalInfoList.get(additionalInfoList.size() - 1);
last.getRedisCommandData().forEach(it -> {
try {
redisHandler.handle(it);
} catch (Exception e){
SimpleLogger.error("FAILED TO HANDLE REDIS COMMAND", e);
assert false;
}
});
}

redisHandler.getEvaluatedRedisCommands().stream()
.map(p ->
new ExtraHeuristicEntryDto(
ExtraHeuristicEntryDto.Type.REDIS,
ExtraHeuristicEntryDto.Objective.MINIMIZE_TO_ZERO,
p.getRedisCommand().toString(),
p.getRedisDistanceWithMetrics().getDistance(),
p.getRedisDistanceWithMetrics().getNumberOfEvaluatedKeys(),
false
))
.forEach(h -> dto.heuristics.add(h));
}
}

/**
* handle specified init sql script after SUT is started.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.evomaster.client.java.controller.internal.db.redis;

import org.evomaster.client.java.controller.internal.TaintHandlerExecutionTracer;
import org.evomaster.client.java.controller.redis.RedisClient;
import org.evomaster.client.java.controller.redis.ReflectionBasedRedisClient;
import org.evomaster.client.java.controller.redis.RedisHeuristicsCalculator;
import org.evomaster.client.java.controller.redis.RedisInfo;
import org.evomaster.client.java.instrumentation.RedisCommand;
Expand Down Expand Up @@ -32,14 +32,9 @@ public class RedisHandler {
private volatile boolean calculateHeuristics;

/**
* Whether to use execution's info or not
* The client must be created given both host and port for Redis DB.
*/
private volatile boolean extractRedisExecution;

/**
* The client must be created through a connection factory.
*/
private RedisClient redisClient = null;
private ReflectionBasedRedisClient redisClient = null;

private final RedisHeuristicsCalculator calculator = new RedisHeuristicsCalculator(new TaintHandlerExecutionTracer());

Expand All @@ -49,7 +44,6 @@ public class RedisHandler {

public RedisHandler() {
operations = new ArrayList<>();
extractRedisExecution = true;
calculateHeuristics = true;
}

Expand All @@ -62,22 +56,12 @@ public boolean isCalculateHeuristics() {
return calculateHeuristics;
}

public boolean isExtractRedisExecution() {
return extractRedisExecution;
}

public void setCalculateHeuristics(boolean calculateHeuristics) {
this.calculateHeuristics = calculateHeuristics;
}

public void setExtractRedisExecution(boolean extractRedisExecution) {
this.extractRedisExecution = extractRedisExecution;
}

public void handle(RedisCommand info) {
if (extractRedisExecution) {
operations.add(info);
}
operations.add(info);
}

public List<RedisCommandEvaluation> getEvaluatedRedisCommands() {
Expand All @@ -92,7 +76,7 @@ public List<RedisCommandEvaluation> getEvaluatedRedisCommands() {
return evaluatedRedisCommands;
}

private RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, RedisClient redisClient) {
private RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, ReflectionBasedRedisClient redisClient) {
RedisCommand.RedisCommandType type = redisCommand.getType();
try {
switch (type) {
Expand Down Expand Up @@ -138,15 +122,15 @@ private RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, Redi
}
}

private List<RedisInfo> createRedisInfoForIntersection(List<String> keys, RedisClient redisClient) {
private List<RedisInfo> createRedisInfoForIntersection(List<String> keys, ReflectionBasedRedisClient redisClient) {
List<RedisInfo> redisData = new ArrayList<>();
keys.forEach(
key -> redisData.add(new RedisInfo(key, redisClient.getType(key), redisClient.getSetMembers(key))
));
return redisData;
}

private List<RedisInfo> createRedisInfoForAllKeys(RedisClient redisClient) {
private List<RedisInfo> createRedisInfoForAllKeys(ReflectionBasedRedisClient redisClient) {
Set<String> keys = redisClient.getAllKeys();
List<RedisInfo> redisData = new ArrayList<>();
keys.forEach(
Expand All @@ -155,21 +139,21 @@ private List<RedisInfo> createRedisInfoForAllKeys(RedisClient redisClient) {
return redisData;
}

private List<RedisInfo> createRedisInfoForKeysByType(String type, RedisClient redisClient) {
private List<RedisInfo> createRedisInfoForKeysByType(String type, ReflectionBasedRedisClient redisClient) {
Set<String> keys = redisClient.getKeysByType(type);
List<RedisInfo> redisData = new ArrayList<>();
keys.forEach(key -> redisData.add(new RedisInfo(key)));
return redisData;
}

private List<RedisInfo> createRedisInfoForKeysByField(String field, RedisClient redisClient) {
private List<RedisInfo> createRedisInfoForKeysByField(String field, ReflectionBasedRedisClient redisClient) {
Set<String> keys = redisClient.getKeysByType(REDIS_HASH_TYPE);
List<RedisInfo> redisData = new ArrayList<>();
keys.forEach(key -> redisData.add(new RedisInfo(key, redisClient.hashFieldExists(key, field))));
keys.forEach(key -> redisData.add(new RedisInfo(key, redisClient.getHashFields(key))));
return redisData;
}

public void setRedisClient(RedisClient redisClient) {
public void setRedisClient(ReflectionBasedRedisClient redisClient) {
this.redisClient = redisClient;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, List<

case HGET: {
String key = redisCommand.extractArgs().get(0);
return calculateDistanceForFieldInHash(key, redisInfo);
String field = redisCommand.extractArgs().get(1);
return calculateDistanceForFieldInHash(key, field, redisInfo);
}

case SINTER: {
Expand Down Expand Up @@ -89,8 +90,12 @@ private RedisDistanceWithMetrics calculateDistanceForPattern(
return new RedisDistanceWithMetrics(MAX_REDIS_DISTANCE, 0);
}
for (RedisInfo k : keys) {
String key = k.getKey();
double d = TruthnessUtils.normalizeValue(
RegexDistanceUtils.getStandardDistance(k.getKey(), redisPatternToRegex(pattern)));
RegexDistanceUtils.getStandardDistance(key, regex));
if (taintHandler != null) {
taintHandler.handleTaintForRegex(key, regex);
}
minDist = Math.min(minDist, d);
eval++;
if (d == 0) return new RedisDistanceWithMetrics(0, eval);
Expand Down Expand Up @@ -119,8 +124,12 @@ private RedisDistanceWithMetrics calculateDistanceForKeyMatch(

for (RedisInfo k : candidateKeys) {
try {
long rawDist = DistanceHelper.getLeftAlignmentDistance(targetKey, k.getKey());
String key = k.getKey();
long rawDist = DistanceHelper.getLeftAlignmentDistance(targetKey, key);
double normDist = TruthnessUtils.normalizeValue(rawDist);
if (taintHandler != null) {
taintHandler.handleTaintForStringEquals(targetKey, key, false);
}
minDist = Math.min(minDist, normDist);
evaluated++;

Expand All @@ -144,6 +153,7 @@ private RedisDistanceWithMetrics calculateDistanceForKeyMatch(
*/
private RedisDistanceWithMetrics calculateDistanceForFieldInHash(
String targetKey,
String targetField,
List<RedisInfo> keys
) {
if (keys.isEmpty()) {
Expand All @@ -155,12 +165,13 @@ private RedisDistanceWithMetrics calculateDistanceForFieldInHash(

for (RedisInfo k : keys) {
try {
long keyDist = DistanceHelper.getLeftAlignmentDistance(targetKey, k.getKey());

double fieldDist = k.hasField() ? 0d : MAX_REDIS_DISTANCE;

String key = k.getKey();
long keyDist = DistanceHelper.getLeftAlignmentDistance(targetKey, key);
double fieldDist = calculateDistanceForField(targetField, k.getFields().keySet());
double combined = TruthnessUtils.normalizeValue(keyDist + fieldDist);

if (taintHandler != null) {
taintHandler.handleTaintForStringEquals(targetKey, key, false);
}
minDist = Math.min(minDist, combined);
evaluated++;

Expand All @@ -175,6 +186,34 @@ private RedisDistanceWithMetrics calculateDistanceForFieldInHash(
return new RedisDistanceWithMetrics(minDist, evaluated);
}

/**
* Computes the distance of target field to each field in hash.
*
* @param targetField Field searched in query.
* @param fields Fields in hash.
* @return double
*/
private double calculateDistanceForField(String targetField, Set<String> fields) {
if (fields.isEmpty()) {
return Double.MAX_VALUE;
}

double minDist = Double.MAX_VALUE;

for (String field : fields) {
try {
long fieldDist = DistanceHelper.getLeftAlignmentDistance(targetField, field);
if (taintHandler != null) {
taintHandler.handleTaintForStringEquals(targetField, field, false);
}
minDist = Math.min(minDist, fieldDist);
} catch (Exception ex) {
SimpleLogger.uniqueWarn("Failed FIELD distance on " + targetField + ": " + ex.getMessage());
}
}
return minDist;
}

/**
* Computes the distance of a given intersection considering the keys for the given sets.
*
Expand Down Expand Up @@ -237,6 +276,9 @@ private double computeSetIntersectionDistance(Set<String> s1, Set<String> s2) {
for (String a : s1) {
for (String b : s2) {
long raw = DistanceHelper.getLeftAlignmentDistance(a, b);
if (taintHandler != null) {
taintHandler.handleTaintForStringEquals(a, b, false);
}
double norm = TruthnessUtils.normalizeValue(raw);
min = Math.min(min, norm);
if (min == 0) return 0;
Expand Down
Loading