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
@@ -1,5 +1,8 @@
package org.evomaster.client.java.controller.api.dto.database.execution;

import java.util.ArrayList;
import java.util.List;

/**
* Each time a Redis command is executed and returns no data, we keep track of which keys were involved,
* as well as relevant information such as the command type.
Expand All @@ -12,9 +15,9 @@ public class RedisFailedCommand {
public String command;

/**
* Key involved. Could be null if the command does not have a key in the arguments. For example: KEYS (pattern).
* Keys involved. Could be null if the command does not have any key in the arguments. For example: KEYS (pattern).
*/
public String key;
public List<String> keys;

/**
* Pattern involved. It'd only apply to commands with pattern like KEYS.
Expand All @@ -28,9 +31,9 @@ public class RedisFailedCommand {

public RedisFailedCommand() {}

public RedisFailedCommand(String command, String key, String pattern, String field) {
public RedisFailedCommand(String command, List<String> keys, String pattern, String field) {
this.command = command;
this.key = key;
this.keys = new ArrayList<>(keys);
this.pattern = pattern;
this.field = field;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,15 @@ public List<RedisCommandEvaluation> getEvaluatedRedisCommands() {

private void registerFailedCommand(RedisCommand redisCommand, double distance) {
RedisCommand.RedisCommandType type = redisCommand.getType();
if (distance > 0 &&
type.equals(GET) || type.equals(KEYS) || type.equals(HGET) || type.equals(HGETALL)) {
//For this first iteration we'll only work on GET commands.
if (distance > 0 && (
type.equals(GET) ||
type.equals(HGET) ||
type.equals(HGETALL) ||
type.equals(KEYS) ||
type.equals(SINTER) ||
type.equals(SMEMBERS))
) {
// Further commands will be registered in future iterations.
failedCommands.add(createFailedCommand(redisCommand));
}
}
Expand All @@ -117,13 +123,15 @@ private RedisFailedCommand createFailedCommand(RedisCommand redisCommand) {
List<String> args = redisCommand.extractArgs();
switch (type) {
case GET:
case HGETALL: {
case HGETALL:
case SINTER:
case SMEMBERS: {
if (args.isEmpty()) {
throw new IllegalArgumentException("Command " + type.getLabel() + " has invalid arguments.");
}
return new RedisFailedCommand(
type.getLabel().toUpperCase(),
args.get(0),
args,
null,
null);
}
Expand All @@ -134,7 +142,7 @@ private RedisFailedCommand createFailedCommand(RedisCommand redisCommand) {
}
return new RedisFailedCommand(
type.getLabel().toUpperCase(),
null,
Collections.emptyList(),
RedisUtils.redisPatternToRegex(args.get(0)),
null);
}
Expand All @@ -145,7 +153,7 @@ private RedisFailedCommand createFailedCommand(RedisCommand redisCommand) {
}
return new RedisFailedCommand(
type.getLabel().toUpperCase(),
args.get(0),
Collections.singletonList((args.get(0))),
null,
args.get(1));
}
Expand Down Expand Up @@ -207,8 +215,7 @@ private RedisKeyValueStore createRedisInfoForIntersection(List<String> commandKe
//The value for each one, since each key represents a SET, correspond to the members of that given set.
Map<String, RedisValueData> redisData = new HashMap<>();
keySet.forEach(
key -> redisData.put(key, new RedisValueData(redisClient.getSetMembers(key))
));
key -> redisData.put(key, new RedisValueData(redisClient.getSetMembers(key))));
return new RedisKeyValueStore(redisData);
}

Expand All @@ -219,8 +226,7 @@ private RedisKeyValueStore createRedisInfoForAllKeys(ReflectionBasedRedisClient
//No value is needed in this case.
Map<String, RedisValueData> redisData = new HashMap<>();
keys.forEach(
key -> redisData.put(key, null)
);
key -> redisData.put(key, null));
return new RedisKeyValueStore(redisData);
}

Expand All @@ -230,7 +236,8 @@ private RedisKeyValueStore createRedisInfoForKeysByType(String type, ReflectionB
//A Map structure is introduced here using the same keys that are stored in REDIS.
//No value is needed in this case.
Map<String, RedisValueData> redisData = new HashMap<>();
keys.forEach(key -> redisData.put(key, null));
keys.forEach(
key -> redisData.put(key, null));
return new RedisKeyValueStore(redisData);
}

Expand All @@ -240,7 +247,8 @@ private RedisKeyValueStore createRedisInfoForKeysByField(ReflectionBasedRedisCli
//A Map structure is introduced here using the same keys that are stored in REDIS.
//The value for each one, since each key is of type HASH, correspond to the fields stored for that given key.
Map<String, RedisValueData> redisData = new HashMap<>();
keys.forEach(key -> redisData.put(key, new RedisValueData(redisClient.getHashFields(key))));
keys.forEach(
key -> redisData.put(key, new RedisValueData(redisClient.getHashFields(key))));
return new RedisKeyValueStore(redisData);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public static RedisInsertionResultsDto executeInsert(
case "HSET":
client.hashSet(dto.key, dto.field, dto.value);
break;
case "SADD":
client.addMember(dto.key, dto.value);
break;
default:
throw new IllegalArgumentException(
"Unsupported Redis command: " + dto.command);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class ReflectionBasedRedisClient {
private static final String HGETALL_METHOD = "hgetall";
private static final String HSET_METHOD = "hset";
private static final String KEYS_METHOD = "keys";
private static final String SADD_METHOD = "sadd";
private static final String SELECT_METHOD = "select";
private static final String SET_METHOD = "set";
private static final String SHUTDOWN_METHOD = "shutdown";
Expand Down Expand Up @@ -117,6 +118,11 @@ public void hashSet(String key, String field, String value) {
invoke(HSET_METHOD, key, field, value);
}

/** SADD key */
public void addMember(String key, String member) {
invoke(SADD_METHOD, key, new String[]{member});
}

