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
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.evomaster.client.java.controller.internal.TaintHandlerExecutionTracer;
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.controller.redis.RedisValueData;
import org.evomaster.client.java.instrumentation.RedisCommand;
import org.evomaster.client.java.utils.SimpleLogger;

Expand Down Expand Up @@ -82,35 +82,35 @@ private RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, Refl
switch (type) {
case KEYS:
case EXISTS: {
List<RedisInfo> redisInfo = createRedisInfoForAllKeys(redisClient);
return calculator.computeDistance(redisCommand, redisInfo);
Map<String, RedisValueData> redisValueData = createRedisInfoForAllKeys(redisClient);
return calculator.computeDistance(redisCommand, redisValueData);
}

case GET: {
List<RedisInfo> redisInfo = createRedisInfoForKeysByType(REDIS_STRING_TYPE, redisClient);
return calculator.computeDistance(redisCommand, redisInfo);
Map<String, RedisValueData> redisValueData = createRedisInfoForKeysByType(REDIS_STRING_TYPE, redisClient);
return calculator.computeDistance(redisCommand, redisValueData);
}

case HGET: {
String field = redisCommand.extractArgs().get(1);
List<RedisInfo> redisInfo = createRedisInfoForKeysByField(field, redisClient);
return calculator.computeDistance(redisCommand, redisInfo);
Map<String, RedisValueData> redisValueData = createRedisInfoForKeysByField(redisClient);
return calculator.computeDistance(redisCommand, redisValueData);
}

case HGETALL: {
List<RedisInfo> redisInfo = createRedisInfoForKeysByType(REDIS_HASH_TYPE, redisClient);
return calculator.computeDistance(redisCommand, redisInfo);
Map<String, RedisValueData> redisValueData = createRedisInfoForKeysByType(REDIS_HASH_TYPE, redisClient);
return calculator.computeDistance(redisCommand, redisValueData);
}

case SMEMBERS: {
List<RedisInfo> redisInfo = createRedisInfoForKeysByType(REDIS_SET_TYPE, redisClient);
return calculator.computeDistance(redisCommand, redisInfo);
Map<String, RedisValueData> redisValueData = createRedisInfoForKeysByType(REDIS_SET_TYPE, redisClient);
return calculator.computeDistance(redisCommand, redisValueData);
}

case SINTER: {
List<String> keys = redisCommand.extractArgs();
List<RedisInfo> redisInfo = createRedisInfoForIntersection(keys, redisClient);
return calculator.computeDistance(redisCommand, redisInfo);
Map<String, RedisValueData> redisValueData = createRedisInfoForIntersection(keys, redisClient);
return calculator.computeDistance(redisCommand, redisValueData);
}

default:
Expand All @@ -122,34 +122,36 @@ private RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, Refl
}
}

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))
private Map<String, RedisValueData> createRedisInfoForIntersection(List<String> commandKeys, ReflectionBasedRedisClient redisClient) {
Set<String> keySet = redisClient.getKeysByType(REDIS_SET_TYPE);

Map<String, RedisValueData> redisData = new HashMap<>();
keySet.forEach(
key -> redisData.put(key, new RedisValueData(redisClient.getSetMembers(key))
));
return redisData;
}

