summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2016-08-03 10:23:03 +0200
committerantirez <antirez@gmail.com>2016-08-03 10:23:03 +0200
commit04340e1ff100b080e520e06d14c0c7e95a194909 (patch)
treec44237264cda6072e14e6d5ec295587b1eed14c4
parent7829e4ed2c4cda26a9891216ecf97c0b6e637195 (diff)
downloadredis-04340e1ff100b080e520e06d14c0c7e95a194909.tar.gz
Modules: initial draft for a testing module.
-rw-r--r--src/module.c201
-rw-r--r--src/modules/testmodule.c125
-rw-r--r--src/redismodule.h2
3 files changed, 235 insertions, 93 deletions
diff --git a/src/module.c b/src/module.c
index 4d1d88001..03bee27ba 100644
--- a/src/module.c
+++ b/src/module.c
@@ -766,6 +766,10 @@ const char *RM_StringPtrLen(const RedisModuleString *str, size_t *len) {
return str->ptr;
}
+/* --------------------------------------------------------------------------
+ * Higher level string operations
+ * ------------------------------------------------------------------------- */
+
/* Convert the string into a long long integer, storing it at `*ll`.
* Returns REDISMODULE_OK on success. If the string can't be parsed
* as a valid, strict long long (no spaces before/after), REDISMODULE_ERR
@@ -783,6 +787,13 @@ int RM_StringToDouble(const RedisModuleString *str, double *d) {
return (retval == C_OK) ? REDISMODULE_OK : REDISMODULE_ERR;
}
+/* Compare two string objects, returning -1, 0 or 1 respectively if
+ * a < b, a == b, a > b. Strings are compared byte by byte as two
+ * binary blobs without any encoding care / collation attempt. */
+int RM_StringCompare(RedisModuleString *a, RedisModuleString *b) {
+ return compareStringObjects(a,b);
+}
+
/* Return the (possibly modified in encoding) input 'str' object if
* the string is unshared, otherwise NULL is returned. */
RedisModuleString *moduleAssertUnsharedString(RedisModuleString *str) {
@@ -2940,100 +2951,9 @@ int moduleRegisterApi(const char *funcname, void *funcptr) {
#define REGISTER_API(name) \
moduleRegisterApi("RedisModule_" #name, (void *)(unsigned long)RM_ ## name)
-/* Register all the APIs we export. */
-void moduleRegisterCoreAPI(void) {
- server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
- REGISTER_API(Alloc);
- REGISTER_API(Calloc);
- REGISTER_API(Realloc);
- REGISTER_API(Free);
- REGISTER_API(Strdup);
- REGISTER_API(CreateCommand);
- REGISTER_API(SetModuleAttribs);
- REGISTER_API(WrongArity);
- REGISTER_API(ReplyWithLongLong);
- REGISTER_API(ReplyWithError);
- REGISTER_API(ReplyWithSimpleString);
- REGISTER_API(ReplyWithArray);
- REGISTER_API(ReplySetArrayLength);
- REGISTER_API(ReplyWithString);
- REGISTER_API(ReplyWithStringBuffer);
- REGISTER_API(ReplyWithNull);
- REGISTER_API(ReplyWithCallReply);
- REGISTER_API(ReplyWithDouble);
- REGISTER_API(GetSelectedDb);
- REGISTER_API(SelectDb);
- REGISTER_API(OpenKey);
- REGISTER_API(CloseKey);
- REGISTER_API(KeyType);
- REGISTER_API(ValueLength);
- REGISTER_API(ListPush);
- REGISTER_API(ListPop);
- REGISTER_API(StringToLongLong);
- REGISTER_API(StringToDouble);
- REGISTER_API(Call);
- REGISTER_API(CallReplyProto);
- REGISTER_API(FreeCallReply);
- REGISTER_API(CallReplyInteger);
- REGISTER_API(CallReplyType);
- REGISTER_API(CallReplyLength);
- REGISTER_API(CallReplyArrayElement);
- REGISTER_API(CallReplyStringPtr);
- REGISTER_API(CreateStringFromCallReply);
- REGISTER_API(CreateString);
- REGISTER_API(CreateStringFromLongLong);
- REGISTER_API(CreateStringFromString);
- REGISTER_API(FreeString);
- REGISTER_API(StringPtrLen);
- REGISTER_API(AutoMemory);
- REGISTER_API(Replicate);
- REGISTER_API(ReplicateVerbatim);
- REGISTER_API(DeleteKey);
- REGISTER_API(StringSet);
- REGISTER_API(StringDMA);
- REGISTER_API(StringTruncate);
- REGISTER_API(SetExpire);
- REGISTER_API(GetExpire);
- REGISTER_API(ZsetAdd);
- REGISTER_API(ZsetIncrby);
- REGISTER_API(ZsetScore);
- REGISTER_API(ZsetRem);
- REGISTER_API(ZsetRangeStop);
- REGISTER_API(ZsetFirstInScoreRange);
- REGISTER_API(ZsetLastInScoreRange);
- REGISTER_API(ZsetFirstInLexRange);
- REGISTER_API(ZsetLastInLexRange);
- REGISTER_API(ZsetRangeCurrentElement);
- REGISTER_API(ZsetRangeNext);
- REGISTER_API(ZsetRangePrev);
- REGISTER_API(ZsetRangeEndReached);
- REGISTER_API(HashSet);
- REGISTER_API(HashGet);
- REGISTER_API(IsKeysPositionRequest);
- REGISTER_API(KeyAtPos);
- REGISTER_API(GetClientId);
- REGISTER_API(PoolAlloc);
- REGISTER_API(CreateDataType);
- REGISTER_API(ModuleTypeSetValue);
- REGISTER_API(ModuleTypeGetType);
- REGISTER_API(ModuleTypeGetValue);
- REGISTER_API(SaveUnsigned);
- REGISTER_API(LoadUnsigned);
- REGISTER_API(SaveSigned);
- REGISTER_API(LoadSigned);
- REGISTER_API(SaveString);
- REGISTER_API(SaveStringBuffer);
- REGISTER_API(LoadString);
- REGISTER_API(LoadStringBuffer);
- REGISTER_API(SaveDouble);
- REGISTER_API(LoadDouble);
- REGISTER_API(EmitAOF);
- REGISTER_API(Log);
- REGISTER_API(StringAppendBuffer);
- REGISTER_API(RetainString);
-}
-
/* Global initialization at Redis startup. */
+void moduleRegisterCoreAPI(void);
+
void moduleInitModulesSystem(void) {
server.loadmodule_queue = listCreate();
modules = dictCreate(&modulesDictType,NULL);
@@ -3222,3 +3142,98 @@ void moduleCommand(client *c) {
addReply(c,shared.syntaxerr);
}
}
+
+/* Register all the APIs we export. Keep this function at the end of the
+ * file so that's easy to seek it to add new entries. */
+void moduleRegisterCoreAPI(void) {
+ server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
+ REGISTER_API(Alloc);
+ REGISTER_API(Calloc);
+ REGISTER_API(Realloc);
+ REGISTER_API(Free);
+ REGISTER_API(Strdup);
+ REGISTER_API(CreateCommand);
+ REGISTER_API(SetModuleAttribs);
+ REGISTER_API(WrongArity);
+ REGISTER_API(ReplyWithLongLong);
+ REGISTER_API(ReplyWithError);
+ REGISTER_API(ReplyWithSimpleString);
+ REGISTER_API(ReplyWithArray);
+ REGISTER_API(ReplySetArrayLength);
+ REGISTER_API(ReplyWithString);
+ REGISTER_API(ReplyWithStringBuffer);
+ REGISTER_API(ReplyWithNull);
+ REGISTER_API(ReplyWithCallReply);
+ REGISTER_API(ReplyWithDouble);
+ REGISTER_API(GetSelectedDb);
+ REGISTER_API(SelectDb);
+ REGISTER_API(OpenKey);
+ REGISTER_API(CloseKey);
+ REGISTER_API(KeyType);
+ REGISTER_API(ValueLength);
+ REGISTER_API(ListPush);
+ REGISTER_API(ListPop);
+ REGISTER_API(StringToLongLong);
+ REGISTER_API(StringToDouble);
+ REGISTER_API(Call);
+ REGISTER_API(CallReplyProto);
+ REGISTER_API(FreeCallReply);
+ REGISTER_API(CallReplyInteger);
+ REGISTER_API(CallReplyType);
+ REGISTER_API(CallReplyLength);
+ REGISTER_API(CallReplyArrayElement);
+ REGISTER_API(CallReplyStringPtr);
+ REGISTER_API(CreateStringFromCallReply);
+ REGISTER_API(CreateString);
+ REGISTER_API(CreateStringFromLongLong);
+ REGISTER_API(CreateStringFromString);
+ REGISTER_API(FreeString);
+ REGISTER_API(StringPtrLen);
+ REGISTER_API(AutoMemory);
+ REGISTER_API(Replicate);
+ REGISTER_API(ReplicateVerbatim);
+ REGISTER_API(DeleteKey);
+ REGISTER_API(StringSet);
+ REGISTER_API(StringDMA);
+ REGISTER_API(StringTruncate);
+ REGISTER_API(SetExpire);
+ REGISTER_API(GetExpire);
+ REGISTER_API(ZsetAdd);
+ REGISTER_API(ZsetIncrby);
+ REGISTER_API(ZsetScore);
+ REGISTER_API(ZsetRem);
+ REGISTER_API(ZsetRangeStop);
+ REGISTER_API(ZsetFirstInScoreRange);
+ REGISTER_API(ZsetLastInScoreRange);
+ REGISTER_API(ZsetFirstInLexRange);
+ REGISTER_API(ZsetLastInLexRange);
+ REGISTER_API(ZsetRangeCurrentElement);
+ REGISTER_API(ZsetRangeNext);
+ REGISTER_API(ZsetRangePrev);
+ REGISTER_API(ZsetRangeEndReached);
+ REGISTER_API(HashSet);
+ REGISTER_API(HashGet);
+ REGISTER_API(IsKeysPositionRequest);
+ REGISTER_API(KeyAtPos);
+ REGISTER_API(GetClientId);
+ REGISTER_API(PoolAlloc);
+ REGISTER_API(CreateDataType);
+ REGISTER_API(ModuleTypeSetValue);
+ REGISTER_API(ModuleTypeGetType);
+ REGISTER_API(ModuleTypeGetValue);
+ REGISTER_API(SaveUnsigned);
+ REGISTER_API(LoadUnsigned);
+ REGISTER_API(SaveSigned);
+ REGISTER_API(LoadSigned);
+ REGISTER_API(SaveString);
+ REGISTER_API(SaveStringBuffer);
+ REGISTER_API(LoadString);
+ REGISTER_API(LoadStringBuffer);
+ REGISTER_API(SaveDouble);
+ REGISTER_API(LoadDouble);
+ REGISTER_API(EmitAOF);
+ REGISTER_API(Log);
+ REGISTER_API(StringAppendBuffer);
+ REGISTER_API(RetainString);
+ REGISTER_API(StringCompare);
+}
diff --git a/src/modules/testmodule.c b/src/modules/testmodule.c
new file mode 100644
index 000000000..1cd843b3d
--- /dev/null
+++ b/src/modules/testmodule.c
@@ -0,0 +1,125 @@
+/* Module designed to test the Redis modules subsystem.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "../redismodule.h"
+
+/* ------------------------------- Test units ------------------------------- */
+
+/* TEST.STRING.APPEND -- Test appending to an existing string object. */
+int TestStringAppend(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3);
+ RedisModule_StringAppendBuffer(ctx,s,"bar",3);
+ RedisModule_ReplyWithString(ctx,s);
+ RedisModule_FreeString(ctx,s);
+ return REDISMODULE_OK;
+}
+
+/* TEST.STRING.APPEND.AM -- Test append with retain when auto memory is on. */
+int TestStringAppendAM(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ RedisModule_AutoMemory(ctx);
+ RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3);
+ RedisModule_RetainString(ctx,s);
+ RedisModule_StringAppendBuffer(ctx,s,"bar",3);
+ RedisModule_ReplyWithString(ctx,s);
+ RedisModule_FreeString(ctx,s);
+ return REDISMODULE_OK;
+}
+
+/* ----------------------------- Test framework ----------------------------- */
+
+/* Return 1 if the reply matches the specified string, otherwise log errors
+ * in the server log and return 0. */
+int TestAssertStringReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply, char *str, size_t len) {
+ RedisModuleString *mystr, *expected;
+
+ if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_STRING) {
+ RedisModule_Log(ctx,"warning","Unexpected reply type %d",
+ RedisModule_CallReplyType(reply));
+ return 0;
+ }
+ mystr = RedisModule_CreateStringFromCallReply(reply);
+ expected = RedisModule_CreateString(ctx,str,len);
+ if (RedisModule_StringCompare(mystr,expected) != 0) {
+ const char *mystr_ptr = RedisModule_StringPtrLen(mystr,NULL);
+ const char *expected_ptr = RedisModule_StringPtrLen(expected,NULL);
+ RedisModule_Log(ctx,"warning",
+ "Unexpected string reply '%s' (instead of '%s')",
+ mystr_ptr, expected_ptr);
+ return 0;
+ }
+ return 1;
+}
+
+#define T(name,...) \
+ do { \
+ RedisModule_Log(ctx,"warning","Testing %s", name); \
+ reply = RedisModule_Call(ctx,name,__VA_ARGS__); \
+ } while (0);
+
+/* TEST.IT -- Run all the tests. */
+int TestIt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ RedisModule_AutoMemory(ctx);
+ RedisModuleCallReply *reply;
+
+ T("test.string.append","");
+ if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail;
+
+ T("test.string.append.am","");
+ if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail;
+
+ RedisModule_ReplyWithSimpleString(ctx,"ALL TESTS PASSED");
+ return REDISMODULE_OK;
+
+fail:
+ RedisModule_ReplyWithSimpleString(ctx,
+ "SOME TEST NOT PASSED! Check server logs");
+ return REDISMODULE_OK;
+}
+
+int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (RedisModule_Init(ctx,"test",1,REDISMODULE_APIVER_1)
+ == REDISMODULE_ERR) return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"test.string.append",
+ TestStringAppend,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"test.string.append.am",
+ TestStringAppendAM,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx,"test.it",
+ TestIt,"readonly",1,1,1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ return REDISMODULE_OK;
+}
diff --git a/src/redismodule.h b/src/redismodule.h
index b368049d1..0a35cf047 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -186,6 +186,7 @@ double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);
int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len);
void REDISMODULE_API_FUNC(RedisModule_RetainString)(RedisModuleCtx *ctx, RedisModuleString *str);
+int REDISMODULE_API_FUNC(RedisModule_StringCompare)(RedisModuleString *a, RedisModuleString *b);
/* This is included inline inside each Redis module. */
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));
@@ -281,6 +282,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(Log);
REDISMODULE_GET_API(StringAppendBuffer);
REDISMODULE_GET_API(RetainString);
+ REDISMODULE_GET_API(StringCompare);
RedisModule_SetModuleAttribs(ctx,name,ver,apiver);
return REDISMODULE_OK;