Add base version of Redis modules 36/136/3
authorRolf Badorek <rolf.badorek@nokia.com>
Fri, 10 May 2019 13:12:32 +0000 (16:12 +0300)
committerRolf Badorek <rolf.badorek@nokia.com>
Tue, 14 May 2019 11:36:28 +0000 (14:36 +0300)
Copied from Nokia internal RCP code repository, revision d452b42d from
branch 'rcp2.0'.

No functional changes compared to above mentioned base version.

Documentation of the available commands added to README file.

Updated `Dockerfile.redis` so that Redis modules are built and
installed to image which is used as a basis for `redis-standalone`
pod.

Change-Id: I32bc59b4f0a60eb3683fdd31e295d57ee68d8430
Signed-off-by: Rolf Badorek <rolf.badorek@nokia.com>
14 files changed:
docker/Dockerfile.redis
redismodule/Makefile.am [new file with mode: 0755]
redismodule/README.md [new file with mode: 0755]
redismodule/autogen.sh [new file with mode: 0755]
redismodule/configure.ac [new file with mode: 0755]
redismodule/include/redismodule.h [new file with mode: 0755]
redismodule/m4/ax_prog_doxygen.m4 [new file with mode: 0755]
redismodule/run-tests.sh.in [new file with mode: 0755]
redismodule/src/exstrings.c [new file with mode: 0755]
redismodule/tst/mock/include/exstringsStub.h [new file with mode: 0755]
redismodule/tst/mock/include/redismodule.h [new file with mode: 0755]
redismodule/tst/mock/src/redismoduleStub.cpp [new file with mode: 0755]
redismodule/tst/src/exstrings_test.cpp [new file with mode: 0755]
redismodule/tst/src/main.cpp [new file with mode: 0755]

index 8d02fe3..a8b0c32 100644 (file)
 FROM ubuntu:latest
 
 # Install redis
-RUN apt-get update && \
+RUN apt update && \
     apt install -y redis-server && \
-    apt-get clean
+    apt clean
+
+# Install Redis modules
+RUN apt install -y build-essential && \
+    apt install -y automake && \
+    apt install -y libtool && \
+    apt clean
+COPY ./redismodule ./redismodule
+WORKDIR /redismodule
+RUN ./autogen.sh && \
+    ./configure && \
+    make install -j
 
 # Create suitable configuration file
 RUN sed -i 's/^\(bind .*\)$/# \1/' /etc/redis/redis.conf && \
     sed -i 's/^\(daemonize .*\)$/# \1/' /etc/redis/redis.conf && \
     sed 's/^protected-mode yes/protected-mode no/' -i /etc/redis/redis.conf && \
+    echo 'loadmodule /usr/local/libexec/redismodule/libredismodule.so' >> /etc/redis/redis.conf && \
     sed -i 's/^\(save .*\)$/# \1/' /etc/redis/redis.conf && \
     echo 'save ""' >> /etc/redis/redis.conf
 