private List<RedisInfo> createRedisInfoForAllKeys(ReflectionBasedRedisClient redisClient) {
private Map<String, RedisValueData> createRedisInfoForAllKeys(ReflectionBasedRedisClient redisClient) {
Set<String> keys = redisClient.getAllKeys();
List<RedisInfo> redisData = new ArrayList<>();
Map<String, RedisValueData> redisData = new HashMap<>();
keys.forEach(
key -> redisData.add(new RedisInfo(key))
key -> redisData.put(key, null)
);
return redisData;
}

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.evomaster.client.java.utils.SimpleLogger;

import java.util.*;
import java.util.stream.Collectors;

import static org.evomaster.client.java.controller.redis.RedisUtils.redisPatternToRegex;

Expand All @@ -31,34 +32,35 @@ public RedisHeuristicsCalculator(TaintHandler taintHandler) {
* Dispatches the computation based on the command keyword (type).
*
* @param redisCommand Redis command.
* @param redisInfo Redis data in a generic structure.
* @param redisValueData Redis data in a generic structure.
* @return RedisDistanceWithMetrics
*/
public RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, List<RedisInfo> redisInfo) {
public RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand,
Map<String, RedisValueData> redisValueData) {
RedisCommand.RedisCommandType type = redisCommand.getType();
try {
switch (type) {
case KEYS: {
String pattern = redisCommand.extractArgs().get(0);
return calculateDistanceForPattern(pattern, redisInfo);
return calculateDistanceForPattern(pattern, redisValueData);
}

case EXISTS:
case GET:
case HGETALL:
case SMEMBERS: {
String target = redisCommand.extractArgs().get(0);
return calculateDistanceForKeyMatch(target, redisInfo);
return calculateDistanceForKeyMatch(target, redisValueData);
}

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

case SINTER: {
return calculateDistanceForIntersection(redisInfo);
return calculateDistanceForIntersection(redisCommand.extractArgs(), redisValueData);
}

default:
Expand All @@ -79,7 +81,7 @@ public RedisDistanceWithMetrics computeDistance(RedisCommand redisCommand, List<
*/
private RedisDistanceWithMetrics calculateDistanceForPattern(
String pattern,
List<RedisInfo> keys) {
Map<String, RedisValueData> keys) {
double minDist = MAX_REDIS_DISTANCE;
int eval = 0;
String regex;
Expand All @@ -89,8 +91,7 @@ private RedisDistanceWithMetrics calculateDistanceForPattern(
SimpleLogger.uniqueWarn("Invalid Redis pattern. Cannot compute regex for: " + pattern);
return new RedisDistanceWithMetrics(MAX_REDIS_DISTANCE, 0);
}
for (RedisInfo k : keys) {
String key = k.getKey();
for (String key : keys.keySet()) {
double d = TruthnessUtils.normalizeValue(
RegexDistanceUtils.getStandardDistance(key, regex));
if (taintHandler != null) {
Expand All @@ -113,7 +114,7 @@ private RedisDistanceWithMetrics calculateDistanceForPattern(
*/
private RedisDistanceWithMetrics calculateDistanceForKeyMatch(
String targetKey,
List<RedisInfo> candidateKeys
Map<String, RedisValueData> candidateKeys
) {
if (candidateKeys.isEmpty()) {
return new RedisDistanceWithMetrics(MAX_REDIS_DISTANCE, 0);
Expand All @@ -122,9 +123,8 @@ private RedisDistanceWithMetrics calculateDistanceForKeyMatch(
double minDist = MAX_REDIS_DISTANCE;
int evaluated = 0;

for (RedisInfo k : candidateKeys) {
for (String key : candidateKeys.keySet()) {
try {
String key = k.getKey();
long rawDist = DistanceHelper.getLeftAlignmentDistance(targetKey, key);
double normDist = TruthnessUtils.normalizeValue(rawDist);
if (taintHandler != null) {
Expand All @@ -137,7 +137,7 @@ private RedisDistanceWithMetrics calculateDistanceForKeyMatch(
return new RedisDistanceWithMetrics(0, evaluated);
}
} catch (Exception ex) {
SimpleLogger.uniqueWarn("Failed to compute distance for key " + k + ": " + ex.getMessage());
SimpleLogger.uniqueWarn("Failed to compute distance for key " + key + ": " + ex.getMessage());
}
}

Expand All @@ -154,7 +154,7 @@ private RedisDistanceWithMetrics calculateDistanceForKeyMatch(
private RedisDistanceWithMetrics calculateDistanceForFieldInHash(
String targetKey,
String targetField,
List<RedisInfo> keys
Map<String, RedisValueData> keys
) {
if (keys.isEmpty()) {
return new RedisDistanceWithMetrics(MAX_REDIS_DISTANCE, 0);
Expand All @@ -163,11 +163,10 @@ private RedisDistanceWithMetrics calculateDistanceForFieldInHash(
double minDist = MAX_REDIS_DISTANCE;
int evaluated = 0;

for (RedisInfo k : keys) {
for (String key : keys.keySet()) {
try {
String key = k.getKey();
long keyDist = DistanceHelper.getLeftAlignmentDistance(targetKey, key);
double fieldDist = calculateDistanceForField(targetField, k.getFields().keySet());
double fieldDist = calculateDistanceForField(targetField, keys.get(key).getFields().keySet());
double combined = TruthnessUtils.normalizeValue(keyDist + fieldDist);
if (taintHandler != null) {
taintHandler.handleTaintForStringEquals(targetKey, key, false);
Expand All @@ -179,7 +178,7 @@ private RedisDistanceWithMetrics calculateDistanceForFieldInHash(
return new RedisDistanceWithMetrics(0, evaluated);
}
} catch (Exception ex) {
SimpleLogger.uniqueWarn("Failed HGET distance on " + k + ": " + ex.getMessage());
SimpleLogger.uniqueWarn("Failed HGET distance on " + key + ": " + ex.getMessage());
}
}

Expand Down Expand Up @@ -216,37 +215,72 @@ private double calculateDistanceForField(String targetField, Set<String> fields)

/**
* Computes the distance of a given intersection considering the keys for the given sets.
* Distance would be a function considering whether the keys are valid sets existing in Redis
* and whether the successive intersections return elements in common.
*
* @param keys Set keys for the intersection
* @param commandArgs List of keys for the intersection
* @param storedKeys Set keys stored in Redis
* @return RedisDistanceWithMetrics
*/
private RedisDistanceWithMetrics calculateDistanceForIntersection(
List<RedisInfo> keys
List<String> commandArgs,
Map<String, RedisValueData> storedKeys
) {
if (keys == null || keys.isEmpty()) {
if (storedKeys == null || storedKeys.isEmpty()) {
return new RedisDistanceWithMetrics(MAX_REDIS_DISTANCE, 0);
}

return new RedisDistanceWithMetrics(TruthnessUtils.normalizeValue(
computeDistanceForKeysInArgs(commandArgs, storedKeys) + computeIntersectionDistanceForArgs(commandArgs, storedKeys)
), storedKeys.size());
}

/**
* Computes the distance for the list of keys of each one existing in Redis.
*
* @param commandArgs List of keys for the intersection
* @param storedKeys Set keys stored in Redis
* @return RedisDistanceWithMetrics
*/
private double computeDistanceForKeysInArgs(
List<String> commandArgs,
Map<String, RedisValueData> storedKeys
) {
int numberOfCommandKeys = commandArgs.size();
double sum = 0d;
for (String arg : commandArgs) {
sum += calculateDistanceForKeyMatch(arg, storedKeys).getDistance();
}
return sum / numberOfCommandKeys;
}

/**
* Computes the distance of a given intersection considering the keys present in the command args.
*
* @param commandArgs List of keys for the intersection
* @param storedSets Set keys stored in Redis
* @return RedisDistanceWithMetrics
*/
private double computeIntersectionDistanceForArgs(
List<String> commandArgs,
Map<String, RedisValueData> storedSets
) {

List<Set<String>> membersInCommandArgsSets = commandArgs.stream()
.map(key -> storedSets.getOrDefault(key, new RedisValueData(new HashSet<>())).getMembers())
.collect(Collectors.toList());

double total = 0d;
int evaluated = 0;

Set<String> currentIntersection = null;

for (int i = 0; i < keys.size(); i++) {
RedisInfo k = keys.get(i);
String type = k.getType();
if (!"set".equalsIgnoreCase(type)) {
return new RedisDistanceWithMetrics(MAX_REDIS_DISTANCE, evaluated);
}

Set<String> set = k.getMembers();
if (set == null) set = Collections.emptySet();
for (int i = 0; i < membersInCommandArgsSets.size(); i++) {
Set<String> set = membersInCommandArgsSets.get(i);

if (i == 0) {
currentIntersection = new HashSet<>(set);
double d0 = currentIntersection.isEmpty() ? MAX_REDIS_DISTANCE : 0d;
total += d0;
evaluated++;
} else {
Set<String> newIntersection = new HashSet<>(currentIntersection);
newIntersection.retainAll(set);
Expand All @@ -256,12 +290,11 @@ private RedisDistanceWithMetrics calculateDistanceForIntersection(
: 0d;

total += di;
evaluated++;
currentIntersection = newIntersection;
}
}

return new RedisDistanceWithMetrics(total / keys.size(), evaluated);
return total / membersInCommandArgsSets.size();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,18 @@
* Hence, RedisHeuristicCalculator will be decoupled from Redis.
* There'll be no need to call Redis to calculate distances.
*/
public class RedisInfo {
private String key;
private String type;
public class RedisValueData {
private Map<String, String> fields;
private Set<String> members;

public RedisInfo(String key) {
this.key = key;
}

public RedisInfo(String key, Map<String, String> fields) {
this.key = key;
public RedisValueData(Map<String, String> fields) {
this.fields = fields;
}

public RedisInfo(String key, String type, Set<String> members) {
this.key = key;
this.type = type;
public RedisValueData(Set<String> members) {
this.members = members;
}

public String getKey() {
return key;
}

public String getType() {
return type;
}

public Set<String> getMembers() {
return members;
Expand Down
Loading