TEST simple_concurrency() { delete_file(); rliteContext *context1 = rliteConnect(FILEPATH, 0); rliteContext *context2 = rliteConnect(FILEPATH, 0); rliteReply* reply; size_t argvlen[100]; { char* argv[100] = {"GET", "key", NULL}; reply = rliteCommandArgv(context1, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_NIL(reply); rliteFreeReplyObject(reply); } { char* argv[100] = {"set", "key", "value", NULL}; reply = rliteCommandArgv(context2, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_STATUS(reply, "OK", 2); rliteFreeReplyObject(reply); } { char* argv[100] = {"GET", "key", NULL}; reply = rliteCommandArgv(context1, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_STR(reply, "value", 5); rliteFreeReplyObject(reply); } rliteFree(context1); rliteFree(context2); unlink(FILEPATH); PASS(); }
TEST test_sdiffstore() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; char *m1 = "mymember", *m2 = "member2", *s1 = "myset", *s2 = "myset2", *t = "target"; sadd(context, s1, m1); sadd(context, s1, m2); sadd(context, s2, m1); sadd(context, s2, "meh"); rliteReply* reply; { char* argv[100] = {"sdiffstore", t, s1, s2, NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 1); rliteFreeReplyObject(reply); } { char* argv[100] = {"smembers", t, NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_LEN(reply, 1); EXPECT_REPLY_STR(reply->element[0], m2, strlen(m2)); rliteFreeReplyObject(reply); } rliteFree(context); return 0; }
TEST test_scard() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; if (randomSadd(context, "myset", 5) != 0) { return 1; } rliteReply* reply; char* argv[100] = {"scard", "myset", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 5); rliteFreeReplyObject(reply); if (randomSadd(context, "myset", 3) != 0) { return 1; } char* argv2[100] = {"scard", "myset", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv2, argvlen), argv2, argvlen); EXPECT_REPLY_INTEGER(reply, 8); rliteFreeReplyObject(reply); rliteFree(context); return 0; }
TEST test_sort_alpha() { rliteContext *context = rliteConnect(":memory:", 0); rliteReply* reply; size_t argvlen[100]; { char* argv[100] = {"sadd", "key", "31", "12", "41", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 3); rliteFreeReplyObject(reply); } { char* argv[100] = {"sort", "key", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_LEN(reply, 3); EXPECT_REPLY_STR(reply->element[0], "12", 2); EXPECT_REPLY_STR(reply->element[1], "31", 2); EXPECT_REPLY_STR(reply->element[2], "41", 2); rliteFreeReplyObject(reply); } rliteFree(context); PASS(); }
TEST test_sort_get_pattern_get_pound() { rliteContext *context = rliteConnect(":memory:", 0); rliteReply* reply; size_t argvlen[100]; { char* argv[100] = {"rpush", "key", "3", "1", "2", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 3); rliteFreeReplyObject(reply); } { char* argv[100] = {"sort", "key", "GET", "#", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_LEN(reply, 3); EXPECT_REPLY_STR(reply->element[0], "1", 1); EXPECT_REPLY_STR(reply->element[1], "2", 1); EXPECT_REPLY_STR(reply->element[2], "3", 1); rliteFreeReplyObject(reply); } rliteFree(context); PASS(); }
TEST test_srem() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; char *m1 = "mymember", *m2 = "member2"; sadd(context, "myset", m1); sadd(context, "myset", m2); rliteReply* reply; { char* argv[100] = {"srem", "myset", m1, "other", m2, NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 2); rliteFreeReplyObject(reply); } { char* argv[100] = {"exists", "mykey", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 0); rliteFreeReplyObject(reply); } rliteFree(context); return 0; }
TEST threads_concurrency() { delete_file(); rliteContext *context = rliteConnect(FILEPATH, 0); pthread_t thread; pthread_create(&thread, NULL, increment, NULL); rliteReply* reply; size_t argvlen[100]; char* argv[100] = {"GET", "key", NULL}; int argc = populateArgvlen(argv, argvlen); long long val; char tmp[40]; do { reply = rliteCommandArgv(context, argc, argv, argvlen); if (reply->type == RLITE_REPLY_NIL) { val = 0; } else if (reply->type != RLITE_REPLY_STRING) { fprintf(stderr, "Expected incremented value to be a string, got %d instead on line %d\n", reply->type, __LINE__); rliteFreeReplyObject(reply); break; } else { memcpy(tmp, reply->str, reply->len); tmp[reply->len] = 0; val = strtoll(tmp, NULL, 10); } rliteFreeReplyObject(reply); } while (val < INCREMENT_LIMIT); rliteFree(context); pthread_join(thread, NULL); PASS(); }
TEST test_echo_oom() { size_t argvlen[2]; char* argv[2]; rliteReply* reply; argvlen[0] = 4; argv[0] = "echo"; argvlen[1] = 11; argv[1] = "hello world"; rliteContext *context = rliteConnect(":memory:", 0); int i; for (i = 1;;i++) { test_mode = 1; test_mode_counter = i; reply = rliteCommandArgv(context, 2, argv, argvlen); if (reply != NULL) { if (i == 1) { fprintf(stderr, "No OOM triggered\n"); test_mode = 0; FAIL(); } EXPECT_REPLY_STR(reply, argv[1], argvlen[1]); break; } } test_mode = 0; rliteFreeReplyObject(reply); rliteFree(context); PASS(); }
TEST test_ping_oom() { size_t argvlen[2]; char* argv[2]; rliteReply* reply; argvlen[0] = 4; argv[0] = "PING"; rliteContext *context = rliteConnect(":memory:", 0); int i; for (i = 1;;i++) { test_mode = 1; test_mode_counter = i; reply = rliteCommandArgv(context, 1, argv, argvlen); if (reply != NULL) { if (i == 1) { fprintf(stderr, "No OOM triggered\n"); test_mode = 0; FAIL(); } EXPECT_REPLY_STATUS(reply, "PONG", 4); break; } } test_mode = 0; rliteFreeReplyObject(reply); rliteFree(context); PASS(); }
TEST test_echo_wrong_arity_oom() { size_t argvlen[2]; char* argv[2]; rliteReply* reply; argvlen[0] = 4; argv[0] = "echo"; rliteContext *context = rliteConnect(":memory:", 0); int i; for (i = 1;;i++) { test_mode = 1; test_mode_counter = i; reply = rliteCommandArgv(context, 1, argv, argvlen); if (reply != NULL) { if (i == 1) { fprintf(stderr, "No OOM triggered\n"); test_mode = 0; FAIL(); } EXPECT_REPLY_ERROR(reply); const char *err = "wrong number of arguments for 'echo' command"; ASSERT_EQ(memcmp(reply->str, err, strlen(err)), 0); break; } } test_mode = 0; rliteFreeReplyObject(reply); rliteFree(context); PASS(); }
static void sadd(rliteContext* context, char *key, char *element) { rliteReply* reply; size_t argvlen[100]; char* argv[100] = {"sadd", key, element, NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); rliteFreeReplyObject(reply); }
TEST test_srandmember_10_non_unique() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; long i; char *m1 = "mymember", *m2 = "member2"; sadd(context, "myset", m1); sadd(context, "myset", m2); rliteReply* reply; { char* argv[100] = {"srandmember", "myset", "-10", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_LEN(reply, 10); for (i = 0; i < 10; i++) { if (!(reply->element[i]->len == (int)strlen(m1) && memcmp(reply->element[i]->str, m1, reply->element[i]->len) == 0) && !(reply->element[i]->len == (int)strlen(m2) && memcmp(reply->element[i]->str, m2, reply->element[i]->len) == 0)) { FAIL(); } } rliteFreeReplyObject(reply); } rliteFree(context); return 0; }
static int randomSadd(rliteContext* context, char *key, int elements) { rliteReply* reply; if (elements == 0) { elements = 3; } char* argv[100] = {"sadd", key, NULL}; int i, j, len; for (i = 0; i < elements; i++) { len = 5 + floor(((float)rand() / RAND_MAX) * 10); argv[2 + i] = malloc(sizeof(char) * len); for (j = 0; j < len - 1; j++) { argv[2 + i][j] = 'a' + (int)floor(((float)rand() / RAND_MAX) * 25); } argv[2 + i][len - 1] = 0; } size_t argvlen[100]; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, elements); rliteFreeReplyObject(reply); for (i = 0; i < elements; i++) { free(argv[2 + i]); } return 0; }
TEST test_not_null_terminated_long() { size_t argvlen[4]; char* argv[4]; rliteReply* reply; argvlen[0] = 6; argv[0] = "lrange"; argvlen[1] = 3; argv[1] = "key"; argvlen[2] = 1; argv[2] = "0"; argvlen[3] = 2; argv[3] = malloc(sizeof(char) * 4); argv[3][0] = '-'; argv[3][1] = '1'; argv[3][2] = 'z'; rliteContext *context = rliteConnect(":memory:", 0); reply = rliteCommandArgv(context, 4, argv, argvlen); ASSERT_EQ(reply->type, RLITE_REPLY_ARRAY); rliteFreeReplyObject(reply); rliteFree(context); free(argv[3]); PASS(); }
TEST test_sortby_hash() { rliteContext *context = rliteConnect(":memory:", 0); rliteReply* reply; size_t argvlen[100]; { char* argv[100] = {"hset", "wobj_a", "weight", "1", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 1); rliteFreeReplyObject(reply); } { char* argv[100] = {"hset", "wobj_b", "weight", "2", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 1); rliteFreeReplyObject(reply); } { char* argv[100] = {"hset", "wobj_c", "weight", "3", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 1); rliteFreeReplyObject(reply); } { char* argv[100] = {"rpush", "key", "b", "a", "c", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 3); rliteFreeReplyObject(reply); } { char* argv[100] = {"sort", "key", "by", "wobj_*->weight", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_LEN(reply, 3); EXPECT_REPLY_STR(reply->element[0], "a", 1); EXPECT_REPLY_STR(reply->element[1], "b", 1); EXPECT_REPLY_STR(reply->element[2], "c", 1); rliteFreeReplyObject(reply); } rliteFree(context); PASS(); }
TEST test_smove() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; sadd(context, "myset", "mymember"); rliteReply* reply; { char* argv[100] = {"smove", "myset", "otherset", "mymember", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 1); rliteFreeReplyObject(reply); } { char* argv[100] = {"sismember", "myset", "mymember", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 0); rliteFreeReplyObject(reply); } { char* argv[100] = {"sismember", "otherset", "mymember", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 1); rliteFreeReplyObject(reply); } { char* argv[100] = {"smove", "otherset", "myset", "member2", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 0); rliteFreeReplyObject(reply); } { char* argv[100] = {"sismember", "myset", "member2", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 0); rliteFreeReplyObject(reply); } rliteFree(context); return 0; }
TEST test_spop() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; char *m1 = "mymember", *m2 = "member2"; sadd(context, "myset", m1); sadd(context, "myset", m2); rliteReply* reply; { char* argv[100] = {"spop", "myset", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); ASSERT_EQ(reply->type, RLITE_REPLY_STRING); if (!(reply->len == (int)strlen(m1) && memcmp(reply->str, m1, reply->len) == 0) && !(reply->len == (int)strlen(m2) && memcmp(reply->str, m2, reply->len) == 0)) { FAIL(); } rliteFreeReplyObject(reply); } { char* argv[100] = {"spop", "myset", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); ASSERT_EQ(reply->type, RLITE_REPLY_STRING); if (!(reply->len == (int)strlen(m1) && memcmp(reply->str, m1, reply->len) == 0) && !(reply->len == (int)strlen(m2) && memcmp(reply->str, m2, reply->len) == 0)) { FAIL(); } rliteFreeReplyObject(reply); } { char* argv[100] = {"spop", "myset", NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_NIL(reply); rliteFreeReplyObject(reply); } rliteFree(context); return 0; }
TEST test_sadd() { rliteContext *context = rliteConnect(":memory:", 0); rliteReply* reply; char* argv[100] = {"sadd", "myset", "member1", "member2", "anothermember", "member1", NULL}; size_t argvlen[100]; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_INTEGER(reply, 3); rliteFreeReplyObject(reply); rliteFree(context); return 0; }
TEST test_ping() { size_t argvlen[2]; char* argv[2]; rliteReply* reply; argvlen[0] = 4; argv[0] = "PING"; rliteContext *context = rliteConnect(":memory:", 0); reply = rliteCommandArgv(context, 1, argv, argvlen); EXPECT_REPLY_STATUS(reply, "PONG", 4); rliteFreeReplyObject(reply); rliteFree(context); PASS(); }
static PyObject *Rlite_command(hirlite_RliteObject *self, PyObject *args) { PyObject *object; int i, argc; char **argv; size_t *argvlen; PyObject *bytes; char *str; size_t len; rliteReply *reply; argc = (int)PyTuple_Size(args); argv = malloc(sizeof(char *) * argc); if (!argv) return NULL; argvlen = malloc(sizeof(size_t) * argc); if (!argvlen) { free(argv); return NULL; } for (i = 0; i < argc; i++) { object = PyTuple_GetItem(args, i); if (PyUnicode_Check(object)) bytes = PyUnicode_AsASCIIString(object); else bytes = PyObject_Bytes(object); if (bytes == NULL) return NULL; argvlen[i] = len = PyBytes_Size(bytes); str = PyBytes_AsString(bytes); argv[i] = (char*)malloc(len+1); memcpy(argv[i], str, len); argv[i][len] = '\0'; Py_DECREF(bytes); } reply = rliteCommandArgv(self->context, argc, argv, argvlen); object = replyToPyObject(self, reply); for (i = 0; i < argc; i++) { free(argv[i]); } free(argv); free(argvlen); rliteFreeReplyObject(reply); return object; }
TEST test_echo_wrong_arity() { size_t argvlen[2]; char* argv[2]; rliteReply* reply; argvlen[0] = 4; argv[0] = "echo"; rliteContext *context = rliteConnect(":memory:", 0); reply = rliteCommandArgv(context, 1, argv, argvlen); EXPECT_REPLY_ERROR(reply); const char *err = "wrong number of arguments for 'echo' command"; ASSERT_EQ(memcmp(reply->str, err, strlen(err)), 0); rliteFreeReplyObject(reply); rliteFree(context); PASS(); }
TEST test_echo() { size_t argvlen[2]; char* argv[2]; rliteReply* reply; argvlen[0] = 4; argv[0] = "echo"; argvlen[1] = 11; argv[1] = "hello world"; rliteContext *context = rliteConnect(":memory:", 0); reply = rliteCommandArgv(context, 2, argv, argvlen); EXPECT_REPLY_STR(reply, argv[1], argvlen[1]); rliteFreeReplyObject(reply); rliteFree(context); PASS(); }
static void *increment(void *UNUSED(arg)) { rliteContext *context = rliteConnect(FILEPATH, 0); rliteReply* reply; size_t argvlen[100]; char* argv[100] = {"INCR", "key", NULL}; int argc = populateArgvlen(argv, argvlen); long long val = 0; do { reply = rliteCommandArgv(context, argc, argv, argvlen); if (reply->type != RLITE_REPLY_INTEGER || reply->integer < val) { fprintf(stderr, "Expected incremented value to be an integer greater than %lld, got %d (%lld) instead\n", val, reply->type, reply->integer); val = INCREMENT_LIMIT; // break after free } else { val = reply->integer; } rliteFreeReplyObject(reply); } while (val < INCREMENT_LIMIT); rliteFree(context); return NULL; }
TEST test_sunion() { rliteContext *context = rliteConnect(":memory:", 0); size_t argvlen[100]; char *m1 = "mymember", *m2 = "member2", *s1 = "myset", *s2 = "myset2"; sadd(context, s1, m1); sadd(context, s1, m2); sadd(context, s2, m1); sadd(context, s2, "meh"); rliteReply* reply; { char* argv[100] = {"sunion", s1, s2, NULL}; reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen); EXPECT_REPLY_LEN(reply, 3); rliteFreeReplyObject(reply); } rliteFree(context); return 0; }
int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct rliteCommand *cmd; rliteClient *c = lua_client; rliteReply *reply; /* Cached across calls. */ static char **argv = NULL; static size_t *argvlen = NULL; static int argv_size = 0; static int inuse = 0; /* Recursive calls detection. */ /* By using Lua debug hooks it is possible to trigger a recursive call * to luaRedisGenericCommand(), which normally should never happen. * To make this function reentrant is futile and makes it slower, but * we should at least detect such a misuse, and abort. */ if (inuse) { char *recursion_warning = "luaRedisGenericCommand() recursive call detected. " "Are you doing funny stuff with Lua debug hooks?"; rliteLog(RLITE_WARNING,"%s",recursion_warning); luaPushError(lua,recursion_warning); return 1; } inuse++; /* Require at least one argument */ if (argc == 0) { luaPushError(lua, "Please specify at least one argument for rlite.call()"); inuse--; return 1; } /* Build the arguments vector */ if (argv_size < argc) { argv = realloc(argv, sizeof(char *) * argc); argvlen = realloc(argvlen, sizeof(size_t) * argc); argv_size = argc; } for (j = 0; j < argc; j++) { char *obj_s; size_t obj_len; char dbuf[64]; if (lua_type(lua,j+1) == LUA_TNUMBER) { /* We can't use lua_tolstring() for number -> string conversion * since Lua uses a format specifier that loses precision. */ lua_Number num = lua_tonumber(lua,j+1); obj_len = snprintf(dbuf,sizeof(dbuf),"%.17g",(double)num); obj_s = dbuf; } else { obj_s = (char*)lua_tolstring(lua,j+1,&obj_len); if (obj_s == NULL) break; /* Not a string. */ } argv[j] = obj_s; argvlen[j] = obj_len; } /* Check if one of the arguments passed by the Lua script * is not a string or an integer (lua_isstring() return true for * integers as well). */ if (j != argc) { j--; while (j >= 0) { free(argv[j]); j--; } luaPushError(lua, "Lua rlite() command arguments must be strings or integers"); inuse--; return 1; } /* Setup our fake client for command execution */ c->argvlen = argvlen; c->argv = argv; c->argc = argc; /* Command lookup */ cmd = rliteLookupCommand(argv[0], argvlen[0]); if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) || (argc < -cmd->arity))) { if (cmd) luaPushError(lua, "Wrong number of args calling Redis command From Lua script"); else luaPushError(lua,"Unknown Redis command called from Lua script"); goto cleanup; } /* There are commands that are not allowed inside scripts. */ if (cmd->flags & RLITE_CMD_NOSCRIPT) { luaPushError(lua, "This Redis command is not allowed from scripts"); goto cleanup; } /* Write commands are forbidden against read-only slaves, or if a * command marked as non-deterministic was already called in the context * of this script. */ if (cmd->flags & RLITE_CMD_WRITE) { if (lua_random_dirty) { luaPushError(lua, "Write commands not allowed after non deterministic commands"); goto cleanup; } } if (cmd->flags & RLITE_CMD_RANDOM) lua_random_dirty = 1; if (cmd->flags & RLITE_CMD_WRITE) lua_write_dirty = 1; /* Run the command */ rliteAppendCommandClient(c); rliteGetReply(c->context, (void **)&reply); if (raise_error && reply->type != RLITE_REPLY_ERROR) raise_error = 0; rliteToLuaType(lua,reply); /* Sort the output array if needed, assuming it is a non-null multi bulk * reply as expected. */ if ((cmd->flags & RLITE_CMD_SORT_FOR_SCRIPT) && (reply->type == RLITE_REPLY_ARRAY && reply->elements > 0)) { luaSortArray(lua); } rliteFreeReplyObject(reply); cleanup: if (c->argv != argv) { free(c->argv); argv = NULL; argv_size = 0; } if (raise_error) { /* If we are here we should have an error in the stack, in the * form of a table with an "err" field. Extract the string to * return the plain error. */ lua_pushstring(lua,"err"); lua_gettable(lua,-2); inuse--; return lua_error(lua); } inuse--; return 1; }