diff --git a/redismodule/Makefile.am b/redismodule/Makefile.am
new file mode 100755 (executable)
index 0000000..f2b9795
--- /dev/null
@@ -0,0 +1,64 @@
+ACLOCAL_AMFLAGS = -I m4
+
+redismoduledir = @libexecdir@/$(PACKAGE)
+redismodule_LTLIBRARIES = libredismodule.la
+#lib_LTLIBRARIES = libredismodule.la
+
+BASE_LDFLAGS =
+
+libredismodule_la_SOURCES = \
+       include/redismodule.h\
+       src/exstrings.c 
+
+libredismodule_la_CFLAGS = \
+       -std=c11 -fPIC -g \
+       -I${includedir} -I${top_srcdir}/include 
+
+
+libredismodule_la_LDFLAGS = $(BASE_LDFLAGS) -avoid-version -module -shared
+
+#pkgincludedir = ${includedir}
+#pkginclude_HEADERS = include/redismodule.h 
+
+clean-local: 
+       rm -rf ${builddir}/libredismodule.pc
+
+# UT
+CPP_U_TEST=$(CPP_U_TEST_LATEST)
+check_PROGRAMS = redismodule_ut
+#TESTS = ${check_PROGRAMS} 
+redismodule_ut_SOURCES = \
+       tst/mock/include/redismodule.h  \
+       tst/mock/include/exstringsStub.h \
+       src/exstrings.c \
+       tst/mock/src/redismoduleStub.cpp \
+       tst/src/main.cpp \
+       tst/src/exstrings_test.cpp 
+
+
+redismodule_ut_CFLAGS = \
+        -std=c11 -g -Wall \
+        -fprofile-arcs -ftest-coverage \
+        -D__UT__ \
+               -I${top_srcdir}/tst/mock/include \
+               -I${includedir} \
+               -I${top_srcdir}/include \
+               -I${CPP_U_TEST_LATEST}/include 
+
+redismodule_ut_CXXFLAGS = \
+       -std=c++11 -g -Wall \
+       -fprofile-arcs -ftest-coverage \
+       -D__UT__ \
+       -I${top_srcdir}/tst/mock/include \
+       -I${includedir} \
+       -I${top_srcdir}/include \
+       -I${CPP_U_TEST_LATEST}/include 
+
+
+redismodule_ut_LDFLAGS = -Wl,-rpath=${libdir} ${UT_COVERAGE_LDFLAGS}
+redismodule_ut_LDADD = -L${libdir} -L${CPP_U_TEST_LATEST}/lib -lCppUTest -lCppUTestExt -lgcov 
+
+test: redismodule_ut
+       ./run-tests.sh
+
+TESTS = run-tests.sh
diff --git a/redismodule/README.md b/redismodule/README.md
new file mode 100755 (executable)
index 0000000..04012ea
--- /dev/null
@@ -0,0 +1,223 @@
+# Introduction
+
+This subdirectory provides implementation for the commands which are implemented
+as a [Redis modules](https://redis.io/topics/modules-intro).
+
+# Commands
+
+## SETIE key value oldvalue [expiration EX seconds|PX milliseconds]
+
+Time complexity: O(1) + O(1)
+
+Checks a String 'key' for 'oldvalue' equality and set key for 'value' with
+optional expired.
+
+```
+Example:
+
+redis> get mykey
+(nil)
+redis> setie mykey "Hello again" "Hello"
+(nil)
+
+redis> set mykey "Hello"
+OK
+redis> get mykey
+"Hello"
+redis> setie mykey "Hello again" "Hello"
+"OK"
+redis> get mykey
+"Hello again"
+redis> setie mykey "Hello 2" "Hello"
+(nil)
+redis> get mykey
+"Hello again"
+redis> setie mykey "Hello 2" "Hello again" ex 100
+"OK"
+redis> ttl mykey
+(integer) 96
+redis> get mykey
+"Hello 2"
+```
+
+## SETNE key value oldvalue [expiration EX seconds|PX milliseconds]
+
+Time complexity: O(1) + O(1)
+
+Checks a String 'key' for 'oldvalue' not equality and set key for 'value' with optional expired.
+
+Example:
+
+```
+redis> get mykey
+(nil)
+redis> setne mykey "Hello again" "Hello"
+"OK"
+redis> get mykey
+"Hello again"
+redis> setne mykey "Hello 2" "Hello again"
+(nil)
+redis> setne mykey "Hello 2" "Hello"
+"OK"
+redis> get mykey
+"Hello 2"
+redis> setne mykey "Hello 3" "Hello" ex 100
+"OK"
+redis> get mykey
+"Hello 3"
+redis> ttl mykey
+(integer) 93
+```
+
+## DELIE key oldvalue
+
+Time complexity: O(1) + O(1)
+
+Checks a String 'key' for 'oldvalue' equality and delete the key.
+
+```
+Example:
+redis> get mykey
+(nil)
+redis> set mykey "Hello"
+"OK"
+redis> get mykey
+"Hello"
+redis> delie mykey "Hello again"
+(integer) 0
+redis> get mykey
+"Hello"
+redis> delie mykey "Hello"
+(integer) 1
+redis> get mykey
+(nil)
+```
+
+## DELNE key oldvalue
+
+Time complexity: O(1) + O(1)
+
+Checks a String 'key' for 'oldvalue' not equality and delete the key.
+
+```
+Example:
+redis> get mykey
+(nil)
+redis> set mykey "Hello"
+"OK"
+redis> get mykey
+"Hello"
+redis> delne mykey "Hello"
+(integer) 0
+redis> get mykey
+"Hello"
+redis> delne mykey "Hello again"
+(integer) 1
+redis> get mykey
+(nil)
+```
+
+## MSETPUB key value [key value...] channel message
+
+Time complexity: O(N) where N is the number of keys to set + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Set the given keys to their respective values and post a message to the given channel
+
+## SETXXPUB key value channel message
+
+Time complexity: O(1) + O(1) + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Set key to hold string value if key already exist and post a message to the given channel if set key value successfully
+
+## SETNXPUB key value channel message
+
+Time complexity: O(1) + O(1) + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Set key to hold string value if key does not exist and post a message to the given channel if set key value successfully
+
+## SETIEPUB key value oldvalue channel message
+
+Time complexity: O(1) + O(1) + O(1) + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Checks a String 'key' for 'oldvalue' equality and set key for 'value'  and post a message to the given channel if set key value successfully
+
+## SETNEPUB key value oldvalue channel message
+
+Time complexity: O(1) + O(1) + O(1) + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Checks a String 'key' for 'oldvalue' not equality and set key for 'value'  and post a message to the given channel if set key value successfully
+
+## DELPUB key [key...] channel message
+
+Time complexity: O(N) where N is the number of keys that will be removed + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Removes the specified keys and post a message to the given channel if delete key successfully(return >0)
+
+## DELIEPUB key oldvalue channel message
+
+ime complexity: O(1) + O(1) + O(1) + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Checks a String 'key' for 'oldvalue' equality and delete the key and post a message to the given channel if delete key successfully(return 1)
+
+## DELNEPUB key oldvalue channel message
+
+Time complexity: O(1) + O(1) + O(1) + O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client)
+
+Checks a String 'key' for 'oldvalue' not equality and delete the key and post a message to the given channel if delete key successfully(return 1)
+
+## NGET pattern
+
+Time complexity: O(N) with N being the number of keys in the instance + O(N) where N is the number of keys to retrieve
+
+Returns all key-value pairs matching pattern.
+
+```
+example:
+
+redis> nget mykey*
+(empty list or set)
+
+redis> set mykey1 "myvalue1"
+OK
+redis> set mykey2 "myvalue2"
+OK
+redis> set mykey3 "myvalue3"
+OK
+redis> set mykey4 "myvalue4"
+OK
+redis> nget mykey*
+1) "mykey2"
+2) "myvalue2"
+3) "mykey1"
+4) "myvalue1"
+5) "mykey4"
+6) "myvalue4"
+7) "mykey3"
+8) "myvalue3"
+```
+
+## NDEL pattern
+
+Time complexity: O(N) with N being the number of keys in the instance + O(N) where N is the number of keys that will be removed
+
+Remove all key-value pairs matching pattern.
+
+```
+example:
+
+redis> nget mykey*
+1) "mykey2"
+2) "myvalue2"
+3) "mykey1"
+4) "myvalue1"
+5) "mykey4"
+6) "myvalue4"
+7) "mykey3"
+8) "myvalue3"
+
+redis> ndel mykey*
+(integer) 4
+
+redis> ndel mykey*
+(integer) 0
+```
diff --git a/redismodule/autogen.sh b/redismodule/autogen.sh
new file mode 100755 (executable)
index 0000000..d87dafd
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+autoreconf --install
diff --git a/redismodule/configure.ac b/redismodule/configure.ac
new file mode 100755 (executable)
index 0000000..ecc73e5
--- /dev/null
@@ -0,0 +1,27 @@
+AC_INIT([redismodule], [0.0.0], [], [], [https://gerrit.oran-osc.org/r/#/admin/projects/ric-plt/dbaas])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIRS([m4])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([run-tests.sh], [chmod +x run-tests.sh])
+
+
+AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects tar-pax])
+
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+LT_INIT([disable-static])
+
+# Checks for programs.
+AC_PROG_CXX
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+
+# Checks for header files.
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+
+AC_CONFIG_FILES([Makefile])
+
+AC_OUTPUT
diff --git a/redismodule/include/redismodule.h b/redismodule/include/redismodule.h
new file mode 100755 (executable)
index 0000000..4f08f80
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#ifndef REDISMODULE_H
+#define REDISMODULE_H
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/* ---------------- Defines common between core and modules --------------- */
+
+/* Error status return values. */
+#define REDISMODULE_OK 0
+#define REDISMODULE_ERR 1
+
+/* API versions. */
+#define REDISMODULE_APIVER_1 1
+
+/* API flags and constants */
+#define REDISMODULE_READ (1<<0)
+#define REDISMODULE_WRITE (1<<1)
+
+#define REDISMODULE_LIST_HEAD 0
+#define REDISMODULE_LIST_TAIL 1
+
+/* Key types. */
+#define REDISMODULE_KEYTYPE_EMPTY 0
+#define REDISMODULE_KEYTYPE_STRING 1
+#define REDISMODULE_KEYTYPE_LIST 2
+#define REDISMODULE_KEYTYPE_HASH 3
+#define REDISMODULE_KEYTYPE_SET 4
+#define REDISMODULE_KEYTYPE_ZSET 5
+#define REDISMODULE_KEYTYPE_MODULE 6
+
+/* Reply types. */
+#define REDISMODULE_REPLY_UNKNOWN -1
+#define REDISMODULE_REPLY_STRING 0
+#define REDISMODULE_REPLY_ERROR 1
+#define REDISMODULE_REPLY_INTEGER 2
+#define REDISMODULE_REPLY_ARRAY 3
+#define REDISMODULE_REPLY_NULL 4
+
+/* Postponed array length. */
+#define REDISMODULE_POSTPONED_ARRAY_LEN -1
+
+/* Expire */
+#define REDISMODULE_NO_EXPIRE -1
+
+/* Sorted set API flags. */
+#define REDISMODULE_ZADD_XX      (1<<0)
+#define REDISMODULE_ZADD_NX      (1<<1)
+#define REDISMODULE_ZADD_ADDED   (1<<2)
+#define REDISMODULE_ZADD_UPDATED (1<<3)
+#define REDISMODULE_ZADD_NOP     (1<<4)
+
+/* Hash API flags. */
+#define REDISMODULE_HASH_NONE       0
+#define REDISMODULE_HASH_NX         (1<<0)
+#define REDISMODULE_HASH_XX         (1<<1)
+#define REDISMODULE_HASH_CFIELDS    (1<<2)
+#define REDISMODULE_HASH_EXISTS     (1<<3)
+
+/* Context Flags: Info about the current context returned by RM_GetContextFlags */
+
+/* The command is running in the context of a Lua script */
+#define REDISMODULE_CTX_FLAGS_LUA 0x0001
+/* The command is running inside a Redis transaction */
+#define REDISMODULE_CTX_FLAGS_MULTI 0x0002
+/* The instance is a master */
+#define REDISMODULE_CTX_FLAGS_MASTER 0x0004
+/* The instance is a slave */
+#define REDISMODULE_CTX_FLAGS_SLAVE 0x0008
+/* The instance is read-only (usually meaning it's a slave as well) */
+#define REDISMODULE_CTX_FLAGS_READONLY 0x0010
+/* The instance is running in cluster mode */
+#define REDISMODULE_CTX_FLAGS_CLUSTER 0x0020
+/* The instance has AOF enabled */
+#define REDISMODULE_CTX_FLAGS_AOF 0x0040 //
+/* The instance has RDB enabled */
+#define REDISMODULE_CTX_FLAGS_RDB 0x0080 //
+/* The instance has Maxmemory set */
+#define REDISMODULE_CTX_FLAGS_MAXMEMORY 0x0100
+/* Maxmemory is set and has an eviction policy that may delete keys */
+#define REDISMODULE_CTX_FLAGS_EVICT 0x0200 
+
+
+#define REDISMODULE_NOTIFY_GENERIC (1<<2)     /* g */
+#define REDISMODULE_NOTIFY_STRING (1<<3)      /* $ */
+#define REDISMODULE_NOTIFY_LIST (1<<4)        /* l */
+#define REDISMODULE_NOTIFY_SET (1<<5)         /* s */
+#define REDISMODULE_NOTIFY_HASH (1<<6)        /* h */
+#define REDISMODULE_NOTIFY_ZSET (1<<7)        /* z */
+#define REDISMODULE_NOTIFY_EXPIRED (1<<8)     /* x */
+#define REDISMODULE_NOTIFY_EVICTED (1<<9)     /* e */
+#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED)      /* A */
+
+
+/* A special pointer that we can use between the core and the module to signal
+ * field deletion, and that is impossible to be a valid pointer. */
+#define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1)
+
+/* Error messages. */
+#define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value"
+
+#define REDISMODULE_POSITIVE_INFINITE (1.0/0.0)
+#define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0)
+
+#define REDISMODULE_NOT_USED(V) ((void) V)
+
+/* ------------------------- End of common defines ------------------------ */
+
+#ifndef REDISMODULE_CORE
+
+typedef long long mstime_t;
+
+/* Incomplete structures for compiler checks but opaque access. */
+typedef struct RedisModuleCtx RedisModuleCtx;
+typedef struct RedisModuleKey RedisModuleKey;
+typedef struct RedisModuleString RedisModuleString;
+typedef struct RedisModuleCallReply RedisModuleCallReply;
+typedef struct RedisModuleIO RedisModuleIO;
+typedef struct RedisModuleType RedisModuleType;
+typedef struct RedisModuleDigest RedisModuleDigest;
+typedef struct RedisModuleBlockedClient RedisModuleBlockedClient;
+
+typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+
+typedef int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key);
+typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);
+typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);
+typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);
+typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value);
+typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);
+typedef void (*RedisModuleTypeFreeFunc)(void *value);
+
+#define REDISMODULE_TYPE_METHOD_VERSION 1
+typedef struct RedisModuleTypeMethods {
+    uint64_t version;
+    RedisModuleTypeLoadFunc rdb_load;
+    RedisModuleTypeSaveFunc rdb_save;
+    RedisModuleTypeRewriteFunc aof_rewrite;
+    RedisModuleTypeMemUsageFunc mem_usage;
+    RedisModuleTypeDigestFunc digest;
+    RedisModuleTypeFreeFunc free;
+} RedisModuleTypeMethods;
+
+#define REDISMODULE_GET_API(name) \
+    RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name))
+
+#define REDISMODULE_API_FUNC(x) (*x)
+
+
+void *REDISMODULE_API_FUNC(RedisModule_Alloc)(size_t bytes);
+void *REDISMODULE_API_FUNC(RedisModule_Realloc)(void *ptr, size_t bytes);
+void REDISMODULE_API_FUNC(RedisModule_Free)(void *ptr);
+void *REDISMODULE_API_FUNC(RedisModule_Calloc)(size_t nmemb, size_t size);
+char *REDISMODULE_API_FUNC(RedisModule_Strdup)(const char *str);
+int REDISMODULE_API_FUNC(RedisModule_GetApi)(const char *, void *);
+int REDISMODULE_API_FUNC(RedisModule_CreateCommand)(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep);
+void REDISMODULE_API_FUNC(RedisModule_SetModuleAttribs)(RedisModuleCtx *ctx, const char *name, int ver, int apiver);
+int REDISMODULE_API_FUNC(RedisModule_IsModuleNameBusy)(const char *name);
+int REDISMODULE_API_FUNC(RedisModule_WrongArity)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithLongLong)(RedisModuleCtx *ctx, long long ll);
+int REDISMODULE_API_FUNC(RedisModule_GetSelectedDb)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid);
+void *REDISMODULE_API_FUNC(RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode);
+void REDISMODULE_API_FUNC(RedisModule_CloseKey)(RedisModuleKey *kp);
+int REDISMODULE_API_FUNC(RedisModule_KeyType)(RedisModuleKey *kp);
+size_t REDISMODULE_API_FUNC(RedisModule_ValueLength)(RedisModuleKey *kp);
+int REDISMODULE_API_FUNC(RedisModule_ListPush)(RedisModuleKey *kp, int where, RedisModuleString *ele);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ListPop)(RedisModuleKey *key, int where);
+RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_Call)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
+const char *REDISMODULE_API_FUNC(RedisModule_CallReplyProto)(RedisModuleCallReply *reply, size_t *len);
+void REDISMODULE_API_FUNC(RedisModule_FreeCallReply)(RedisModuleCallReply *reply);
+int REDISMODULE_API_FUNC(RedisModule_CallReplyType)(RedisModuleCallReply *reply);
+long long REDISMODULE_API_FUNC(RedisModule_CallReplyInteger)(RedisModuleCallReply *reply);
+size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *reply);
+RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);
+void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str);
+const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len);
+void REDISMODULE_API_FUNC(RedisModule_ReplySetArrayLength)(RedisModuleCtx *ctx, long len);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithStringBuffer)(RedisModuleCtx *ctx, const char *buf, size_t len);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithString)(RedisModuleCtx *ctx, RedisModuleString *str);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithNull)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d);
+int REDISMODULE_API_FUNC(RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply);
+int REDISMODULE_API_FUNC(RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll);
+int REDISMODULE_API_FUNC(RedisModule_StringToDouble)(const RedisModuleString *str, double *d);
+void REDISMODULE_API_FUNC(RedisModule_AutoMemory)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_Replicate)(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
+int REDISMODULE_API_FUNC(RedisModule_ReplicateVerbatim)(RedisModuleCtx *ctx);
+const char *REDISMODULE_API_FUNC(RedisModule_CallReplyStringPtr)(RedisModuleCallReply *reply, size_t *len);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromCallReply)(RedisModuleCallReply *reply);
+int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_UnlinkKey)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str);
+char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode);
+int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen);
+mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire);
+int REDISMODULE_API_FUNC(RedisModule_ZsetAdd)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr);
+int REDISMODULE_API_FUNC(RedisModule_ZsetIncrby)(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr, double *newscore);
+int REDISMODULE_API_FUNC(RedisModule_ZsetScore)(RedisModuleKey *key, RedisModuleString *ele, double *score);
+int REDISMODULE_API_FUNC(RedisModule_ZsetRem)(RedisModuleKey *key, RedisModuleString *ele, int *deleted);
+void REDISMODULE_API_FUNC(RedisModule_ZsetRangeStop)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);
+int REDISMODULE_API_FUNC(RedisModule_ZsetLastInScoreRange)(RedisModuleKey *key, double min, double max, int minex, int maxex);
+int REDISMODULE_API_FUNC(RedisModule_ZsetFirstInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);
+int REDISMODULE_API_FUNC(RedisModule_ZsetLastInLexRange)(RedisModuleKey *key, RedisModuleString *min, RedisModuleString *max);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_ZsetRangeCurrentElement)(RedisModuleKey *key, double *score);
+int REDISMODULE_API_FUNC(RedisModule_ZsetRangeNext)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_ZsetRangePrev)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_ZsetRangeEndReached)(RedisModuleKey *key);
+int REDISMODULE_API_FUNC(RedisModule_HashSet)(RedisModuleKey *key, int flags, ...);
+int REDISMODULE_API_FUNC(RedisModule_HashGet)(RedisModuleKey *key, int flags, ...);
+int REDISMODULE_API_FUNC(RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx);
+void REDISMODULE_API_FUNC(RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos);
+unsigned long long REDISMODULE_API_FUNC(RedisModule_GetClientId)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_GetContextFlags)(RedisModuleCtx *ctx);
+void *REDISMODULE_API_FUNC(RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes);
+RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods);
+int REDISMODULE_API_FUNC(RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value);
+RedisModuleType *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetType)(RedisModuleKey *key);
+void *REDISMODULE_API_FUNC(RedisModule_ModuleTypeGetValue)(RedisModuleKey *key);
+void REDISMODULE_API_FUNC(RedisModule_SaveUnsigned)(RedisModuleIO *io, uint64_t value);
+uint64_t REDISMODULE_API_FUNC(RedisModule_LoadUnsigned)(RedisModuleIO *io);
+void REDISMODULE_API_FUNC(RedisModule_SaveSigned)(RedisModuleIO *io, int64_t value);
+int64_t REDISMODULE_API_FUNC(RedisModule_LoadSigned)(RedisModuleIO *io);
+void REDISMODULE_API_FUNC(RedisModule_EmitAOF)(RedisModuleIO *io, const char *cmdname, const char *fmt, ...);
+void REDISMODULE_API_FUNC(RedisModule_SaveString)(RedisModuleIO *io, RedisModuleString *s);
+void REDISMODULE_API_FUNC(RedisModule_SaveStringBuffer)(RedisModuleIO *io, const char *str, size_t len);
+RedisModuleString *REDISMODULE_API_FUNC(RedisModule_LoadString)(RedisModuleIO *io);
+char *REDISMODULE_API_FUNC(RedisModule_LoadStringBuffer)(RedisModuleIO *io, size_t *lenptr);
+void REDISMODULE_API_FUNC(RedisModule_SaveDouble)(RedisModuleIO *io, double value);
+double REDISMODULE_API_FUNC(RedisModule_LoadDouble)(RedisModuleIO *io);
+void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value);
+float REDISMODULE_API_FUNC(RedisModule_LoadFloat)(RedisModuleIO *io);
+void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);
+void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, 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);
+RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetContextFromIO)(RedisModuleIO *io);
+long long REDISMODULE_API_FUNC(RedisModule_Milliseconds)(void);
+void REDISMODULE_API_FUNC(RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, unsigned char *ele, size_t len);
+void REDISMODULE_API_FUNC(RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele);
+void REDISMODULE_API_FUNC(RedisModule_DigestEndSequence)(RedisModuleDigest *md);
+
+/* Experimental APIs */
+#ifdef REDISMODULE_EXPERIMENTAL_API
+RedisModuleBlockedClient *REDISMODULE_API_FUNC(RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(void*), long long timeout_ms);
+int REDISMODULE_API_FUNC(RedisModule_UnblockClient)(RedisModuleBlockedClient *bc, void *privdata);
+int REDISMODULE_API_FUNC(RedisModule_IsBlockedReplyRequest)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_IsBlockedTimeoutRequest)(RedisModuleCtx *ctx);
+void *REDISMODULE_API_FUNC(RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_AbortBlock)(RedisModuleBlockedClient *bc);
+RedisModuleCtx *REDISMODULE_API_FUNC(RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc);
+void REDISMODULE_API_FUNC(RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx);
+void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx);
+void REDISMODULE_API_FUNC(RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx);
+int REDISMODULE_API_FUNC(RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb);
+
+#endif
+
+/* This is included inline inside each Redis module. */
+static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));
+static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) {
+    void *getapifuncptr = ((void**)ctx)[0];
+    RedisModule_GetApi = (int (*)(const char *, void *)) (unsigned long)getapifuncptr;
+    REDISMODULE_GET_API(Alloc);
+    REDISMODULE_GET_API(Calloc);
+    REDISMODULE_GET_API(Free);
+    REDISMODULE_GET_API(Realloc);
+    REDISMODULE_GET_API(Strdup);
+    REDISMODULE_GET_API(CreateCommand);
+    REDISMODULE_GET_API(SetModuleAttribs);
+    REDISMODULE_GET_API(IsModuleNameBusy);
+    REDISMODULE_GET_API(WrongArity);
+    REDISMODULE_GET_API(ReplyWithLongLong);
+    REDISMODULE_GET_API(ReplyWithError);
+    REDISMODULE_GET_API(ReplyWithSimpleString);
+    REDISMODULE_GET_API(ReplyWithArray);
+    REDISMODULE_GET_API(ReplySetArrayLength);
+    REDISMODULE_GET_API(ReplyWithStringBuffer);
+    REDISMODULE_GET_API(ReplyWithString);
+    REDISMODULE_GET_API(ReplyWithNull);
+    REDISMODULE_GET_API(ReplyWithCallReply);
+    REDISMODULE_GET_API(ReplyWithDouble);
+    REDISMODULE_GET_API(ReplySetArrayLength);
+    REDISMODULE_GET_API(GetSelectedDb);
+    REDISMODULE_GET_API(SelectDb);
+    REDISMODULE_GET_API(OpenKey);
+    REDISMODULE_GET_API(CloseKey);
+    REDISMODULE_GET_API(KeyType);
+    REDISMODULE_GET_API(ValueLength);
+    REDISMODULE_GET_API(ListPush);
+    REDISMODULE_GET_API(ListPop);
+    REDISMODULE_GET_API(StringToLongLong);
+    REDISMODULE_GET_API(StringToDouble);
+    REDISMODULE_GET_API(Call);
+    REDISMODULE_GET_API(CallReplyProto);
+    REDISMODULE_GET_API(FreeCallReply);
+    REDISMODULE_GET_API(CallReplyInteger);
+    REDISMODULE_GET_API(CallReplyType);
+    REDISMODULE_GET_API(CallReplyLength);
+    REDISMODULE_GET_API(CallReplyArrayElement);
+    REDISMODULE_GET_API(CallReplyStringPtr);
+    REDISMODULE_GET_API(CreateStringFromCallReply);
+    REDISMODULE_GET_API(CreateString);
+    REDISMODULE_GET_API(CreateStringFromLongLong);
+    REDISMODULE_GET_API(CreateStringFromString);
+    REDISMODULE_GET_API(CreateStringPrintf);
+    REDISMODULE_GET_API(FreeString);
+    REDISMODULE_GET_API(StringPtrLen);
+    REDISMODULE_GET_API(AutoMemory);
+    REDISMODULE_GET_API(Replicate);
+    REDISMODULE_GET_API(ReplicateVerbatim);
+    REDISMODULE_GET_API(DeleteKey);
+    REDISMODULE_GET_API(UnlinkKey);
+    REDISMODULE_GET_API(StringSet);
+    REDISMODULE_GET_API(StringDMA);
+    REDISMODULE_GET_API(StringTruncate);
+    REDISMODULE_GET_API(GetExpire);
+    REDISMODULE_GET_API(SetExpire);
+    REDISMODULE_GET_API(ZsetAdd);
+    REDISMODULE_GET_API(ZsetIncrby);
+    REDISMODULE_GET_API(ZsetScore);
+    REDISMODULE_GET_API(ZsetRem);
+    REDISMODULE_GET_API(ZsetRangeStop);
+    REDISMODULE_GET_API(ZsetFirstInScoreRange);
+    REDISMODULE_GET_API(ZsetLastInScoreRange);
+    REDISMODULE_GET_API(ZsetFirstInLexRange);
+    REDISMODULE_GET_API(ZsetLastInLexRange);
+    REDISMODULE_GET_API(ZsetRangeCurrentElement);
+    REDISMODULE_GET_API(ZsetRangeNext);
+    REDISMODULE_GET_API(ZsetRangePrev);
+    REDISMODULE_GET_API(ZsetRangeEndReached);
+    REDISMODULE_GET_API(HashSet);
+    REDISMODULE_GET_API(HashGet);
+    REDISMODULE_GET_API(IsKeysPositionRequest);
+    REDISMODULE_GET_API(KeyAtPos);
+    REDISMODULE_GET_API(GetClientId);
+    REDISMODULE_GET_API(GetContextFlags);
+    REDISMODULE_GET_API(PoolAlloc);
+    REDISMODULE_GET_API(CreateDataType);
+    REDISMODULE_GET_API(ModuleTypeSetValue);
+    REDISMODULE_GET_API(ModuleTypeGetType);
+    REDISMODULE_GET_API(ModuleTypeGetValue);
+    REDISMODULE_GET_API(SaveUnsigned);
+    REDISMODULE_GET_API(LoadUnsigned);
+    REDISMODULE_GET_API(SaveSigned);
+    REDISMODULE_GET_API(LoadSigned);
+    REDISMODULE_GET_API(SaveString);
+    REDISMODULE_GET_API(SaveStringBuffer);
+    REDISMODULE_GET_API(LoadString);
+    REDISMODULE_GET_API(LoadStringBuffer);
+    REDISMODULE_GET_API(SaveDouble);
+    REDISMODULE_GET_API(LoadDouble);
+    REDISMODULE_GET_API(SaveFloat);
+    REDISMODULE_GET_API(LoadFloat);
+    REDISMODULE_GET_API(EmitAOF);
+    REDISMODULE_GET_API(Log);
+    REDISMODULE_GET_API(LogIOError);
+    REDISMODULE_GET_API(StringAppendBuffer);
+    REDISMODULE_GET_API(RetainString);
+    REDISMODULE_GET_API(StringCompare);
+    REDISMODULE_GET_API(GetContextFromIO);
+    REDISMODULE_GET_API(Milliseconds);
+    REDISMODULE_GET_API(DigestAddStringBuffer);
+    REDISMODULE_GET_API(DigestAddLongLong);
+    REDISMODULE_GET_API(DigestEndSequence);
+
+#ifdef REDISMODULE_EXPERIMENTAL_API
+    REDISMODULE_GET_API(GetThreadSafeContext);
+    REDISMODULE_GET_API(FreeThreadSafeContext);
+    REDISMODULE_GET_API(ThreadSafeContextLock);
+    REDISMODULE_GET_API(ThreadSafeContextUnlock);
+    REDISMODULE_GET_API(BlockClient);
+    REDISMODULE_GET_API(UnblockClient);
+    REDISMODULE_GET_API(IsBlockedReplyRequest);
+    REDISMODULE_GET_API(IsBlockedTimeoutRequest);
+    REDISMODULE_GET_API(GetBlockedClientPrivateData);
+    REDISMODULE_GET_API(AbortBlock);
+    REDISMODULE_GET_API(SubscribeToKeyspaceEvents);
+
+#endif
+
+    if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
+    RedisModule_SetModuleAttribs(ctx,name,ver,apiver);
+    return REDISMODULE_OK;
+}
+
+#else
+
+/* Things only defined for the modules core, not exported to modules
+ * including this file. */
+#define RedisModuleString robj
+
+#endif /* REDISMODULE_CORE */
+#endif /* REDISMOUDLE_H */
diff --git a/redismodule/m4/ax_prog_doxygen.m4 b/redismodule/m4/ax_prog_doxygen.m4
new file mode 100755 (executable)
index 0000000..a371f7f
--- /dev/null
@@ -0,0 +1,586 @@
+# ===========================================================================
+#     https://www.gnu.org/software/autoconf-archive/ax_prog_doxygen.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   DX_INIT_DOXYGEN(PROJECT-NAME, [DOXYFILE-PATH], [OUTPUT-DIR], ...)
+#   DX_DOXYGEN_FEATURE(ON|OFF)
+#   DX_DOT_FEATURE(ON|OFF)
+#   DX_HTML_FEATURE(ON|OFF)
+#   DX_CHM_FEATURE(ON|OFF)
+#   DX_CHI_FEATURE(ON|OFF)
+#   DX_MAN_FEATURE(ON|OFF)
+#   DX_RTF_FEATURE(ON|OFF)
+#   DX_XML_FEATURE(ON|OFF)
+#   DX_PDF_FEATURE(ON|OFF)
+#   DX_PS_FEATURE(ON|OFF)
+#
+# DESCRIPTION
+#
+#   The DX_*_FEATURE macros control the default setting for the given
+#   Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for
+#   generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML
+#   help (for MS users), 'CHI' for generating a separate .chi file by the
+#   .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate
+#   output formats. The environment variable DOXYGEN_PAPER_SIZE may be
+#   specified to override the default 'a4wide' paper size.
+#
+#   By default, HTML, PDF and PS documentation is generated as this seems to
+#   be the most popular and portable combination. MAN pages created by
+#   Doxygen are usually problematic, though by picking an appropriate subset
+#   and doing some massaging they might be better than nothing. CHM and RTF
+#   are specific for MS (note that you can't generate both HTML and CHM at
+#   the same time). The XML is rather useless unless you apply specialized
+#   post-processing to it.
+#
+#   The macros mainly control the default state of the feature. The use can
+#   override the default by specifying --enable or --disable. The macros
+#   ensure that contradictory flags are not given (e.g.,
+#   --enable-doxygen-html and --enable-doxygen-chm,
+#   --enable-doxygen-anything with --disable-doxygen, etc.) Finally, each
+#   feature will be automatically disabled (with a warning) if the required
+#   programs are missing.
+#
+#   Once all the feature defaults have been specified, call DX_INIT_DOXYGEN
+#   with the following parameters: a one-word name for the project for use
+#   as a filename base etc., an optional configuration file name (the
+#   default is '$(srcdir)/Doxyfile', the same as Doxygen's default), and an
+#   optional output directory name (the default is 'doxygen-doc'). To run
+#   doxygen multiple times for different configuration files and output
+#   directories provide more parameters: the second, forth, sixth, etc
+#   parameter are configuration file names and the third, fifth, seventh,
+#   etc parameter are output directories. No checking is done to catch
+#   duplicates.
+#
+#   Automake Support
+#
+#   The DX_RULES substitution can be used to add all needed rules to the
+#   Makefile. Note that this is a substitution without being a variable:
+#   only the @DX_RULES@ syntax will work.
+#
+#   The provided targets are:
+#
+#     doxygen-doc: Generate all doxygen documentation.
+#
+#     doxygen-run: Run doxygen, which will generate some of the
+#                  documentation (HTML, CHM, CHI, MAN, RTF, XML)
+#                  but will not do the post processing required
+#                  for the rest of it (PS, PDF).
+#
+#     doxygen-ps:  Generate doxygen PostScript documentation.
+#
+#     doxygen-pdf: Generate doxygen PDF documentation.
+#
+#   Note that by default these are not integrated into the automake targets.
+#   If doxygen is used to generate man pages, you can achieve this
+#   integration by setting man3_MANS to the list of man pages generated and
+#   then adding the dependency:
+#
+#     $(man3_MANS): doxygen-doc
+#
+#   This will cause make to run doxygen and generate all the documentation.
+#
+#   The following variable is intended for use in Makefile.am:
+#
+#     DX_CLEANFILES = everything to clean.
+#
+#   Then add this variable to MOSTLYCLEANFILES.
+#
+# LICENSE
+#
+#   Copyright (c) 2009 Oren Ben-Kiki <oren@ben-kiki.org>
+#   Copyright (c) 2015 Olaf Mandel <olaf@mandel.name>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+#serial 23
+
+## ----------##
+## Defaults. ##
+## ----------##
+
+DX_ENV=""
+AC_DEFUN([DX_FEATURE_doc],  ON)
+AC_DEFUN([DX_FEATURE_dot],  OFF)
+AC_DEFUN([DX_FEATURE_man],  OFF)
+AC_DEFUN([DX_FEATURE_html], ON)
+AC_DEFUN([DX_FEATURE_chm],  OFF)
+AC_DEFUN([DX_FEATURE_chi],  OFF)
+AC_DEFUN([DX_FEATURE_rtf],  OFF)
+AC_DEFUN([DX_FEATURE_xml],  OFF)
+AC_DEFUN([DX_FEATURE_pdf],  ON)
+AC_DEFUN([DX_FEATURE_ps],   ON)
+
+## --------------- ##
+## Private macros. ##
+## --------------- ##
+
+# DX_ENV_APPEND(VARIABLE, VALUE)
+# ------------------------------
+# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen and add it
+# as a substitution (but not a Makefile variable). The substitution
+# is skipped if the variable name is VERSION.
+AC_DEFUN([DX_ENV_APPEND],
+[AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])dnl
+m4_if([$1], [VERSION], [], [AC_SUBST([$1], [$2])dnl
+AM_SUBST_NOTMAKE([$1])])dnl
+])
+
+# DX_DIRNAME_EXPR
+# ---------------
+# Expand into a shell expression prints the directory part of a path.
+AC_DEFUN([DX_DIRNAME_EXPR],
+         [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']])
+
+# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF)
+# -------------------------------------
+# Expands according to the M4 (static) status of the feature.
+AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])])
+
+# DX_REQUIRE_PROG(VARIABLE, PROGRAM)
+# ----------------------------------
+# Require the specified program to be found for the DX_CURRENT_FEATURE to work.
+AC_DEFUN([DX_REQUIRE_PROG], [
+AC_PATH_TOOL([$1], [$2])
+if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then
+    AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION])
+    AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
+fi
+])
+
+# DX_TEST_FEATURE(FEATURE)
+# ------------------------
+# Expand to a shell expression testing whether the feature is active.
+AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1])
+
+# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE)
+# -------------------------------------------------
+# Verify that a required features has the right state before trying to turn on
+# the DX_CURRENT_FEATURE.
+AC_DEFUN([DX_CHECK_DEPEND], [
+test "$DX_FLAG_$1" = "$2" \
+|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1,
+                            requires, contradicts) doxygen-DX_CURRENT_FEATURE])
+])
+
+# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE)
+# ----------------------------------------------------------
+# Turn off the DX_CURRENT_FEATURE if the required feature is off.
+AC_DEFUN([DX_CLEAR_DEPEND], [
+test "$DX_FLAG_$1" = "$2" || AC_SUBST(DX_FLAG_[]DX_CURRENT_FEATURE, 0)
+])
+
+# DX_FEATURE_ARG(FEATURE, DESCRIPTION,
+#                CHECK_DEPEND, CLEAR_DEPEND,
+#                REQUIRE, DO-IF-ON, DO-IF-OFF)
+# --------------------------------------------
+# Parse the command-line option controlling a feature. CHECK_DEPEND is called
+# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND),
+# otherwise CLEAR_DEPEND is called to turn off the default state if a required
+# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional
+# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and
+# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature.
+AC_DEFUN([DX_ARG_ABLE], [
+    AC_DEFUN([DX_CURRENT_FEATURE], [$1])
+    AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2])
+    AC_ARG_ENABLE(doxygen-$1,
+                  [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1],
+                                                      [--enable-doxygen-$1]),
+                                  DX_IF_FEATURE([$1], [don't $2], [$2]))],
+                  [
+case "$enableval" in
+#(
+y|Y|yes|Yes|YES)
+    AC_SUBST([DX_FLAG_$1], 1)
+    $3
+;; #(
+n|N|no|No|NO)
+    AC_SUBST([DX_FLAG_$1], 0)
+;; #(
+*)
+    AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1])
+;;
+esac
+], [
+AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)])
+$4
+])
+if DX_TEST_FEATURE([$1]); then
+    $5
+    :
+fi
+if DX_TEST_FEATURE([$1]); then
+    $6
+    :
+else
+    $7
+    :
+fi
+])
+
+## -------------- ##
+## Public macros. ##
+## -------------- ##
+
+# DX_XXX_FEATURE(DEFAULT_STATE)
+# -----------------------------
+AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc],  [$1])])
+AC_DEFUN([DX_DOT_FEATURE],     [AC_DEFUN([DX_FEATURE_dot], [$1])])
+AC_DEFUN([DX_MAN_FEATURE],     [AC_DEFUN([DX_FEATURE_man],  [$1])])
+AC_DEFUN([DX_HTML_FEATURE],    [AC_DEFUN([DX_FEATURE_html], [$1])])
+AC_DEFUN([DX_CHM_FEATURE],     [AC_DEFUN([DX_FEATURE_chm],  [$1])])
+AC_DEFUN([DX_CHI_FEATURE],     [AC_DEFUN([DX_FEATURE_chi],  [$1])])
+AC_DEFUN([DX_RTF_FEATURE],     [AC_DEFUN([DX_FEATURE_rtf],  [$1])])
+AC_DEFUN([DX_XML_FEATURE],     [AC_DEFUN([DX_FEATURE_xml],  [$1])])
+AC_DEFUN([DX_XML_FEATURE],     [AC_DEFUN([DX_FEATURE_xml],  [$1])])
+AC_DEFUN([DX_PDF_FEATURE],     [AC_DEFUN([DX_FEATURE_pdf],  [$1])])
+AC_DEFUN([DX_PS_FEATURE],      [AC_DEFUN([DX_FEATURE_ps],   [$1])])
+
+# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR], ...)
+# --------------------------------------------------------------
+# PROJECT also serves as the base name for the documentation files.
+# The default CONFIG-FILE is "$(srcdir)/Doxyfile" and OUTPUT-DOC-DIR is
+# "doxygen-doc".
+# More arguments are interpreted as interleaved CONFIG-FILE and
+# OUTPUT-DOC-DIR values.
+AC_DEFUN([DX_INIT_DOXYGEN], [
+
+# Files:
+AC_SUBST([DX_PROJECT], [$1])
+AC_SUBST([DX_CONFIG], ['ifelse([$2], [], [$(srcdir)/Doxyfile], [$2])'])
+AC_SUBST([DX_DOCDIR], ['ifelse([$3], [], [doxygen-doc], [$3])'])
+m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 4, m4_count($@), 2,
+      [AC_SUBST([DX_CONFIG]m4_eval(DX_i[/2]),
+                'm4_default_nblank_quoted(m4_argn(DX_i, $@),
+                                          [$(srcdir)/Doxyfile])')])])dnl
+m4_if(m4_eval(3 < m4_count($@)), 1, [m4_for([DX_i], 5, m4_count($@,), 2,
+      [AC_SUBST([DX_DOCDIR]m4_eval([(]DX_i[-1)/2]),
+                'm4_default_nblank_quoted(m4_argn(DX_i, $@),
+                                          [doxygen-doc])')])])dnl
+m4_define([DX_loop], m4_dquote(m4_if(m4_eval(3 < m4_count($@)), 1,
+          [m4_for([DX_i], 4, m4_count($@), 2, [, m4_eval(DX_i[/2])])],
+          [])))dnl
+
+# Environment variables used inside doxygen.cfg:
+DX_ENV_APPEND(SRCDIR, $srcdir)
+DX_ENV_APPEND(PROJECT, $DX_PROJECT)
+DX_ENV_APPEND(VERSION, $PACKAGE_VERSION)
+
+# Doxygen itself:
+DX_ARG_ABLE(doc, [generate any doxygen documentation],
+            [],
+            [],
+            [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen)
+             DX_REQUIRE_PROG([DX_PERL], perl)],
+            [DX_ENV_APPEND(PERL_PATH, $DX_PERL)])
+
+# Dot for graphics:
+DX_ARG_ABLE(dot, [generate graphics for doxygen documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_DOT], dot)],
+            [DX_ENV_APPEND(HAVE_DOT, YES)
+             DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])],
+            [DX_ENV_APPEND(HAVE_DOT, NO)])
+
+# Man pages generation:
+DX_ARG_ABLE(man, [generate doxygen manual pages],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_MAN, YES)],
+            [DX_ENV_APPEND(GENERATE_MAN, NO)])
+
+# RTF file generation:
+DX_ARG_ABLE(rtf, [generate doxygen RTF documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_RTF, YES)],
+            [DX_ENV_APPEND(GENERATE_RTF, NO)])
+
+# XML file generation:
+DX_ARG_ABLE(xml, [generate doxygen XML documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_XML, YES)],
+            [DX_ENV_APPEND(GENERATE_XML, NO)])
+
+# (Compressed) HTML help generation:
+DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_HHC], hhc)],
+            [DX_ENV_APPEND(HHC_PATH, $DX_HHC)
+             DX_ENV_APPEND(GENERATE_HTML, YES)
+             DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
+            [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
+
+# Separate CHI file generation.
+DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file],
+            [DX_CHECK_DEPEND(chm, 1)],
+            [DX_CLEAR_DEPEND(chm, 1)],
+            [],
+            [DX_ENV_APPEND(GENERATE_CHI, YES)],
+            [DX_ENV_APPEND(GENERATE_CHI, NO)])
+
+# Plain HTML pages generation:
+DX_ARG_ABLE(html, [generate doxygen plain HTML documentation],
+            [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)],
+            [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)],
+            [],
+            [DX_ENV_APPEND(GENERATE_HTML, YES)],
+            [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)])
+
+# PostScript file generation:
+DX_ARG_ABLE(ps, [generate doxygen PostScript documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_LATEX], latex)
+             DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+             DX_REQUIRE_PROG([DX_DVIPS], dvips)
+             DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# PDF file generation:
+DX_ARG_ABLE(pdf, [generate doxygen PDF documentation],
+            [DX_CHECK_DEPEND(doc, 1)],
+            [DX_CLEAR_DEPEND(doc, 1)],
+            [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex)
+             DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex)
+             DX_REQUIRE_PROG([DX_EGREP], egrep)])
+
+# LaTeX generation for PS and/or PDF:
+if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then
+    DX_ENV_APPEND(GENERATE_LATEX, YES)
+else
+    DX_ENV_APPEND(GENERATE_LATEX, NO)
+fi
+
+# Paper size for PS and/or PDF:
+AC_ARG_VAR(DOXYGEN_PAPER_SIZE,
+           [a4wide (default), a4, letter, legal or executive])
+case "$DOXYGEN_PAPER_SIZE" in
+#(
+"")
+    AC_SUBST(DOXYGEN_PAPER_SIZE, "")
+;; #(
+a4wide|a4|letter|legal|executive)
+    DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE)
+;; #(
+*)
+    AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE'])
+;;
+esac
+
+# Rules:
+AS_IF([[test $DX_FLAG_html -eq 1]],
+[[DX_SNIPPET_html="## ------------------------------- ##
+## Rules specific for HTML output. ##
+## ------------------------------- ##
+
+DX_CLEAN_HTML = \$(DX_DOCDIR)/html]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+                \$(DX_DOCDIR]DX_i[)/html]])[
+
+"]],
+[[DX_SNIPPET_html=""]])
+AS_IF([[test $DX_FLAG_chi -eq 1]],
+[[DX_SNIPPET_chi="
+DX_CLEAN_CHI = \$(DX_DOCDIR)/\$(PACKAGE).chi]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+               \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).chi]])["]],
+[[DX_SNIPPET_chi=""]])
+AS_IF([[test $DX_FLAG_chm -eq 1]],
+[[DX_SNIPPET_chm="## ------------------------------ ##
+## Rules specific for CHM output. ##
+## ------------------------------ ##
+
+DX_CLEAN_CHM = \$(DX_DOCDIR)/chm]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+               \$(DX_DOCDIR]DX_i[)/chm]])[\
+${DX_SNIPPET_chi}
+
+"]],
+[[DX_SNIPPET_chm=""]])
+AS_IF([[test $DX_FLAG_man -eq 1]],
+[[DX_SNIPPET_man="## ------------------------------ ##
+## Rules specific for MAN output. ##
+## ------------------------------ ##
+
+DX_CLEAN_MAN = \$(DX_DOCDIR)/man]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+               \$(DX_DOCDIR]DX_i[)/man]])[
+
+"]],
+[[DX_SNIPPET_man=""]])
+AS_IF([[test $DX_FLAG_rtf -eq 1]],
+[[DX_SNIPPET_rtf="## ------------------------------ ##
+## Rules specific for RTF output. ##
+## ------------------------------ ##
+
+DX_CLEAN_RTF = \$(DX_DOCDIR)/rtf]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+               \$(DX_DOCDIR]DX_i[)/rtf]])[
+
+"]],
+[[DX_SNIPPET_rtf=""]])
+AS_IF([[test $DX_FLAG_xml -eq 1]],
+[[DX_SNIPPET_xml="## ------------------------------ ##
+## Rules specific for XML output. ##
+## ------------------------------ ##
+
+DX_CLEAN_XML = \$(DX_DOCDIR)/xml]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+               \$(DX_DOCDIR]DX_i[)/xml]])[
+
+"]],
+[[DX_SNIPPET_xml=""]])
+AS_IF([[test $DX_FLAG_ps -eq 1]],
+[[DX_SNIPPET_ps="## ----------------------------- ##
+## Rules specific for PS output. ##
+## ----------------------------- ##
+
+DX_CLEAN_PS = \$(DX_DOCDIR)/\$(PACKAGE).ps]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+              \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps]])[
+
+DX_PS_GOAL = doxygen-ps
+
+doxygen-ps: \$(DX_CLEAN_PS)
+
+]m4_foreach([DX_i], [DX_loop],
+[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).ps: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag
+       \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\
+       rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\
+       \$(DX_LATEX) refman.tex; \\
+       \$(DX_MAKEINDEX) refman.idx; \\
+       \$(DX_LATEX) refman.tex; \\
+       countdown=5; \\
+       while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\
+                         refman.log > /dev/null 2>&1 \\
+          && test \$\$countdown -gt 0; do \\
+           \$(DX_LATEX) refman.tex; \\
+            countdown=\`expr \$\$countdown - 1\`; \\
+       done; \\
+       \$(DX_DVIPS) -o ../\$(PACKAGE).ps refman.dvi
+
+]])["]],
+[[DX_SNIPPET_ps=""]])
+AS_IF([[test $DX_FLAG_pdf -eq 1]],
+[[DX_SNIPPET_pdf="## ------------------------------ ##
+## Rules specific for PDF output. ##
+## ------------------------------ ##
+
+DX_CLEAN_PDF = \$(DX_DOCDIR)/\$(PACKAGE).pdf]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+               \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf]])[
+
+DX_PDF_GOAL = doxygen-pdf
+
+doxygen-pdf: \$(DX_CLEAN_PDF)
+
+]m4_foreach([DX_i], [DX_loop],
+[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).pdf: \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag
+       \$(DX_V_LATEX)cd \$(DX_DOCDIR]DX_i[)/latex; \\
+       rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \\
+       \$(DX_PDFLATEX) refman.tex; \\
+       \$(DX_MAKEINDEX) refman.idx; \\
+       \$(DX_PDFLATEX) refman.tex; \\
+       countdown=5; \\
+       while \$(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \\
+                         refman.log > /dev/null 2>&1 \\
+          && test \$\$countdown -gt 0; do \\
+           \$(DX_PDFLATEX) refman.tex; \\
+           countdown=\`expr \$\$countdown - 1\`; \\
+       done; \\
+       mv refman.pdf ../\$(PACKAGE).pdf
+
+]])["]],
+[[DX_SNIPPET_pdf=""]])
+AS_IF([[test $DX_FLAG_ps -eq 1 -o $DX_FLAG_pdf -eq 1]],
+[[DX_SNIPPET_latex="## ------------------------------------------------- ##
+## Rules specific for LaTeX (shared for PS and PDF). ##
+## ------------------------------------------------- ##
+
+DX_V_LATEX = \$(_DX_v_LATEX_\$(V))
+_DX_v_LATEX_ = \$(_DX_v_LATEX_\$(AM_DEFAULT_VERBOSITY))
+_DX_v_LATEX_0 = @echo \"  LATEX \" \$][@;
+
+DX_CLEAN_LATEX = \$(DX_DOCDIR)/latex]dnl
+m4_foreach([DX_i], [m4_shift(DX_loop)], [[\\
+                 \$(DX_DOCDIR]DX_i[)/latex]])[
+
+"]],
+[[DX_SNIPPET_latex=""]])
+
+AS_IF([[test $DX_FLAG_doc -eq 1]],
+[[DX_SNIPPET_doc="## --------------------------------- ##
+## Format-independent Doxygen rules. ##
+## --------------------------------- ##
+
+${DX_SNIPPET_html}\
+${DX_SNIPPET_chm}\
+${DX_SNIPPET_man}\
+${DX_SNIPPET_rtf}\
+${DX_SNIPPET_xml}\
+${DX_SNIPPET_ps}\
+${DX_SNIPPET_pdf}\
+${DX_SNIPPET_latex}\
+DX_V_DXGEN = \$(_DX_v_DXGEN_\$(V))
+_DX_v_DXGEN_ = \$(_DX_v_DXGEN_\$(AM_DEFAULT_VERBOSITY))
+_DX_v_DXGEN_0 = @echo \"  DXGEN \" \$<;
+
+.PHONY: doxygen-run doxygen-doc \$(DX_PS_GOAL) \$(DX_PDF_GOAL)
+
+.INTERMEDIATE: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL)
+
+doxygen-run:]m4_foreach([DX_i], [DX_loop],
+                         [[ \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag]])[
+
+doxygen-doc: doxygen-run \$(DX_PS_GOAL) \$(DX_PDF_GOAL)
+
+]m4_foreach([DX_i], [DX_loop],
+[[\$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag: \$(DX_CONFIG]DX_i[) \$(pkginclude_HEADERS)
+       \$(A""M_V_at)rm -rf \$(DX_DOCDIR]DX_i[)
+       \$(DX_V_DXGEN)\$(DX_ENV) DOCDIR=\$(DX_DOCDIR]DX_i[) \$(DX_DOXYGEN) \$(DX_CONFIG]DX_i[)
+       \$(A""M_V_at)echo Timestamp >\$][@
+
+]])dnl
+[DX_CLEANFILES = \\]
+m4_foreach([DX_i], [DX_loop],
+[[     \$(DX_DOCDIR]DX_i[)/doxygen_sqlite3.db \\
+       \$(DX_DOCDIR]DX_i[)/\$(PACKAGE).tag \\
+]])dnl
+[      -r \\
+       \$(DX_CLEAN_HTML) \\
+       \$(DX_CLEAN_CHM) \\
+       \$(DX_CLEAN_CHI) \\
+       \$(DX_CLEAN_MAN) \\
+       \$(DX_CLEAN_RTF) \\
+       \$(DX_CLEAN_XML) \\
+       \$(DX_CLEAN_PS) \\
+       \$(DX_CLEAN_PDF) \\
+       \$(DX_CLEAN_LATEX)"]],
+[[DX_SNIPPET_doc=""]])
+AC_SUBST([DX_RULES],
+["${DX_SNIPPET_doc}"])dnl
+AM_SUBST_NOTMAKE([DX_RULES])
+
+#For debugging:
+#echo DX_FLAG_doc=$DX_FLAG_doc
+#echo DX_FLAG_dot=$DX_FLAG_dot
+#echo DX_FLAG_man=$DX_FLAG_man
+#echo DX_FLAG_html=$DX_FLAG_html
+#echo DX_FLAG_chm=$DX_FLAG_chm
+#echo DX_FLAG_chi=$DX_FLAG_chi
+#echo DX_FLAG_rtf=$DX_FLAG_rtf
+#echo DX_FLAG_xml=$DX_FLAG_xml
+#echo DX_FLAG_pdf=$DX_FLAG_pdf
+#echo DX_FLAG_ps=$DX_FLAG_ps
+#echo DX_ENV=$DX_ENV
+])
diff --git a/redismodule/run-tests.sh.in b/redismodule/run-tests.sh.in
new file mode 100755 (executable)
index 0000000..57a6843
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+./redismodule_ut
+if [ $? != 0 ]
+then
+   exit -1
+fi
+subsystem=redismodule
+prbname=libredismodule
+UT_DIR=${VOBTAG}/$subsystem
+gcda_files=$(find $UT_DIR -name '*.gcda')
+if [ -z "$gcda_files" ]
+then
+   UT_DIR=${VOBTAG}/$subsystem/buildme.ut
+   gcda_files=$(find $UT_DIR -name '*.gcda')
+   if [ -z "$gcda_files" ]
+   then
+      echo "no gcda files, so cannot generate UT coverage"
+      exit 0
+   fi
+fi
+echo "gcda file is found"
+lcov_src_patterns='*/redismodule/src/*'
+lcov_objs_root=${VOBTAG}/$subsystem
+lcov_remove_patterns='*/tst/*'
+${VOBTAG}/flexiserver/build/ut_lcov.sh ${lcov_objs_root} ${lcov_src_patterns} ${lcov_remove_patterns}
+
diff --git a/redismodule/src/exstrings.c b/redismodule/src/exstrings.c
new file mode 100755 (executable)
index 0000000..66f31c2
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#include "redismodule.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "../../redismodule/include/redismodule.h"
+
+#ifdef __UT__
+#include "exstringsStub.h"
+#endif
+
+/* make sure the response is not NULL or an error.
+sends the error to the client and exit the current function if its */
+#define  ASSERT_NOERROR(r) \
+    if (r == NULL) { \
+        return RedisModule_ReplyWithError(ctx,"ERR reply is NULL"); \
+    } else if (RedisModule_CallReplyType(r) == REDISMODULE_REPLY_ERROR) { \
+        RedisModule_ReplyWithCallReply(ctx,r); \
+        RedisModule_FreeCallReply(r); \
+        return REDISMODULE_ERR; \
+    }
+
+#define OBJ_OP_NO 0
+#define OBJ_OP_XX (1<<1)     /* OP if key exist */
+#define OBJ_OP_NX (1<<2)     /* OP if key not exist */
+#define OBJ_OP_IE (1<<4)     /* OP if equal old value */
+#define OBJ_OP_NE (1<<5)     /* OP if not equal old value */
+
+
+int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc < 4)
+        return RedisModule_WrongArity(ctx);
+    else
+        oldvalstr = argv[3];
+
+    /*Check if key type is string*/
+    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+        REDISMODULE_READ);
+    int type = RedisModule_KeyType(key);
+    RedisModule_CloseKey(key);
+
+    if (type == REDISMODULE_KEYTYPE_EMPTY) {
+        if (flag == OBJ_OP_IE){
+            RedisModule_ReplyWithNull(ctx);
+            return REDISMODULE_OK;
+        }
+    } else if (type != REDISMODULE_KEYTYPE_STRING) {
+        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+    }
+
+    /*Get the value*/
+    reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+    size_t curlen=0, oldvallen=0;
+    const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+    const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+    if (((flag == OBJ_OP_IE) &&
+        (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+        ||
+        ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+          !strncmp(oldval, curval, curlen))) {
+        RedisModule_FreeCallReply(reply);
+        return RedisModule_ReplyWithNull(ctx);
+    }
+    RedisModule_FreeCallReply(reply);
+
+    /* Prepare the arguments for the command. */
+    int i, j=0, cmdargc=argc-2;
+    RedisModuleString *cmdargv[cmdargc];
+    for (i = 1; i < argc; i++) {
+        if (i == 3)
+            continue;
+        cmdargv[j++] = argv[i];
+    }
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "SET", "v!", cmdargv, cmdargc);
+    ASSERT_NOERROR(reply)
+    RedisModule_ReplyWithCallReply(ctx, reply);
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc == 3)
+        oldvalstr = argv[2];
+    else
+        return RedisModule_WrongArity(ctx);
+
+    /*Check if key type is string*/
+    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+        REDISMODULE_READ);
+    int type = RedisModule_KeyType(key);
+    RedisModule_CloseKey(key);
+
+    if (type == REDISMODULE_KEYTYPE_EMPTY) {
+        return RedisModule_ReplyWithLongLong(ctx, 0);
+    } else if (type != REDISMODULE_KEYTYPE_STRING) {
+        return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+    }
+
+    /*Get the value*/
+    reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+    size_t curlen = 0, oldvallen = 0;
+    const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+    const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+    if (((flag == OBJ_OP_IE) &&
+        (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+        ||
+        ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+          !strncmp(oldval, curval, curlen))) {
+        RedisModule_FreeCallReply(reply);
+        return RedisModule_ReplyWithLongLong(ctx, 0);
+    }
+    RedisModule_FreeCallReply(reply);
+
+    /* Prepare the arguments for the command. */
+    int cmdargc=1;
+    RedisModuleString *cmdargv[1];
+    cmdargv[0] = argv[1];
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, cmdargc);
+    ASSERT_NOERROR(reply)
+    RedisModule_ReplyWithCallReply(ctx, reply);
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return delStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return delStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc != 2)
+        return RedisModule_WrongArity(ctx);
+
+    /* Call the command to get keys with pattern. */
+    reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+
+    /* Prepare the arguments for the command. */
+    size_t items = RedisModule_CallReplyLength(reply);
+    if (items == 0) {
+        //RedisModule_ReplyWithArray(ctx, items);
+        RedisModule_ReplyWithCallReply(ctx, reply);
+        RedisModule_FreeCallReply(reply);
+    }
+    else {
+        RedisModuleString *cmdargv[items];
+        size_t i=0, j;
+        for (j = 0; j < items; j++) {
+           RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
+           cmdargv[i++] = rms;
+
+           /*Assume all keys via SDL is string type for sake of saving time*/
+#if 0
+           /*Check if key type is string*/
+           RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
+
+           if (key) {
+               int type = RedisModule_KeyType(key);
+               RedisModule_CloseKey(key);
+               if (type == REDISMODULE_KEYTYPE_STRING) {
+                   cmdargv[i++] = rms;
+               }
+           } else {
+               RedisModule_CloseKey(key);
+           }
+#endif
+        }
+        RedisModule_FreeCallReply(reply);
+
+        reply = RedisModule_Call(ctx, "MGET", "v", cmdargv, i);
+        ASSERT_NOERROR(reply)
+        items = RedisModule_CallReplyLength(reply);
+        RedisModule_ReplyWithArray(ctx, i*2);
+        for (j = 0; (j<items && j<i); j++) {
+           RedisModule_ReplyWithString(ctx, cmdargv[j]);
+           RedisModule_ReplyWithString(ctx, RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j)));
+        }
+
+        RedisModule_FreeCallReply(reply);
+    }
+
+    return REDISMODULE_OK;
+}
+
+int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    RedisModuleCallReply *reply = NULL;
+
+    if (argc != 2)
+        return RedisModule_WrongArity(ctx);
+
+    /* Call the command to get keys with pattern. */
+    reply = RedisModule_Call(ctx, "KEYS", "s", argv[1]);
+    ASSERT_NOERROR(reply)
+
+    /* Prepare the arguments for the command. */
+    size_t items = RedisModule_CallReplyLength(reply);
+    if (items == 0) {
+        RedisModule_ReplyWithLongLong(ctx, 0);
+        RedisModule_FreeCallReply(reply);
+    }
+    else {
+        RedisModuleString *cmdargv[items];
+        size_t i=0, j;
+        for (j = 0; j < items; j++) {
+           RedisModuleString *rms = RedisModule_CreateStringFromCallReply(RedisModule_CallReplyArrayElement(reply, j));
+           cmdargv[i++] = rms;
+
+           /*Assume all keys via SDL is string type for sake of saving time*/
+#if 0
+           //Check if key type is string
+           RedisModuleKey *key = RedisModule_OpenKey(ctx, rms ,REDISMODULE_READ);
+
+           if (key) {
+               int type = RedisModule_KeyType(key);
+               RedisModule_CloseKey(key);
+               if (type == REDISMODULE_KEYTYPE_STRING) {
+                   cmdargv[i++] = rms;
+               }
+           } else {
+               RedisModule_CloseKey(key);
+           }
+#endif
+        }
+        RedisModule_FreeCallReply(reply);
+
+        reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, i);
+        ASSERT_NOERROR(reply)
+        RedisModule_ReplyWithCallReply(ctx, reply);
+        RedisModule_FreeCallReply(reply);
+
+    }
+
+    return REDISMODULE_OK;
+}
+
+int setPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (flag == OBJ_OP_NO) {
+        if (argc < 5 || (argc % 2) == 0)
+            return RedisModule_WrongArity(ctx);
+        else {
+            channel = argv[argc-2];
+            message = argv[argc-1];
+        }
+    } else if (flag == OBJ_OP_XX || flag == OBJ_OP_NX) {
+        if (argc != 5)
+            return RedisModule_WrongArity(ctx);
+        else {
+            channel = argv[3];
+            message = argv[4];
+        }
+    } else {
+        if (argc != 6)
+            return RedisModule_WrongArity(ctx);
+        else {
+            oldvalstr = argv[3];
+            channel = argv[4];
+            message = argv[5];
+        }
+    }
+
+    /*Check if key type is string*/
+    RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+        REDISMODULE_READ);
+    int type = RedisModule_KeyType(key);
+    RedisModule_CloseKey(key);
+
+    if (flag != OBJ_OP_NO) {
+        if (type == REDISMODULE_KEYTYPE_EMPTY) {
+            if (flag == OBJ_OP_IE || flag == OBJ_OP_XX){
+                return RedisModule_ReplyWithNull(ctx);
+            }
+        } else if (flag == OBJ_OP_NX) {
+            return RedisModule_ReplyWithNull(ctx);
+        } else if (type != REDISMODULE_KEYTYPE_STRING) {
+            return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+        }
+    }
+
+    if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
+        /*Get the value*/
+        reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+        ASSERT_NOERROR(reply)
+        size_t curlen = 0, oldvallen = 0;
+        const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+        const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+        if (((flag == OBJ_OP_IE) &&
+            (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+            ||
+            ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+              !strncmp(oldval, curval, curlen))) {
+            RedisModule_FreeCallReply(reply);
+            return RedisModule_ReplyWithNull(ctx);
+        }
+        RedisModule_FreeCallReply(reply);
+    }
+
+
+    /* Prepare the arguments for the command. */
+    int i, j=0, cmdargc=argc-3;
+    RedisModuleString *cmdargv[cmdargc];
+    for (i = 1; i < argc-2; i++) {
+        if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 3))
+            continue;
+        cmdargv[j++] = argv[i];
+    }
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "MSET", "v!", cmdargv, j);
+    ASSERT_NOERROR(reply)
+    int replytype = RedisModule_CallReplyType(reply);
+    if (replytype == REDISMODULE_REPLY_NULL) {
+        RedisModule_ReplyWithNull(ctx);
+    }
+    else {
+        cmdargc = 2;
+        cmdargv[0] = channel;
+        cmdargv[1] = message;
+        RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
+        RedisModule_FreeCallReply(pubreply);
+        RedisModule_ReplyWithCallReply(ctx, reply);
+    }
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
+}
+
+int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NX);
+}
+
+int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+    return setPubStringGenericCommand(ctx, argv, argc, OBJ_OP_XX);
+}
+
+int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
+                                       int argc, const int flag)
+{
+    RedisModuleString *oldvalstr = NULL, *channel = NULL, *message = NULL;
+    RedisModuleCallReply *reply = NULL;
+
+    if (flag == OBJ_OP_NO) {
+        if (argc < 4)
+            return RedisModule_WrongArity(ctx);
+        else {
+            channel = argv[argc-2];
+            message = argv[argc-1];
+        }
+    } else {
+        if (argc != 5)
+            return RedisModule_WrongArity(ctx);
+        else {
+            oldvalstr = argv[2];
+            channel = argv[3];
+            message = argv[4];
+        }
+    }
+
+    if (flag != OBJ_OP_NO) {
+        /*Check if key type is string*/
+        RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1],
+            REDISMODULE_READ);
+        int type = RedisModule_KeyType(key);
+        RedisModule_CloseKey(key);
+
+        if (type == REDISMODULE_KEYTYPE_EMPTY) {
+            return RedisModule_ReplyWithLongLong(ctx, 0);
+        } else if (type != REDISMODULE_KEYTYPE_STRING) {
+            return RedisModule_ReplyWithError(ctx,REDISMODULE_ERRORMSG_WRONGTYPE);
+        }
+    }
+
+    if (flag == OBJ_OP_IE || flag == OBJ_OP_NE) {
+        /*Get the value*/
+        reply = RedisModule_Call(ctx, "GET", "s", argv[1]);
+        ASSERT_NOERROR(reply)
+        size_t curlen = 0, oldvallen = 0;
+        const char *oldval = RedisModule_StringPtrLen(oldvalstr, &oldvallen);
+        const char *curval = RedisModule_CallReplyStringPtr(reply, &curlen);
+        if (((flag == OBJ_OP_IE) &&
+            (!curval || (oldvallen != curlen) || strncmp(oldval, curval, curlen)))
+            ||
+            ((flag == OBJ_OP_NE) && curval && (oldvallen == curlen) &&
+              !strncmp(oldval, curval, curlen))) {
+            RedisModule_FreeCallReply(reply);
+            return RedisModule_ReplyWithLongLong(ctx, 0);
+        }
+        RedisModule_FreeCallReply(reply);
+    }
+
+
+    /* Prepare the arguments for the command. */
+    int i, j=0, cmdargc=argc-3;
+    RedisModuleString *cmdargv[cmdargc];
+    for (i = 1; i < argc-2; i++) {
+        if ((flag == OBJ_OP_IE || flag == OBJ_OP_NE) && (i == 2))
+            continue;
+        cmdargv[j++] = argv[i];
+    }
+
+    /* Call the command and pass back the reply. */
+    reply = RedisModule_Call(ctx, "UNLINK", "v!", cmdargv, j);
+    ASSERT_NOERROR(reply)
+    int replytype = RedisModule_CallReplyType(reply);
+    if (replytype == REDISMODULE_REPLY_NULL) {
+        RedisModule_ReplyWithNull(ctx);
+    }
+    else if (RedisModule_CallReplyInteger(reply) == 0) {
+        RedisModule_ReplyWithCallReply(ctx, reply);
+    } else {
+        cmdargc = 2;
+        cmdargv[0] = channel;
+        cmdargv[1] = message;
+        RedisModuleCallReply *pubreply = RedisModule_Call(ctx, "PUBLISH", "v", cmdargv, cmdargc);
+        RedisModule_FreeCallReply(pubreply);
+        RedisModule_ReplyWithCallReply(ctx, reply);
+    }
+
+    RedisModule_FreeCallReply(reply);
+    return REDISMODULE_OK;
+}
+
+int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+   return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NO);
+}
+
+int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+   return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_IE);
+}
+
+int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+   return delPubStringGenericCommand(ctx, argv, argc, OBJ_OP_NE);
+}
+
+/* This function must be present on each Redis module. It is used in order to
+ * register the commands into the Redis server. */
+int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+    REDISMODULE_NOT_USED(argv);
+    REDISMODULE_NOT_USED(argc);
+
+    if (RedisModule_Init(ctx,"exstrings",1,REDISMODULE_APIVER_1)
+        == REDISMODULE_ERR) return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setie",
+        SetIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setne",
+        SetNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delie",
+        DelIE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delne",
+        DelNE_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"nget",
+        NGet_RedisCommand,"readonly",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"ndel",
+        NDel_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"msetpub",
+        SetPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setiepub",
+        SetIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setnepub",
+        SetNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setxxpub",
+        SetXXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"setnxpub",
+        SetNXPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delpub",
+        DelPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"deliepub",
+        DelIEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    if (RedisModule_CreateCommand(ctx,"delnepub",
+        DelNEPub_RedisCommand,"write deny-oom",1,1,1) == REDISMODULE_ERR)
+        return REDISMODULE_ERR;
+
+    return REDISMODULE_OK;
+}
diff --git a/redismodule/tst/mock/include/exstringsStub.h b/redismodule/tst/mock/include/exstringsStub.h
new file mode 100755 (executable)
index 0000000..dc426cd
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#ifndef EXSTRINGSTUB_H_
+#define EXSTRINGSTUB_H_
+
+
+#include "redismodule.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "../../../../redismodule/tst/mock/include/redismodule.h"
+
+int setStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, const int flag);
+int SetIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ;
+int delStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc, const int flag);
+int DelIE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int DelNE_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetNXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int SetXXPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int DelPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int DelIEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int DelNEPub_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int NGet_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int NDel_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+int setPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,          int argc, const int flag);
+int delPubStringGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,          int argc, const int flag);
+
+
+#endif
diff --git a/redismodule/tst/mock/include/redismodule.h b/redismodule/tst/mock/include/redismodule.h
new file mode 100755 (executable)
index 0000000..df79c72
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#ifndef REDISMODULE_H
+#define REDISMODULE_H
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+
+/* ---------------- Defines common between core and modules --------------- */
+
+
+
+
+/* Error status return values. */
+#define REDISMODULE_OK 0
+#define REDISMODULE_ERR 1
+
+/* API versions. */
+#define REDISMODULE_APIVER_1 1
+
+/* API flags and constants */
+#define REDISMODULE_READ (1<<0)
+#define REDISMODULE_WRITE (1<<1)
+
+#define REDISMODULE_LIST_HEAD 0
+#define REDISMODULE_LIST_TAIL 1
+
+/* Key types. */
+#define REDISMODULE_KEYTYPE_EMPTY 0
+#define REDISMODULE_KEYTYPE_STRING 1
+#define REDISMODULE_KEYTYPE_LIST 2
+#define REDISMODULE_KEYTYPE_HASH 3
+#define REDISMODULE_KEYTYPE_SET 4
+#define REDISMODULE_KEYTYPE_ZSET 5
+#define REDISMODULE_KEYTYPE_MODULE 6
+
+/* Reply types. */
+#define REDISMODULE_REPLY_UNKNOWN -1
+#define REDISMODULE_REPLY_STRING 0
+#define REDISMODULE_REPLY_ERROR 1
+#define REDISMODULE_REPLY_INTEGER 2
+#define REDISMODULE_REPLY_ARRAY 3
+#define REDISMODULE_REPLY_NULL 4
+
+/* Postponed array length. */
+#define REDISMODULE_POSTPONED_ARRAY_LEN -1
+
+/* Expire */
+#define REDISMODULE_NO_EXPIRE -1
+
+/* Sorted set API flags. */
+#define REDISMODULE_ZADD_XX      (1<<0)
+#define REDISMODULE_ZADD_NX      (1<<1)
+#define REDISMODULE_ZADD_ADDED   (1<<2)
+#define REDISMODULE_ZADD_UPDATED (1<<3)
+#define REDISMODULE_ZADD_NOP     (1<<4)
+
+/* Hash API flags. */
+#define REDISMODULE_HASH_NONE       0
+#define REDISMODULE_HASH_NX         (1<<0)
+#define REDISMODULE_HASH_XX         (1<<1)
+#define REDISMODULE_HASH_CFIELDS    (1<<2)
+#define REDISMODULE_HASH_EXISTS     (1<<3)
+
+/* Context Flags: Info about the current context returned by RM_GetContextFlags */
+
+/* The command is running in the context of a Lua script */
+#define REDISMODULE_CTX_FLAGS_LUA 0x0001
+/* The command is running inside a Redis transaction */
+#define REDISMODULE_CTX_FLAGS_MULTI 0x0002
+/* The instance is a master */
+#define REDISMODULE_CTX_FLAGS_MASTER 0x0004
+/* The instance is a slave */
+#define REDISMODULE_CTX_FLAGS_SLAVE 0x0008
+/* The instance is read-only (usually meaning it's a slave as well) */
+#define REDISMODULE_CTX_FLAGS_READONLY 0x0010
+/* The instance is running in cluster mode */
+#define REDISMODULE_CTX_FLAGS_CLUSTER 0x0020
+/* The instance has AOF enabled */
+#define REDISMODULE_CTX_FLAGS_AOF 0x0040 //
+/* The instance has RDB enabled */
+#define REDISMODULE_CTX_FLAGS_RDB 0x0080 //
+/* The instance has Maxmemory set */
+#define REDISMODULE_CTX_FLAGS_MAXMEMORY 0x0100
+/* Maxmemory is set and has an eviction policy that may delete keys */
+#define REDISMODULE_CTX_FLAGS_EVICT 0x0200
+
+
+/* A special pointer that we can use between the core and the module to signal
+ * field deletion, and that is impossible to be a valid pointer. */
+#define REDISMODULE_HASH_DELETE ((RedisModuleString*)(long)1)
+
+/* Error messages. */
+#define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value"
+
+#define REDISMODULE_POSITIVE_INFINITE (1.0/0.0)
+#define REDISMODULE_NEGATIVE_INFINITE (-1.0/0.0)
+
+#define REDISMODULE_NOT_USED(V) ((void) V)
+
+/* ------------------------- End of common defines ------------------------ */
+
+
+
+
+#ifndef REDISMODULE_CORE
+
+
+
+typedef long long mstime_t;
+
+/* Incomplete structures for compiler checks but opaque access. */
+typedef struct RedisModuleCtx RedisModuleCtx;
+typedef struct RedisModuleKey RedisModuleKey;
+typedef struct RedisModuleString RedisModuleString;
+typedef struct RedisModuleCallReply RedisModuleCallReply;
+typedef struct RedisModuleIO RedisModuleIO;
+typedef struct RedisModuleType RedisModuleType;
+typedef struct RedisModuleDigest RedisModuleDigest;
+typedef struct RedisModuleBlockedClient RedisModuleBlockedClient;
+
+//typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+
+typedef void *(*RedisModuleTypeLoadFunc)(RedisModuleIO *rdb, int encver);
+typedef void (*RedisModuleTypeSaveFunc)(RedisModuleIO *rdb, void *value);
+typedef void (*RedisModuleTypeRewriteFunc)(RedisModuleIO *aof, RedisModuleString *key, void *value);
+typedef size_t (*RedisModuleTypeMemUsageFunc)(const void *value);
+typedef void (*RedisModuleTypeDigestFunc)(RedisModuleDigest *digest, void *value);
+typedef void (*RedisModuleTypeFreeFunc)(void *value);
+
+#define REDISMODULE_TYPE_METHOD_VERSION 1
+typedef struct RedisModuleTypeMethods {
+    uint64_t version;
+    RedisModuleTypeLoadFunc rdb_load;
+    RedisModuleTypeSaveFunc rdb_save;
+    RedisModuleTypeRewriteFunc aof_rewrite;
+    RedisModuleTypeMemUsageFunc mem_usage;
+    RedisModuleTypeDigestFunc digest;
+    RedisModuleTypeFreeFunc free;
+} RedisModuleTypeMethods;
+
+#define REDISMODULE_GET_API(name) \
+    RedisModule_GetApi("RedisModule_" #name, ((void **)&RedisModule_ ## name))
+
+#define REDISMODULE_API_FUNC(x) (*x)
+
+#if 1
+
+
+typedef struct redisObject {
+    unsigned type:4;
+    unsigned encoding:4;
+    int refcount;
+    void *ptr;
+} robj;
+/* This structure represents a module inside the system. */
+struct RedisModule {
+    void *handle;   /* Module dlopen() handle. */
+    char *name;     /* Module name. */
+    int ver;        /* Module version. We use just progressive integers. */
+    int apiver;     /* Module API version as requested during initialization.*/
+    //list *types;    /* Module data types. */
+};
+typedef struct RedisModule RedisModule;
+
+//static dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/
+
+/* Entries in the context->amqueue array, representing objects to free
+ * when the callback returns. */
+struct AutoMemEntry {
+    void *ptr;
+    int type;
+};
+
+/* AutMemEntry type field values. */
+#define REDISMODULE_AM_KEY 0
+#define REDISMODULE_AM_STRING 1
+#define REDISMODULE_AM_REPLY 2
+#define REDISMODULE_AM_FREED 3 /* Explicitly freed by user already. */
+
+/* The pool allocator block. Redis Modules can allocate memory via this special
+ * allocator that will automatically release it all once the callback returns.
+ * This means that it can only be used for ephemeral allocations. However
+ * there are two advantages for modules to use this API:
+ *
+ * 1) The memory is automatically released when the callback returns.
+ * 2) This allocator is faster for many small allocations since whole blocks
+ *    are allocated, and small pieces returned to the caller just advancing
+ *    the index of the allocation.
+ *
+ * Allocations are always rounded to the size of the void pointer in order
+ * to always return aligned memory chunks. */
+
+#define REDISMODULE_POOL_ALLOC_MIN_SIZE (1024*8)
+#define REDISMODULE_POOL_ALLOC_ALIGN (sizeof(void*))
+
+typedef struct RedisModulePoolAllocBlock {
+    uint32_t size;
+    uint32_t used;
+    struct RedisModulePoolAllocBlock *next;
+    char memory[];
+} RedisModulePoolAllocBlock;
+
+/* This structure represents the context in which Redis modules operate.
+ * Most APIs module can access, get a pointer to the context, so that the API
+ * implementation can hold state across calls, or remember what to free after
+ * the call and so forth.
+ *
+ * Note that not all the context structure is always filled with actual values
+ * but only the fields needed in a given context. */
+
+struct RedisModuleBlockedClient;
+
+struct RedisModuleCtx {
+    void *getapifuncptr;            /* NOTE: Must be the first field. */
+    struct RedisModule *module;     /* Module reference. */
+    //client *client;                 /* Client calling a command. */
+    struct RedisModuleBlockedClient *blocked_client; /* Blocked client for
+                                                        thread safe context. */
+    struct AutoMemEntry *amqueue;   /* Auto memory queue of objects to free. */
+    int amqueue_len;                /* Number of slots in amqueue. */
+    int amqueue_used;               /* Number of used slots in amqueue. */
+    int flags;                      /* REDISMODULE_CTX_... flags. */
+    void **postponed_arrays;        /* To set with RM_ReplySetArrayLength(). */
+    int postponed_arrays_count;     /* Number of entries in postponed_arrays. */
+    void *blocked_privdata;         /* Privdata set when unblocking a client. */
+
+    /* Used if there is the REDISMODULE_CTX_KEYS_POS_REQUEST flag set. */
+    int *keys_pos;
+    int keys_count;
+
+    struct RedisModulePoolAllocBlock *pa_head;
+};
+typedef struct RedisModuleCtx RedisModuleCtx;
+
+#define REDISMODULE_CTX_INIT {(void*)(unsigned long)&RM_GetApi, NULL, NULL, NULL, NULL, 0, 0, 0, NULL, 0, NULL, NULL, 0, NULL}
+#define REDISMODULE_CTX_MULTI_EMITTED (1<<0)
+#define REDISMODULE_CTX_AUTO_MEMORY (1<<1)
+#define REDISMODULE_CTX_KEYS_POS_REQUEST (1<<2)
+#define REDISMODULE_CTX_BLOCKED_REPLY (1<<3)
+#define REDISMODULE_CTX_BLOCKED_TIMEOUT (1<<4)
+#define REDISMODULE_CTX_THREAD_SAFE (1<<5)
+
+/* This represents a Redis key opened with RM_OpenKey(). */
+struct RedisModuleKey {
+    RedisModuleCtx *ctx;
+    //redisDb *db;
+    robj *key;      /* Key name object. */
+    robj *value;    /* Value object, or NULL if the key was not found. */
+    void *iter;     /* Iterator. */
+    int mode;       /* Opening mode. */
+
+    /* Zset iterator. */
+    uint32_t ztype;         /* REDISMODULE_ZSET_RANGE_* */
+    //zrangespec zrs;         /* Score range. */
+    //zlexrangespec zlrs;     /* Lex range. */
+    uint32_t zstart;        /* Start pos for positional ranges. */
+    uint32_t zend;          /* End pos for positional ranges. */
+    void *zcurrent;         /* Zset iterator current node. */
+    int zer;                /* Zset iterator end reached flag
+                               (true if end was reached). */
+};
+typedef struct RedisModuleKey RedisModuleKey;
+
+/* RedisModuleKey 'ztype' values. */
+#define REDISMODULE_ZSET_RANGE_NONE 0       /* This must always be 0. */
+#define REDISMODULE_ZSET_RANGE_LEX 1
+#define REDISMODULE_ZSET_RANGE_SCORE 2
+#define REDISMODULE_ZSET_RANGE_POS 3
+
+/* Function pointer type of a function representing a command inside
+ * a Redis module. */
+
+typedef int (*RedisModuleCmdFunc) (RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
+
+
+/* This struct holds the information about a command registered by a module.*/
+struct RedisModuleCommandProxy {
+    struct RedisModule *module;
+    RedisModuleCmdFunc func;
+    struct redisCommand *rediscmd;
+};
+typedef struct RedisModuleCommandProxy RedisModuleCommandProxy;
+
+#define REDISMODULE_REPLYFLAG_NONE 0
+#define REDISMODULE_REPLYFLAG_TOPARSE (1<<0) /* Protocol must be parsed. */
+#define REDISMODULE_REPLYFLAG_NESTED (1<<1)  /* Nested reply object. No proto
+                                                or struct free. */
+
+/* Reply of RM_Call() function. The function is filled in a lazy
+ * way depending on the function called on the reply structure. By default
+ * only the type, proto and protolen are filled. */
+typedef struct RedisModuleCallReply {
+    RedisModuleCtx *ctx;
+    int type;       /* REDISMODULE_REPLY_... */
+    int flags;      /* REDISMODULE_REPLYFLAG_...  */
+    size_t len;     /* Len of strings or num of elements of arrays. */
+    char *proto;    /* Raw reply protocol. An SDS string at top-level object. */
+    size_t protolen;/* Length of protocol. */
+    union {
+        const char *str; /* String pointer for string and error replies. This
+                            does not need to be freed, always points inside
+                            a reply->proto buffer of the reply object or, in
+                            case of array elements, of parent reply objects. */
+        long long ll;    /* Reply value for integer reply. */
+        struct RedisModuleCallReply *array; /* Array of sub-reply elements. */
+    } val;
+} RedisModuleCallReply;
+
+/* Structure representing a blocked client. We get a pointer to such
+ * an object when blocking from modules. */
+typedef struct RedisModuleBlockedClient {
+    //client *client;  /* Pointer to the blocked client. or NULL if the client
+     //                   was destroyed during the life of this object. */
+    RedisModule *module;    /* Module blocking the client. */
+    RedisModuleCmdFunc reply_callback; /* Reply callback on normal completion.*/
+    RedisModuleCmdFunc timeout_callback; /* Reply callback on timeout. */
+    void (*free_privdata)(void *);       /* privdata cleanup callback. */
+    void *privdata;     /* Module private data that may be used by the reply
+                           or timeout callback. It is set via the
+                           RedisModule_UnblockClient() API. */
+    //client *reply_client;           /* Fake client used to accumulate replies
+   //                                    in thread safe contexts. */
+    int dbid;           /* Database number selected by the original client. */
+} RedisModuleBlockedClient;
+
+#define RedisModuleString robj
+
+#endif
+
+
+
+
+int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep);
+int RedisModule_WrongArity(RedisModuleCtx *ctx);
+int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll);
+void *RedisModule_OpenKey(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode);
+RedisModuleCallReply *RedisModule_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...);
+void RedisModule_FreeCallReply(RedisModuleCallReply *reply);
+int RedisModule_CallReplyType(RedisModuleCallReply *reply);
+long long RedisModule_CallReplyInteger(RedisModuleCallReply *reply);
+const char *RedisModule_StringPtrLen(const RedisModuleString *str, size_t *len);
+int RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err);
+int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str);
+int RedisModule_ReplyWithNull(RedisModuleCtx *ctx);
+int RedisModule_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply);
+const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len);
+RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply);
+
+int RedisModule_KeyType(RedisModuleKey *kp);
+void RedisModule_CloseKey(RedisModuleKey *kp);
+
+/* This is included inline inside each Redis module. */
+int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver);
+
+size_t RedisModule_CallReplyLength(RedisModuleCallReply *reply);
+RedisModuleCallReply *RedisModule_CallReplyArrayElement(RedisModuleCallReply *reply, size_t idx);
+int RedisModule_ReplyWithArray(RedisModuleCtx *ctx, long len);
+
+
+/* Things only defined for the modules core, not exported to modules
+ * including this file. */
+
+#else
+
+/* Things only defined for the modules core, not exported to modules
+ * including this file. */
+#define RedisModuleString robj
+
+#endif /* REDISMODULE_CORE */
+#endif /* REDISMOUDLE_H */
diff --git a/redismodule/tst/mock/src/redismoduleStub.cpp b/redismodule/tst/mock/src/redismoduleStub.cpp
new file mode 100755 (executable)
index 0000000..b08fd21
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+#include <unistd.h>
+#include <string.h>
+
+
+
+
+extern "C" {
+#include "redismodule.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+}
+
+
+int RedisModule_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep)
+{
+    (void)ctx;
+    (void)name;
+    (void)cmdfunc;
+    (void)strflags;
+    (void)firstkey;
+    (void)lastkey;
+    (void)keystep;
+    return REDISMODULE_OK;
+
+}
+
+int RedisModule_WrongArity(RedisModuleCtx *ctx)
+{
+    (void)ctx;
+    return REDISMODULE_ERR;
+}
+
+int RedisModule_ReplyWithLongLong(RedisModuleCtx *ctx, long long ll)
+{
+
+    (void)ctx;
+    mock().setData("RedisModule_ReplyWithLongLong", (int)ll);
+    return REDISMODULE_OK;
+}
+
+void *RedisModule_OpenKey(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode)
+{
+    (void)ctx;
+    (void)keyname;
+    (void)mode;
+
+    if (mock().hasData("RedisModule_OpenKey_no"))
+    {
+        return (void*)(0);
+    }
+
+    if (mock().hasData("RedisModule_OpenKey_have"))
+    {
+        return (void*)(111111);
+    }
+
+
+    return (void*)(0);
+}
+
+RedisModuleCallReply *RedisModule_Call(RedisModuleCtx *ctx, const char *cmdname, const char *fmt, ...)
+{
+    (void)ctx;
+    (void)cmdname;
+    (void)fmt;
+
+    if (!strcmp(cmdname, "GET"))
+        mock().setData("GET", 1);
+    else if (!strcmp(cmdname, "SET"))
+        mock().setData("SET", 1);
+    else if (!strcmp(cmdname, "MSET"))
+        mock().setData("MSET", 1);
+    else if (!strcmp(cmdname, "DEL"))
+        mock().setData("DEL", 1);
+    else if (!strcmp(cmdname, "UNLINK"))
+        mock().setData("UNLINK", 1);
+    else if (!strcmp(cmdname, "PUBLISH"))
+        mock().setData("PUBLISH", 1);
+    else if (!strcmp(cmdname, "KEYS"))
+        mock().setData("KEYS", 1);
+    else if (!strcmp(cmdname, "MGET"))
+        mock().setData("MGET", 1);
+
+    if (mock().hasData("RedisModule_Call_Return_Null"))
+        return NULL;
+    else
+        return (RedisModuleCallReply *)1;
+}
+
+void RedisModule_FreeCallReply(RedisModuleCallReply *reply)
+{
+    (void)reply;
+    mock().setData("RedisModule_FreeCallReply", mock().getData("RedisModule_FreeCallReply").getIntValue()+1);
+}
+
+int RedisModule_CallReplyType(RedisModuleCallReply *reply)
+{
+
+    (void)reply;
+    if (mock().hasData("RedisModule_CallReplyType_null"))
+    {
+        return REDISMODULE_REPLY_NULL;
+    }
+
+    if (mock().hasData("RedisModule_CallReplyType_inter"))
+    {
+        return REDISMODULE_REPLY_INTEGER;
+    }
+
+    if (mock().hasData("RedisModule_CallReplyType_str"))
+    {
+        return REDISMODULE_REPLY_STRING;
+    }
+
+    if (mock().hasData("RedisModule_CallReplyType_err"))
+    {
+        return REDISMODULE_REPLY_ERROR;
+    }
+
+    return REDISMODULE_REPLY_NULL;;
+
+}
+
+long long RedisModule_CallReplyInteger(RedisModuleCallReply *reply)
+{
+
+    (void)reply;
+    return mock().getData("RedisModule_CallReplyInteger").getIntValue();
+}
+
+const char *RedisModule_StringPtrLen(const RedisModuleString *str, size_t *len)
+{
+
+    (void)str;
+    *len = 5;
+    if (mock().hasData("RedisModule_String_same"))
+    {
+        return "11111";
+    }
+
+    if (mock().hasData("RedisModule_String_nosame"))
+    {
+        return "22222";
+    }
+
+    return "11111";
+}
+
+int RedisModule_ReplyWithError(RedisModuleCtx *ctx, const char *err)
+{
+    (void)ctx;
+    (void)err;
+    mock().setData("RedisModule_ReplyWithError", 1);
+    return REDISMODULE_OK;
+}
+
+int RedisModule_ReplyWithString(RedisModuleCtx *ctx, RedisModuleString *str)
+{
+    (void)ctx;
+    (void)str;
+    mock().setData("RedisModule_ReplyWithString", mock().getData("RedisModule_ReplyWithString").getIntValue()+1);
+    return REDISMODULE_OK;
+}
+
+int RedisModule_ReplyWithNull(RedisModuleCtx *ctx)
+{
+
+    (void)ctx;
+    mock().setData("RedisModule_ReplyWithNull", 1);
+    return REDISMODULE_OK;
+}
+
+int RedisModule_ReplyWithCallReply(RedisModuleCtx *ctx, RedisModuleCallReply *reply)
+{
+    (void)ctx;
+    (void)reply;
+    mock().setData("RedisModule_ReplyWithCallReply", 1);
+    return REDISMODULE_OK;
+}
+
+const char *RedisModule_CallReplyStringPtr(RedisModuleCallReply *reply, size_t *len)
+{
+    (void)reply;
+
+    *len = 5;
+
+    if (mock().hasData("RedisModule_String_same"))
+    {
+        return "11111";
+    }
+
+
+    if (mock().hasData("RedisModule_String_nosame"))
+    {
+        *len = 6;
+        return "333333";
+    }
+
+    return "11111";
+}
+
+RedisModuleString *RedisModule_CreateStringFromCallReply(RedisModuleCallReply *reply)
+{
+    (void)reply;
+    return (RedisModuleString *)1;
+}
+
+
+int RedisModule_KeyType(RedisModuleKey *kp)
+{
+
+
+    (void)kp;
+    if (mock().hasData("RedisModule_KeyType_empty"))
+    {
+        return REDISMODULE_KEYTYPE_EMPTY;
+    }
+
+    if (mock().hasData("RedisModule_KeyType_str"))
+    {
+        return REDISMODULE_KEYTYPE_STRING;
+    }
+
+    if (mock().hasData("RedisModule_KeyType_set"))
+    {
+
+        return REDISMODULE_KEYTYPE_SET;
+    }
+
+    return REDISMODULE_KEYTYPE_EMPTY;
+
+
+}
+
+void RedisModule_CloseKey(RedisModuleKey *kp)
+{
+    (void)kp;
+    mock().actualCall("RedisModule_CloseKey");
+}
+
+/* This is included inline inside each Redis module. */
+int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver)
+{
+    (void)ctx;
+    (void)name;
+    (void)ver;
+    (void)apiver;
+    return REDISMODULE_OK;
+}
+
+size_t RedisModule_CallReplyLength(RedisModuleCallReply *reply)
+{
+    (void)reply;
+    return mock().getData("RedisModule_CallReplyLength").getIntValue();
+}
+
+
+RedisModuleCallReply *RedisModule_CallReplyArrayElement(RedisModuleCallReply *reply, size_t idx)
+{
+    (void)reply;
+    (void)idx;
+    return (RedisModuleCallReply *)1;
+}
+
+int RedisModule_ReplyWithArray(RedisModuleCtx *ctx, long len)
+{
+    (void)ctx;
+    mock().setData("RedisModule_ReplyWithArray", (int)len);
+    return REDISMODULE_OK;
+}
+
diff --git a/redismodule/tst/src/exstrings_test.cpp b/redismodule/tst/src/exstrings_test.cpp
new file mode 100755 (executable)
index 0000000..2d540d5
--- /dev/null
@@ -0,0 +1,1463 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#include <string>
+#include <map>
+#include <list>
+#include <set>
+#include <vector>
+#include <syslog.h>
+
+
+//#include <stdio.h>
+//#include <stdlib.h>
+//#include <ctype.h>
+
+
+extern "C" {
+#include "exstringsStub.h"
+#include "redismodule.h"
+}
+
+#include "CppUTest/TestHarness.h"
+#include "CppUTestExt/MockSupport.h"
+
+#define OBJ_OP_NO 0
+#define OBJ_OP_XX (1<<1)     /* OP if key exist */
+#define OBJ_OP_NX (1<<2)     /* OP if key not exist */
+#define OBJ_OP_IE (1<<4)     /* OP if equal old value */
+#define OBJ_OP_NE (1<<5)     /* OP if not equal old value */
+
+
+TEST_GROUP(exstring)
+{
+    void setup()
+    {
+
+        mock().enable();
+        mock().ignoreOtherCalls();
+    }
+
+    void teardown()
+    {
+        mock().clear();
+        mock().disable();
+    }
+
+};
+
+TEST(exstring, OnLoad)
+{
+    RedisModuleCtx ctx;
+    int ret = RedisModule_OnLoad(&ctx, 0, 0);
+    CHECK_EQUAL(ret, 0);
+}
+
+TEST(exstring, setie)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    int ret = SetIE_RedisCommand(&ctx, redisStrVec,  4);
+    CHECK_EQUAL(ret, 0);
+    delete []redisStrVec;
+}
+
+TEST(exstring, setne)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    int ret = SetNE_RedisCommand(&ctx,redisStrVec, 4);
+    CHECK_EQUAL(ret, 0);
+    delete []redisStrVec;
+}
+
+TEST(exstring, command_parameter_number_incorrect)
+{
+    RedisModuleCtx ctx;
+    int ret = setStringGenericCommand(&ctx, 0, 3, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 1);
+}
+
+
+TEST(exstring, setie_command_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    mock().setData("RedisModule_OpenKey_no", 1);
+
+    int ret = setStringGenericCommand(&ctx, redisStrVec, 4, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    delete []redisStrVec;
+
+}
+
+
+TEST(exstring, setie_command_has_key_set)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_set", 1);
+    int ret = setStringGenericCommand(&ctx, redisStrVec, 4, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setie_command_key_string_nosame)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+
+    mock().setData("RedisModule_String_nosame", 1);
+
+
+    int ret = setStringGenericCommand(&ctx, redisStrVec, 4, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setie_command_key_same_string_reply)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    int ret = setStringGenericCommand(&ctx, redisStrVec, 4, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    delete []redisStrVec;
+}
+
+
+
+TEST(exstring, setne_command_key_string_same_replrstr)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    //mock().setData("RedisModule_CallReplyType_str", 1);
+
+    int ret = setStringGenericCommand(&ctx, redisStrVec, 4, OBJ_OP_NE);
+    CHECK_EQUAL(ret, 0);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setne_command_setne_key_string_nosame_replrstr)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    int ret = setStringGenericCommand(&ctx, redisStrVec, 4, OBJ_OP_NE);
+    CHECK_EQUAL(ret, 0);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delie)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = DelIE_RedisCommand(&ctx, redisStrVec,  3);
+    CHECK_EQUAL(ret, 0);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, delne)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = DelNE_RedisCommand(&ctx,redisStrVec, 3);
+    CHECK_EQUAL(ret, 0);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, del_command_parameter_number_incorrect)
+{
+    RedisModuleCtx ctx;
+    int ret = 0;
+    ret = delStringGenericCommand(&ctx, 0, 2, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 1);
+
+    ret = 0;
+    ret = delStringGenericCommand(&ctx, 0, 4, OBJ_OP_NE);
+    CHECK_EQUAL(ret, 1);
+}
+
+TEST(exstring, delie_command_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    mock().setData("RedisModule_OpenKey_no", 1);
+
+    int ret = delStringGenericCommand(&ctx, redisStrVec, 3, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delie_command_have_key_set)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_set", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delStringGenericCommand(&ctx, redisStrVec, 3, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 1);
+    delete []redisStrVec;
+}
+
+TEST(exstring, delie_command_key_string_nosame)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+
+    mock().setData("RedisModule_String_nosame", 1);
+
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delStringGenericCommand(&ctx, redisStrVec, 3, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+
+TEST(exstring, delie_command_key_same_string_reply)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delStringGenericCommand(&ctx, redisStrVec, 3, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+    delete []redisStrVec;
+}
+
+
+TEST(exstring, delne_command_key_string_same_reply)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delStringGenericCommand(&ctx, redisStrVec, 3, OBJ_OP_NE);
+    CHECK_EQUAL(ret, 0);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delne_command_key_string_nosame_reply)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[3]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delStringGenericCommand(&ctx, redisStrVec, 3, OBJ_OP_NE);
+    CHECK_EQUAL(ret, 0);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setpub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = SetPub_RedisCommand(&ctx, redisStrVec,  5);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, setxxpub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = SetXXPub_RedisCommand(&ctx, redisStrVec,  5);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, setnxpub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = SetNXPub_RedisCommand(&ctx, redisStrVec,  5);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, setiepub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = SetIEPub_RedisCommand(&ctx, redisStrVec,  6);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, setnepub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    redisStrVec[5] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = SetNEPub_RedisCommand(&ctx, redisStrVec,  6);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, setpub_command_parameter_number_incorrect)
+{
+    RedisModuleCtx ctx;
+    int ret = 0;
+    ret = setPubStringGenericCommand(&ctx, 0, 2, OBJ_OP_NO);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = setPubStringGenericCommand(&ctx, 0, 8, OBJ_OP_NO);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = setPubStringGenericCommand(&ctx, 0, 3, OBJ_OP_XX);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = setPubStringGenericCommand(&ctx, 0, 6, OBJ_OP_NX);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = setPubStringGenericCommand(&ctx, 0, 4, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = setPubStringGenericCommand(&ctx, 0, 8, OBJ_OP_NE);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+}
+
+TEST(exstring, setpub_command_no_key_replynull)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NO);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setpub_command_no_key_replystr)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NO);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setxxpub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_XX);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setxxpub_command_parameter_has_key_set)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_set", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_XX);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setxxpub_command_has_key_string)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_XX);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+
+}
+
+
+TEST(exstring, setnxpub_command_has_key_string)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NX);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setnxpub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NX);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+
+}
+
+
+
+TEST(exstring, setiepub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_IE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+
+    delete []redisStrVec;
+
+}
+
+
+TEST(exstring, setiepub_command_key_string_nosame)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_IE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setiepub_command_key_same_string_replynull)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_IE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setiepub_command_key_same_string_reply)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_IE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 3);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setnepub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_empty", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_NE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 3);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, setnepub_command_key_string_same_reply)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_NE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+
+}
+
+
+TEST(exstring, setnepub_command_key_string_nosame_reply)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[6]);
+
+    redisStrVec[0] = (RedisModuleString *)0;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)2;
+    redisStrVec[3] = (RedisModuleString *)3;
+    redisStrVec[4] = (RedisModuleString *)4;
+    redisStrVec[5] = (RedisModuleString *)5;
+
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = setPubStringGenericCommand(&ctx, redisStrVec, 6, OBJ_OP_NE);
+
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MSET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 3);
+
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delpub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[4]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    int ret = DelPub_RedisCommand(&ctx, redisStrVec,  4);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, deliepub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = DelIEPub_RedisCommand(&ctx, redisStrVec,  5);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, delnepub)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = DelNEPub_RedisCommand(&ctx, redisStrVec,  5);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    delete []redisStrVec;
+}
+
+TEST(exstring, delpub_command_parameter_number_incorrect)
+{
+    RedisModuleCtx ctx;
+    int ret = 0;
+    ret = delPubStringGenericCommand(&ctx, 0, 2, OBJ_OP_NO);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = delPubStringGenericCommand(&ctx, 0, 4, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    ret = 0;
+    ret = delPubStringGenericCommand(&ctx, 0, 8, OBJ_OP_NE);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+}
+
+TEST(exstring, delpub_command_reply_null)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    mock().setData("RedisModule_CallReplyInteger", 0);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+    mock().setData("RedisModule_Call_Return_Null", 0);
+
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NO);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delpub_command_reply_error)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    mock().setData("RedisModule_CallReplyInteger", 0);
+    mock().setData("RedisModule_CallReplyType_err", 1);
+
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NO);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, delpub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    mock().setData("RedisModule_CallReplyInteger", 0);
+    mock().setData("RedisModule_CallReplyType_inter", 1);
+
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NO);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, deliepub_command_has_no_key)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+    mock().setData("RedisModule_KeyType_empty", 1);
+
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_IE);
+    CHECK_EQUAL(ret, 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+    delete []redisStrVec;
+
+}
+
+TEST(exstring, deliepub_command_has_key_set)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_set", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 0);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, deliepub_command_key_string_nosame)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, deliepub_command_same_string_replynull)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_null", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithNull").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, deliepub_command_same_string_reply)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_IE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 3);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, delnepub_command_same_string_reply)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_same", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, delnepub_command_nosame_string_reply)
+{
+    RedisModuleCtx ctx;
+    //RedisModuleString str;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[5]);
+
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+    redisStrVec[2] = (RedisModuleString *)1;
+    redisStrVec[3] = (RedisModuleString *)1;
+    redisStrVec[4] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_OpenKey_have", 1);
+    mock().setData("RedisModule_KeyType_str", 1);
+    mock().setData("RedisModule_String_nosame", 1);
+    mock().setData("RedisModule_CallReplyType_str", 1);
+    mock().setData("RedisModule_CallReplyInteger", 1);
+    mock().expectOneCall("RedisModule_CloseKey");
+    int ret = delPubStringGenericCommand(&ctx, redisStrVec, 5, OBJ_OP_NE);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    mock().checkExpectations();
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithError").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("GET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("PUBLISH").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 3);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, nget_command_parameter_number_incorrect)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[2]);
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+
+    int ret = NGet_RedisCommand(&ctx, redisStrVec,  3);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, nget_command_zero_item)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[2]);
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyLength", 0);
+    int ret = NGet_RedisCommand(&ctx, redisStrVec,  2);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    CHECK_EQUAL(mock().getData("KEYS").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, nget_command_none_zero_items)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[2]);
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyLength", 3);
+    int ret = NGet_RedisCommand(&ctx, redisStrVec,  2);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    CHECK_EQUAL(mock().getData("KEYS").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("MGET").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithArray").getIntValue(), 3*2);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithString").getIntValue(), 3*2);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, ndel_command_parameter_parameter_number_incorrect)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[2]);
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+
+    int ret = NDel_RedisCommand(&ctx, redisStrVec,  3);
+    CHECK_EQUAL(ret, REDISMODULE_ERR);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, ndel_command_zero_item)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[2]);
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyLength", 0);
+    int ret = NDel_RedisCommand(&ctx, redisStrVec,  2);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    CHECK_EQUAL(mock().getData("KEYS").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithLongLong").getIntValue(), 0);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 1);
+
+    delete []redisStrVec;
+}
+
+TEST(exstring, ndel_command_none_zero_items)
+{
+    RedisModuleCtx ctx;
+    RedisModuleString ** redisStrVec = new (RedisModuleString*[2]);
+    redisStrVec[0] = (RedisModuleString *)1;
+    redisStrVec[1] = (RedisModuleString *)1;
+
+    mock().setData("RedisModule_CallReplyLength", 3);
+    int ret = NDel_RedisCommand(&ctx, redisStrVec,  2);
+    CHECK_EQUAL(ret, REDISMODULE_OK);
+    CHECK_EQUAL(mock().getData("KEYS").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("UNLINK").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_ReplyWithCallReply").getIntValue(), 1);
+    CHECK_EQUAL(mock().getData("RedisModule_FreeCallReply").getIntValue(), 2);
+
+    delete []redisStrVec;
+}
diff --git a/redismodule/tst/src/main.cpp b/redismodule/tst/src/main.cpp
new file mode 100755 (executable)
index 0000000..d74c9a8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018-2019 Nokia.
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ */
+
+#include "CppUTest/CommandLineTestRunner.h"
+
+extern "C" {
+#include <stdio.h>
+}
+
+int main(int ac, char** av)
+{
+   return CommandLineTestRunner::RunAllTests(ac, av);
+}
\ No newline at end of file