/** SMEMBERS key */
public Set<String> getSetMembers(String key) {
Object result = invoke(SMEMBERS_METHOD, key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@ public RedisStatementDsl hset(String key, String field, String value) {
return this;
}

@Override
public RedisStatementDsl sadd(String key, String member) {
checkDsl();
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Unspecified key");
}
if (member == null || member.isEmpty()) {
throw new IllegalArgumentException("Unspecified member");
}
RedisInsertionDto dto = new RedisInsertionDto();
dto.command = "SADD";
dto.key = key;
dto.value = member;
list.add(dto);
return this;
}

@Override
public RedisSequenceDsl and() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,13 @@ public interface RedisSequenceDsl {
* @return a statement object on which the sequence can be continued or closed
*/
RedisStatementDsl hset(String key, String field, String value);

/**
* A SADD operation on the Redis database.
*
* @param key the set key.
* @param member the new member in that set.
* @return a statement object on which the sequence can be continued or closed
*/
RedisStatementDsl sadd(String key, String member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.redis.lettuce.setintersectionnosave;

import com.redis.SwaggerConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableSwagger2
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class RedisLettuceSetIntersectionNoSaveApp extends SwaggerConfiguration {
public RedisLettuceSetIntersectionNoSaveApp() {
super("redislettucesetintersectionnosave");
}

public static void main(String[] args) {
SpringApplication.run(RedisLettuceSetIntersectionNoSaveApp.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.redis.lettuce.setintersectionnosave;

import com.redis.lettuce.AbstractRedisLettuceRest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Set;

@RestController
@RequestMapping(path = "/redislettucesetintersectionnosave")
public class RedisLettuceSetIntersectionNoSaveRest extends AbstractRedisLettuceRest {

@GetMapping("/set/variable-intersection/{set1}/{set2}")
public ResponseEntity<Void> getIntersection(@PathVariable String set1, @PathVariable String set2) {
Set<String> result = sync.sinter(set1, set2);
if (result != null && !result.isEmpty()) {
return ResponseEntity.status(200).build();
} else {
return ResponseEntity.status(404).build();
}
}

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.redis.lettuce.setmembersnosave;

import com.redis.SwaggerConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@EnableSwagger2
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class RedisLettuceSetMembersNoSaveApp extends SwaggerConfiguration {
public RedisLettuceSetMembersNoSaveApp() {
super("redislettucesetmembersnosave");
}

public static void main(String[] args) {
SpringApplication.run(RedisLettuceSetMembersNoSaveApp.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.redis.lettuce.setmembersnosave;

import com.redis.lettuce.AbstractRedisLettuceRest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Set;

@RestController
@RequestMapping(path = "/redislettucesetmembersnosave")
public class RedisLettuceSetMembersNoSaveRest extends AbstractRedisLettuceRest {

@GetMapping("/set/members/{key}")
public ResponseEntity<Void> getMembers(@PathVariable String key) {
Set<String> result = sync.smembers(key);
if (result != null && !result.isEmpty()) {
return ResponseEntity.status(200).build();
} else {
return ResponseEntity.status(404).build();
}
}

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.foo.spring.rest.redis.lettuce.setintersectionnosave;

import com.foo.spring.rest.redis.RedisController;
import com.redis.lettuce.setintersectionnosave.RedisLettuceSetIntersectionNoSaveApp;

public class RedisLettuceSetIntersectionNoSaveController extends RedisController {
public RedisLettuceSetIntersectionNoSaveController() {
super("lettuce", RedisLettuceSetIntersectionNoSaveApp.class);
}

@Override
public String getPackagePrefixesToCover() {
return "com.redis.lettuce.setintersectionnosave";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.foo.spring.rest.redis.lettuce.setmembersnosave;

import com.foo.spring.rest.redis.RedisController;
import com.redis.lettuce.setmembersnosave.RedisLettuceSetMembersNoSaveApp;

public class RedisLettuceSetMembersNoSaveController extends RedisController {
public RedisLettuceSetMembersNoSaveController() {
super("lettuce", RedisLettuceSetMembersNoSaveApp.class);
}

@Override
public String getPackagePrefixesToCover() {
return "com.redis.lettuce.setmembersnosave";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.evomaster.e2etests.spring.rest.redis.lettuce.setintersectionnosave;

import com.foo.spring.rest.redis.lettuce.setintersectionnosave.RedisLettuceSetIntersectionNoSaveController;
import org.evomaster.core.EMConfig;
import org.evomaster.core.problem.rest.data.HttpVerb;
import org.evomaster.core.problem.rest.data.RestIndividual;
import org.evomaster.core.search.Solution;
import org.evomaster.e2etests.utils.RestTestBase;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;

public class RedisLettuceSetIntersectionNoSaveEMTest extends RestTestBase {

@BeforeAll
public static void initClass() throws Exception {

EMConfig config = new EMConfig();
config.setInstrumentMR_REDIS(true);
RestTestBase.initClass(new RedisLettuceSetIntersectionNoSaveController(), config);
}

@Test
public void testSetIntersectionNoSaveEM() throws Throwable {

runTestHandlingFlakyAndCompilation(
"RedisLettuceSetIntersectionNoSaveEM",
"org.foo.spring.rest.redis.RedisLettuceSetIntersectionNoSaveEM",
2000,
true,
(args) -> {
setOption(args, "maxEvaluations", "10");
setOption(args, "heuristicsForRedis", "true");
setOption(args, "instrumentMR_REDIS", "true");
setOption(args, "extractRedisExecutionInfo", "true");
setOption(args, "generateRedisData", "true");

Solution<RestIndividual> solution = initAndRun(args);

assertFalse(solution.getIndividuals().isEmpty());
assertHasAtLeastOne(solution, HttpVerb.GET, 200, "/redislettucesetintersectionnosave/set/variable-intersection/{set1}/{set2}", null);
assertHasAtLeastOne(solution, HttpVerb.GET, 404, "/redislettucesetintersectionnosave/set/variable-intersection/{set1}/{set2}", null);

},
6);

}
}
Loading