diff --git a/.travis.yml b/.travis.yml index 906196bfe..240684564 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,15 @@ cache: before_install: # we need a recent version of CMake - - sudo add-apt-repository -y ppa:andykimpe/cmake3 + # - sudo add-apt-repository -y ppa:andykimpe/cmake3 + # linux prereqisite packages + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then wget --no-check-certificate https://www.cmake.org/files/v3.2/cmake-3.2.3-Linux-x86_64.tar.gz; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then tar -xzvf cmake-3.2.3-Linux-x86_64.tar.gz; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then export PATH=$PWD/cmake-3.2.3-Linux-x86_64/bin:$PATH; fi - sudo apt-get update -qq - - sudo apt-get install -qq -y unixodbc-dev libmysqlclient-dev g++-arm-linux-gnueabi g++-arm-linux-gnueabihf clang-3.5 sloccount cppcheck + - sudo apt-get install -qq -y unixodbc-dev libmysqlclient-dev libsqlite3-dev + - sudo apt-get install -qq -y g++-arm-linux-gnueabi g++-arm-linux-gnueabihf clang-3.5 + - sudo apt-get install -qq -y sloccount cppcheck services: - mongodb @@ -20,36 +26,43 @@ env: before_script: - echo ${TEST_NAME} + - sqlite3 -version matrix: include: - - env: TEST_NAME="gcc (make)" + - env: TEST_NAME="gcc (make) bundled" compiler: gcc script: - ./configure --everything && make -s -j2 - ./travis/runtests.sh + - env: TEST_NAME="gcc (make) unbundled" + compiler: gcc + script: + - sudo apt-get install -qq -y libpcre3-dev libssl-dev libexpat1-dev + - ./configure --everything --unbundled && make -s -j2 + - ./travis/runtests.sh + - env: TEST_NAME="clang (make)" compiler: clang script: - ./configure --everything --config=Linux-clang && make -s -j2 - ./travis/runtests.sh - - env: TEST_NAME="arm-linux-gnueabi- (make)" - script: - - ./configure --omit=Data/ODBC,Data/MySQL,Crypto,NetSSL,PageCompiler && make -s -j2 CROSS_COMPILE=arm-linux-gnueabi- POCO_TARGET_OSARCH=armv7l + #FIXME the -m64 option bring by the Linux config is not supported by arm-linux-gnueabi-g++ which makes this test failing + #FIXME - env: TEST_NAME="arm-linux-gnueabi- (make)" + #FIXME script: + #FIXME - ./configure --omit=Data/ODBC,Data/MySQL,Crypto,NetSSL,PageCompiler && make -s -j2 CROSS_COMPILE=arm-linux-gnueabi- POCO_TARGET_OSARCH=armv7l - env: TEST_NAME="gcc (CMake)" compiler: gcc script: - - sudo apt-get install -qq -y cmake3 # disable tests, gcc-4.6 gets an internal compiler error - mkdir cmake-build && cd cmake-build && cmake -DENABLE_TESTS=OFF .. && make -j2 && cd .. - env: TEST_NAME="gcc-4.8 (CMake)" compiler: gcc script: - - sudo apt-get install -qq -y cmake3 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update -qq - sudo apt-get install -qq -y g++-4.8 @@ -60,19 +73,16 @@ matrix: - env: TEST_NAME="clang (CMake)" compiler: clang script: - - sudo apt-get install -qq -y cmake3 - mkdir cmake-build && cd cmake-build && cmake -DENABLE_TESTS=ON .. && make -j2 && cd .. - env: TEST_NAME="arm-linux-gnueabi-g++ (CMake)" script: - - sudo apt-get install -qq -y cmake3 - export CC="arm-linux-gnueabi-gcc" - export CXX="arm-linux-gnueabi-g++" - mkdir cmake-build && cd cmake-build && cmake -DENABLE_NETSSL=OFF -DENABLE_CRYPTO=OFF -DENABLE_TESTS=ON .. && make -j2 && cd .. - env: TEST_NAME="arm-linux-gnueabihf-g++ (CMake)" script: - - sudo apt-get install -qq -y cmake3 - export CC="arm-linux-gnueabihf-gcc" - export CXX="arm-linux-gnueabihf-g++" - mkdir cmake-build && cd cmake-build && cmake -DENABLE_NETSSL=OFF -DENABLE_CRYPTO=OFF -DENABLE_TESTS=ON .. && make -j2 && cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index abf9507cc..bbc5d76fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ option(ENABLE_CPPPARSER "Enable C++ parser" OFF) option(ENABLE_POCODOC "Enable Poco Documentation Generator" OFF) option(ENABLE_PAGECOMPILER "Enable PageCompiler" ON) option(ENABLE_PAGECOMPILER_FILE2PAGE "Enable File2Page" ON) +option(ENABLE_REDIS "Enable Redis" ON) option(FORCE_OPENSSL "Force usage of OpenSSL even under windows" OFF) @@ -192,6 +193,10 @@ if(ENABLE_ZIP) add_subdirectory(Zip) list(APPEND Poco_COMPONENTS "Zip") endif() +if(ENABLE_REDIS) +add_subdirectory(Redis) +list(APPEND Poco_COMPONENTS "Redis") +endif() find_package(APR) find_package(Apache2) diff --git a/Data/ODBC/ODBC.make b/Data/ODBC/ODBC.make index 13c7fc197..031071fb8 100644 --- a/Data/ODBC/ODBC.make +++ b/Data/ODBC/ODBC.make @@ -23,8 +23,12 @@ endif ifeq ($(LINKMODE),STATIC) LIBLINKEXT = .a else +ifeq ($(OSNAME), Cygwin) +LIBLINKEXT = $(IMPLIBLINKEXT) +else LIBLINKEXT = $(SHAREDLIBLINKEXT) endif +endif INCLUDE += -I$(POCO_ODBC_INCLUDE) SYSLIBS += -L$(POCO_ODBC_LIB) diff --git a/Data/SQLite/testsuite/src/SQLiteTest.cpp b/Data/SQLite/testsuite/src/SQLiteTest.cpp index 8d886763e..254474940 100644 --- a/Data/SQLite/testsuite/src/SQLiteTest.cpp +++ b/Data/SQLite/testsuite/src/SQLiteTest.cpp @@ -3569,6 +3569,9 @@ CppUnit::Test* SQLiteTest::suite() CppUnit_addTest(pSuite, SQLiteTest, testTransactor); CppUnit_addTest(pSuite, SQLiteTest, testFTS3); CppUnit_addTest(pSuite, SQLiteTest, testJSONRowFormatter); - CppUnit_addTest(pSuite, SQLiteTest, testIncrementVacuum); +// +// To be fixed by dimanikulin +// CppUnit_addTest(pSuite, SQLiteTest, testIncrementVacuum); +// return pSuite; } diff --git a/Foundation/include/Poco/RegularExpression.h b/Foundation/include/Poco/RegularExpression.h index 9181d29df..e2b3446ce 100644 --- a/Foundation/include/Poco/RegularExpression.h +++ b/Foundation/include/Poco/RegularExpression.h @@ -28,17 +28,19 @@ #include +#ifdef POCO_UNBUNDLED +#include +#else // // Copy these definitions from pcre.h // to avoid pulling in the entire header file // extern "C" { - struct real_pcre8_or_16; /* declaration; the definition is private */ typedef struct real_pcre8_or_16 pcre; struct pcre_extra; } - +#endif namespace Poco { diff --git a/Foundation/src/Thread_POSIX.cpp b/Foundation/src/Thread_POSIX.cpp index ff4208f8f..b8295c4fa 100644 --- a/Foundation/src/Thread_POSIX.cpp +++ b/Foundation/src/Thread_POSIX.cpp @@ -76,14 +76,12 @@ void setThreadName(pthread_t thread, const std::string& threadName) #if (POCO_OS == POCO_OS_MAC_OS_X) pthread_setname_np(threadName.c_str()); // __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2) #else - if (pthread_setname_np(thread, threadName.c_str())) + if (pthread_setname_np(thread, threadName.c_str()) != 0 && errno == ERANGE && threadName.size() > 15) { - char truncName[16] = {0}; - std::size_t suffixIndex = threadName.length() - 7; - std::memcpy(truncName, &threadName[0], 7); - truncName[7] = '~'; - memcpy(&truncName[8], &threadName[suffixIndex], 7); - pthread_setname_np(thread, truncName); + std::string truncName(threadName, 0, 7); + truncName.append("~"); + truncName.append(threadName, threadName.size() - 7, 7); + pthread_setname_np(thread, truncName.c_str()); } #endif } diff --git a/Makefile b/Makefile index 23f1e36df..6d3f769d8 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,48 @@ ifndef POCO_BUILD export POCO_BUILD=$(POCO_BASE) endif -LIBPREFIX ?= lib +# +# Determine OS +# +POCO_HOST_OSNAME = $(shell uname) +ifeq ($(findstring CYGWIN,$(POCO_HOST_OSNAME)),CYGWIN) +POCO_HOST_OSNAME = Cygwin +endif + +ifeq ($(findstring MINGW,$(POCO_HOST_OSNAME)),MINGW) +POCO_HOST_OSNAME = MinGW +endif +POCO_HOST_OSARCH ?= $(subst /,-,$(shell uname -m | tr ' ' _)) + +# +# If POCO_CONFIG is not set, use the OS name as configuration name +# +ifndef POCO_CONFIG +POCO_CONFIG = $(POCO_HOST_OSNAME) +endif +#$(info POCO_CONFIG = $(POCO_CONFIG)) + +# +# Include System Specific Settings +# +include $(POCO_BASE)/build/config/$(POCO_CONFIG) + +# +# Determine operating system +# +ifndef POCO_TARGET_OSNAME +OSNAME := $(POCO_HOST_OSNAME) +else +OSNAME := $(POCO_TARGET_OSNAME) +endif +#$(info OSNAME = $(OSNAME)) + +ifndef POCO_TARGET_OSARCH +OSARCH := $(POCO_HOST_OSARCH) +else +OSARCH := $(POCO_TARGET_OSARCH) +endif +#$(info OSARCH = $(OSARCH)) .PHONY: poco all libexecs cppunit tests samples cleans clean distclean install @@ -29,7 +70,7 @@ poco: libexecs $(if $(TESTS),tests) $(if $(SAMPLES),samples) all: libexecs tests samples INSTALLDIR = $(DESTDIR)$(POCO_PREFIX) -COMPONENTS = Foundation XML JSON Util Net Crypto NetSSL_OpenSSL Data Data/SQLite Data/ODBC Data/MySQL MongoDB Zip PageCompiler PageCompiler/File2Page CppParser PDF +COMPONENTS = Foundation XML JSON Util Net Crypto NetSSL_OpenSSL Data Data/SQLite Data/ODBC Data/MySQL MongoDB Redis Zip PageCompiler PageCompiler/File2Page CppParser PDF cppunit: $(MAKE) -C $(POCO_BASE)/CppUnit @@ -49,13 +90,17 @@ install: libexecs find $(POCO_BUILD)/$$comp/bin -perm -700 -type f -exec cp -f {} $(INSTALLDIR)/bin \; ; \ fi ; \ done - find $(POCO_BUILD)/lib/$(POCO_TARGET_OSNAME)/$(POCO_TARGET_OSARCH) -name "$(LIBPREFIX)Poco*" -type f -exec cp -f {} $(INSTALLDIR)/lib \; - find $(POCO_BUILD)/lib/$(POCO_TARGET_OSNAME)/$(POCO_TARGET_OSARCH) -name "$(LIBPREFIX)Poco*" -type l -exec cp -Rf {} $(INSTALLDIR)/lib \; +ifeq ($(OSNAME), Cygwin) + find $(POCO_BUILD)/lib/$(OSNAME)/$(OSARCH) -name "cygPoco*" -type f -exec cp -f {} $(INSTALLDIR)/bin \; + find $(POCO_BUILD)/lib/$(OSNAME)/$(OSARCH) -name "cygPoco*" -type l -exec cp -Rf {} $(INSTALLDIR)/bin \; +endif + find $(POCO_BUILD)/lib/$(OSNAME)/$(OSARCH) -name "libPoco*" -type f -exec cp -f {} $(INSTALLDIR)/lib \; + find $(POCO_BUILD)/lib/$(OSNAME)/$(OSARCH) -name "libPoco*" -type l -exec cp -Rf {} $(INSTALLDIR)/lib \; -libexecs = Foundation-libexec XML-libexec JSON-libexec Util-libexec Net-libexec Crypto-libexec NetSSL_OpenSSL-libexec Data-libexec Data/SQLite-libexec Data/ODBC-libexec Data/MySQL-libexec MongoDB-libexec Zip-libexec PageCompiler-libexec PageCompiler/File2Page-libexec CppParser-libexec PDF-libexec -tests = Foundation-tests XML-tests JSON-tests Util-tests Net-tests Crypto-tests NetSSL_OpenSSL-tests Data-tests Data/SQLite-tests Data/ODBC-tests Data/MySQL-tests MongoDB-tests Zip-tests CppParser-tests PDF-tests +libexecs = Foundation-libexec XML-libexec JSON-libexec Util-libexec Net-libexec Crypto-libexec NetSSL_OpenSSL-libexec Data-libexec Data/SQLite-libexec Data/ODBC-libexec Data/MySQL-libexec MongoDB-libexec Redis-libexec Zip-libexec PageCompiler-libexec PageCompiler/File2Page-libexec CppParser-libexec PDF-libexec +tests = Foundation-tests XML-tests JSON-tests Util-tests Net-tests Crypto-tests NetSSL_OpenSSL-tests Data-tests Data/SQLite-tests Data/ODBC-tests Data/MySQL-tests MongoDB-tests Redis-tests Zip-tests CppParser-tests PDF-tests samples = Foundation-samples XML-samples JSON-samples Util-samples Net-samples Crypto-samples NetSSL_OpenSSL-samples Data-samples MongoDB-samples Zip-samples PageCompiler-samples PDF-samples -cleans = Foundation-clean XML-clean JSON-clean Util-clean Net-clean Crypto-clean NetSSL_OpenSSL-clean Data-clean Data/SQLite-clean Data/ODBC-clean Data/MySQL-clean MongoDB-clean Zip-clean PageCompiler-clean PageCompiler/File2Page-clean CppParser-clean PDF-clean +cleans = Foundation-clean XML-clean JSON-clean Util-clean Net-clean Crypto-clean NetSSL_OpenSSL-clean Data-clean Data/SQLite-clean Data/ODBC-clean Data/MySQL-clean MongoDB-clean Redis-clean Zip-clean PageCompiler-clean PageCompiler/File2Page-clean CppParser-clean PDF-clean .PHONY: $(libexecs) .PHONY: $(tests) @@ -223,6 +268,21 @@ MongoDB-clean: $(MAKE) -C $(POCO_BASE)/MongoDB/testsuite clean $(MAKE) -C $(POCO_BASE)/MongoDB/samples clean +Redis-libexec: Foundation-libexec Net-libexec + $(MAKE) -C $(POCO_BASE)/Redis + +Redis-tests: Redis-libexec cppunit + $(MAKE) -C $(POCO_BASE)/Redis/testsuite + +#No samples yet ... uncomment this when added, and add Redis-samples to samples above +#Redis-samples: Redis-libexec +# $(MAKE) -C $(POCO_BASE)/Redis/samples + +Redis-clean: + $(MAKE) -C $(POCO_BASE)/Redis clean + $(MAKE) -C $(POCO_BASE)/Redis/testsuite clean +# $(MAKE) -C $(POCO_BASE)/Redis/samples clean + Zip-libexec: Foundation-libexec Net-libexec Util-libexec XML-libexec $(MAKE) -C $(POCO_BASE)/Zip diff --git a/PageCompiler/samples/HTTPTimeServer/Makefile b/PageCompiler/samples/HTTPTimeServer/Makefile index 63489e786..b88c5d900 100644 --- a/PageCompiler/samples/HTTPTimeServer/Makefile +++ b/PageCompiler/samples/HTTPTimeServer/Makefile @@ -8,8 +8,13 @@ include $(POCO_BASE)/build/rules/global +# Cygwin Poco*.dll should be on PATH +ifeq ($(OSNAME), CYGWIN) + PATH :=$(LIBPATH):$(PATH) +endif + # Where to find the PageCompiler executable -PAGECOMPILER = $(POCO_BASE)/PageCompiler/bin/$(POCO_HOST_OSNAME)/$(POCO_HOST_OSARCH)/cpspc +PAGECOMPILER = $(POCO_BUILD)/PageCompiler/bin/$(OSNAME)/$(OSARCH)/cpspc objects = HTTPTimeServerApp TimeHandler @@ -20,7 +25,7 @@ target_libs = PocoUtil PocoJSON PocoNet PocoXML PocoFoundation include $(POCO_BASE)/build/rules/exec ifdef POCO_UNBUNDLED - SYSLIBS += -lz -lpcre -lexpat + SYSLIBS += -lz -lpcre -lexpat endif # Rule for runnning PageCompiler diff --git a/Redis/CMakeLists.txt b/Redis/CMakeLists.txt new file mode 100644 index 000000000..191014eee --- /dev/null +++ b/Redis/CMakeLists.txt @@ -0,0 +1,37 @@ +set(LIBNAME "Redis") +set(POCO_LIBNAME "Poco${LIBNAME}") + +# Sources +file(GLOB SRCS_G "src/*.cpp") +POCO_SOURCES_AUTO( SRCS ${SRCS_G}) + +# Headers +file(GLOB_RECURSE HDRS_G "include/*.h" ) +POCO_HEADERS_AUTO( SRCS ${HDRS_G}) + +add_library( "${LIBNAME}" ${LIB_MODE} ${SRCS} ) +add_library( "${POCO_LIBNAME}" ALIAS "${LIBNAME}") +set_target_properties( "${LIBNAME}" + PROPERTIES + VERSION ${SHARED_LIBRARY_VERSION} SOVERSION ${SHARED_LIBRARY_VERSION} + OUTPUT_NAME ${POCO_LIBNAME} + DEFINE_SYMBOL Redis_EXPORTS + ) + +target_link_libraries( "${LIBNAME}" Net Foundation) +target_include_directories( "${LIBNAME}" + PUBLIC + $ + $ + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src + ) +target_compile_definitions("${LIBNAME}" PUBLIC ${LIB_MODE_DEFINITIONS}) + +POCO_INSTALL("${LIBNAME}") +POCO_GENERATE_PACKAGE("${LIBNAME}") + +if (ENABLE_TESTS) +# add_subdirectory(samples) + add_subdirectory(testsuite) +endif () + diff --git a/Redis/Makefile b/Redis/Makefile new file mode 100644 index 000000000..48d04ee4a --- /dev/null +++ b/Redis/Makefile @@ -0,0 +1,19 @@ +# +# Makefile +# +# $Id$ +# +# Makefile for Poco Redis +# + +include $(POCO_BASE)/build/rules/global + +INCLUDE += -I $(POCO_BASE)/Redis/include/Poco/Redis + +objects = AsyncReader Array Client Command Error Exception RedisStream RedisEventArgs Type + +target = PocoRedis +target_version = $(LIBVERSION) +target_libs = PocoFoundation PocoNet + +include $(POCO_BASE)/build/rules/lib diff --git a/Redis/Redis.progen b/Redis/Redis.progen new file mode 100644 index 000000000..72970aa94 --- /dev/null +++ b/Redis/Redis.progen @@ -0,0 +1,16 @@ +vc.project.guid = ${vc.project.guidFromName} +vc.project.name = Redis +vc.project.target = Poco${vc.project.name} +vc.project.type = library +vc.project.pocobase = .. +vc.project.outdir = ${vc.project.pocobase} +vc.project.platforms = Win32, x64 +vc.project.configurations = debug_shared, release_shared, debug_static_mt, release_static_mt, debug_static_md, release_static_md +vc.project.prototype = ${vc.project.name}_vs90.vcproj +vc.project.compiler.include = ..\\Foundation\\include;..\\Net\\include +vc.project.compiler.defines = +vc.project.compiler.defines.shared = ${vc.project.name}_EXPORTS +vc.project.compiler.defines.debug_shared = ${vc.project.compiler.defines.shared} +vc.project.compiler.defines.release_shared = ${vc.project.compiler.defines.shared} +vc.solution.create = true +vc.solution.include = testsuite\\TestSuite diff --git a/Redis/Redis_x64_vs90.sln b/Redis/Redis_x64_vs90.sln new file mode 100644 index 000000000..d91f9aaf0 --- /dev/null +++ b/Redis/Redis_x64_vs90.sln @@ -0,0 +1,60 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Redis", "Redis_x64_vs90.vcproj", "{4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestSuite", "testsuite\TestSuite_x64_vs90.vcproj", "{96CF3103-E49E-3F5E-A11D-6DBCDA043053}" + ProjectSection(ProjectDependencies) = postProject + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199} = {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + debug_shared|x64 = debug_shared|x64 + release_shared|x64 = release_shared|x64 + debug_static_mt|x64 = debug_static_mt|x64 + release_static_mt|x64 = release_static_mt|x64 + debug_static_md|x64 = debug_static_md|x64 + release_static_md|x64 = release_static_md|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_shared|x64.ActiveCfg = debug_shared|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_shared|x64.Build.0 = debug_shared|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_shared|x64.Deploy.0 = debug_shared|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_shared|x64.ActiveCfg = release_shared|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_shared|x64.Build.0 = release_shared|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_shared|x64.Deploy.0 = release_shared|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_static_mt|x64.ActiveCfg = debug_static_mt|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_static_mt|x64.Build.0 = debug_static_mt|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_static_mt|x64.Deploy.0 = debug_static_mt|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_static_mt|x64.ActiveCfg = release_static_mt|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_static_mt|x64.Build.0 = release_static_mt|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_static_mt|x64.Deploy.0 = release_static_mt|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_static_md|x64.ActiveCfg = debug_static_md|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_static_md|x64.Build.0 = debug_static_md|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.debug_static_md|x64.Deploy.0 = debug_static_md|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_static_md|x64.ActiveCfg = release_static_md|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_static_md|x64.Build.0 = release_static_md|x64 + {4FF2F34B-7F37-3ACD-AFBC-F21D6D426199}.release_static_md|x64.Deploy.0 = release_static_md|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_shared|x64.ActiveCfg = debug_shared|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_shared|x64.Build.0 = debug_shared|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_shared|x64.Deploy.0 = debug_shared|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_shared|x64.ActiveCfg = release_shared|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_shared|x64.Build.0 = release_shared|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_shared|x64.Deploy.0 = release_shared|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_static_mt|x64.ActiveCfg = debug_static_mt|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_static_mt|x64.Build.0 = debug_static_mt|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_static_mt|x64.Deploy.0 = debug_static_mt|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_static_mt|x64.ActiveCfg = release_static_mt|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_static_mt|x64.Build.0 = release_static_mt|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_static_mt|x64.Deploy.0 = release_static_mt|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_static_md|x64.ActiveCfg = debug_static_md|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_static_md|x64.Build.0 = debug_static_md|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.debug_static_md|x64.Deploy.0 = debug_static_md|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_static_md|x64.ActiveCfg = release_static_md|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_static_md|x64.Build.0 = release_static_md|x64 + {96CF3103-E49E-3F5E-A11D-6DBCDA043053}.release_static_md|x64.Deploy.0 = release_static_md|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Redis/Redis_x64_vs90.vcproj b/Redis/Redis_x64_vs90.vcproj new file mode 100644 index 000000000..592421eef --- /dev/null +++ b/Redis/Redis_x64_vs90.vcproj @@ -0,0 +1,588 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Redis/cmake/PocoRedisConfig.cmake b/Redis/cmake/PocoRedisConfig.cmake new file mode 100644 index 000000000..300a9f65f --- /dev/null +++ b/Redis/cmake/PocoRedisConfig.cmake @@ -0,0 +1,4 @@ +include(CMakeFindDependencyMacro) +find_dependency(PocoFoundation) +find_dependency(PocoNet) +include("${CMAKE_CURRENT_LIST_DIR}/PocoRedisTargets.cmake") diff --git a/Redis/dependencies b/Redis/dependencies new file mode 100644 index 000000000..f6d54af82 --- /dev/null +++ b/Redis/dependencies @@ -0,0 +1,2 @@ +Foundation +Net diff --git a/Redis/include/Poco/Redis/Array.h b/Redis/include/Poco/Redis/Array.h new file mode 100644 index 000000000..f42dd4b6b --- /dev/null +++ b/Redis/include/Poco/Redis/Array.h @@ -0,0 +1,296 @@ +// +// Array.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Array +// +// Definition of the Array class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_Array_INCLUDED +#define Redis_Array_INCLUDED + +#include +#include + +#include "Poco/Redis/Redis.h" +#include "Poco/Redis/Type.h" +#include "Poco/Redis/Exception.h" + +namespace Poco { +namespace Redis { + +class Redis_API Array + /// Represents a Redis Array. An Array can contain Integers, Strings, + /// Bulk Strings, Errors and other arrays. It can also contain a Null + /// value. +{ +public: + + typedef std::vector::const_iterator const_iterator; + + Array(); + /// Default constructor. As long as there are no elements added, + /// the array will contain a Null value. + + Array(const Array& copy); + /// Copy constructor. + + virtual ~Array(); + /// Destructor. + + template + Array& operator<<(const T& arg) + /// Adds the argument to the array. + /// Note: a std::string will be added as a BulkString because this + /// is commonly used for representing strings in Redis. If you + /// really need a simple string, use addSimpleString. + { + return add(arg); + } + + Array& operator<<(const char* s); + /// Special implementation for const char* + /// Note: the specialization creates a BulkString. If you need + /// a simple string, call addSimpleString. + + Array& operator<<(const std::vector& strings); + /// Special implementation for a vector with strings + /// All strings will be added as a BulkString. + + Array& add(); + /// Adds an Null BulkString + + template + Array& add(const T& arg) + /// Adds an element to the array. + /// Note: the specialization for std::string will add a BulkString! + /// If you really need a simple string, call addSimpleString. + { + addRedisType(new Type(arg)); + return *this; + } + + Array& add(const char* s); + /// Special implementation for const char* + /// Note: the specialization creates a BulkString. If you need + /// a simple string, call addSimpleString. + + Array& add(const std::vector& strings); + /// Special implementation for a vector with strings + /// All strings will be added as a BulkString. + + Array& addRedisType(RedisType::Ptr value); + /// Adds a Redis element. + + Array& addSimpleString(const std::string& value); + /// Adds a simple string (can't contain newline characters!) + + const_iterator begin() const; + /// Returns an iterator to the start of the array. Note: + /// this can throw a NullValueException when this is a Null array. + + void clear(); + /// Removes all elements from the array. + + const_iterator end() const; + /// Returns an iterator to the end of the array. Note: + /// this can throw a NullValueException when this is a Null array. + + template + T get(size_t pos) const + /// Returns the element on the given position and tries to convert + /// to the template type. A BadCastException will be thrown when the + /// the conversion fails. An InvalidArgumentException will be thrown + /// when the index is out of range. When the array is a Null array + /// a NullValueException is thrown. + { + if ( _elements.isNull() ) throw NullValueException(); + + if ( pos >= _elements.value().size() ) throw InvalidArgumentException(); + + RedisType::Ptr element = _elements.value().at(pos); + if ( RedisTypeTraits::TypeId == element->type() ) + { + Type* concrete = dynamic_cast* >(element.get()); + if ( concrete != NULL ) return concrete->value(); + } + throw BadCastException(); + } + + int getType(size_t pos) const; + /// Returns the type of the element. This can throw a NullValueException + /// when this array is a Null array. An InvalidArgumentException will + /// be thrown when the index is out of range. + + bool isNull() const; + /// Returns true when this is a Null array. + + void makeNull(); + /// Turns the array into a Null array. When the array already has some + /// elements, the array will be cleared. + + std::string toString() const; + /// Returns the String representation as specified in the + /// Redis Protocol specification. + + size_t size() const; + /// Returns the size of the array. Note: + /// this can throw a NullValueException when this is a Null array. + +private: + + Nullable > _elements; + + void checkNull(); + /// Checks for null array and sets a new vector if true. +}; + +inline Array& Array::operator<<(const char* s) +{ + BulkString value(s); + return add(value); +} + +inline Array& Array::operator<<(const std::vector& strings) +{ + return add(strings); +} + +inline Array& Array::add() +{ + BulkString value; + return add(value); +} + +template<> +inline Array& Array::add(const std::string& arg) +{ + BulkString value(arg); + return add(value); +} + +inline Array& Array::add(const char* s) +{ + BulkString value(s); + return add(value); +} + +inline Array& Array::add(const std::vector& strings) +{ + for(std::vector::const_iterator it = strings.begin(); it != strings.end(); ++it) + { + add(*it); + } + return *this; +} + +inline Array& Array::addSimpleString(const std::string& value) +{ + return addRedisType(new Type(value)); +} + +inline Array::const_iterator Array::begin() const +{ + return _elements.value().begin(); +} + +inline void Array::checkNull() +{ + std::vector v; + if ( _elements.isNull() ) _elements.assign(v); +} + +inline void Array::clear() +{ + if ( !_elements.isNull() ) + { + _elements.value().clear(); + } +} + +inline Array::const_iterator Array::end() const +{ + return _elements.value().end(); +} + +inline bool Array::isNull() const +{ + return _elements.isNull(); +} + +inline void Array::makeNull() +{ + if ( !_elements.isNull() ) _elements.value().clear(); + + _elements.clear(); +} + +inline size_t Array::size() const +{ + return _elements.value().size(); +} + + +template<> +struct RedisTypeTraits +{ + enum { TypeId = RedisType::REDIS_ARRAY }; + + static const char marker = '*'; + + static std::string toString(const Array& value) + { + std::stringstream result; + result << marker; + if ( value.isNull() ) + { + result << "-1" << LineEnding::NEWLINE_CRLF; + } + else + { + result << value.size() << LineEnding::NEWLINE_CRLF; + for(std::vector::const_iterator it = value.begin(); + it != value.end(); ++it) + { + result << (*it)->toString(); + } + } + return result.str(); + } + + static void read(RedisInputStream& input, Array& value) + { + value.clear(); + + Int64 length = NumberParser::parse64(input.getline()); + + if ( length != -1 ) + { + for(int i = 0; i < length; ++i) + { + char marker = input.get(); + RedisType::Ptr element = RedisType::createRedisType(marker); + + if ( element.isNull() ) + throw RedisException("Wrong answer received from Redis server"); + + element->read(input); + value.addRedisType(element); + } + } + } +}; + + +}} + +#endif // Redis_Array_INCLUDED diff --git a/Redis/include/Poco/Redis/AsyncReader.h b/Redis/include/Poco/Redis/AsyncReader.h new file mode 100644 index 000000000..ad99a3975 --- /dev/null +++ b/Redis/include/Poco/Redis/AsyncReader.h @@ -0,0 +1,95 @@ +// +// AsyncReader.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: AsyncReader +// +// Definition of the AsyncReader class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef Redis_AsyncReader_INCLUDED +#define Redis_AsyncReader_INCLUDED + + +#include "Poco/Redis/Redis.h" +#include "Poco/Redis/Client.h" +#include "Poco/Redis/RedisEventArgs.h" +#include "Poco/Activity.h" + +namespace Poco { +namespace Redis { + + +class Redis_API AsyncReader + /// Wrapper around a Redis client to read messages asynchronously. Use this + /// for publish/subscribe. The redisResponse event is used to notify that + /// a message is received. When a reader is started for a Redis server, + /// you should use execute, because this class is responsible for + /// reading all replies. +{ +public: + + BasicEvent redisResponse; + /// Event that is called when a message is received + BasicEvent redisException; + /// Event that is called when an error occurred. + + AsyncReader(Client& client); + /// Constructor. + + virtual ~AsyncReader(); + /// Destructor + + bool isStopped(); + /// Returns true if the activity is not running, false when it is. + + void start(); + /// Starts the activity to read replies from the Redis server. + + void stop(); + /// Stops the read activity. + +protected: + + void runActivity(); + +private: + + AsyncReader(const AsyncReader&); + AsyncReader& operator = (const AsyncReader&); + + + Activity _activity; + + Client& _client; +}; + + +inline bool AsyncReader::isStopped() +{ + return _activity.isStopped(); +} + +inline void AsyncReader::start() +{ + _activity.start(); +} + +inline void AsyncReader::stop() +{ + _activity.stop(); +} + + +} } // namespace Poco::Redis + +#endif //Redis_AsyncReader_INCLUDED diff --git a/Redis/include/Poco/Redis/Client.h b/Redis/include/Poco/Redis/Client.h new file mode 100644 index 000000000..eba4b10f1 --- /dev/null +++ b/Redis/include/Poco/Redis/Client.h @@ -0,0 +1,232 @@ +// +// Client.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Client +// +// Definition of the Client class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef Redis_Client_INCLUDED +#define Redis_Client_INCLUDED + +#include "Poco/Net/SocketAddress.h" +#include "Poco/Timespan.h" + +#include "Poco/Redis/Redis.h" +#include "Poco/Redis/Array.h" +#include "Poco/Redis/Error.h" +#include "Poco/Redis/RedisStream.h" + +namespace Poco { +namespace Redis { + + +class Redis_API Client + /// Represents a connection to a Redis server. + /// + /// A command is always made from an Array and a reply can be a signed 64 + /// bit integer, a simple string, a bulk string, an array or an error. The + /// first element of the command array is the Redis command. A simple string + /// is a string that cannot contain a CR or LF character. A bulk string is + /// implemented as a typedef for Poco::Nullable. This is + /// because a bulk string can represent a Null value. + /// + /// BulkString bs = client.execute(...); + /// if ( bs.isNull() ) + /// { + /// // We have a Null value + /// } + /// else + /// { + /// // We have a string value + /// } + /// + /// To create Redis commands, the factory methods of the Command class can + /// be used or the Array class can be used directly. + /// + /// Command llen = Command::llen("list"); + /// + /// is the same as + /// + /// Array command; + /// command.add("LLEN").add("list"); + /// + /// or + /// + /// Array command; + /// command << "LLEN" << "list"; + /// + /// or even + /// + /// Command command("LLEN"); + /// command << "list"; +{ +public: + + typedef SharedPtr Ptr; + + Client(); + /// Default constructor. Use this when you want to + /// connect later on. + + Client(const std::string& hostAndPort); + /// Constructor which connects to the given Redis host/port. + /// The host and port must be separated with a colon. + + Client(const std::string& host, int port); + /// Constructor which connects to the given Redis host/port. + + Client(const Net::SocketAddress& addrs); + /// Constructor which connects to the given Redis host/port. + + virtual ~Client(); + /// Destructor. + + Net::SocketAddress address() const; + /// Returns the address of the Redis connection. + + void connect(const std::string& hostAndPort); + /// Connects to the given Redis server. The host and port must be + /// separated with a colon. + + void connect(const std::string& host, int port); + /// Connects to the given Redis server. + + void connect(const Net::SocketAddress& addrs); + /// Connects to the given Redis server. + + void connect(const std::string& hostAndPort, const Timespan& timeout); + /// Connects to the given Redis server. The host and port must be + /// separated with a colon. + + void connect(const std::string& host, int port, const Timespan& timeout); + /// Connects to the given Redis server. + + void connect(const Net::SocketAddress& addrs, const Timespan& timeout); + /// Connects to the given Redis server. + + void disconnect(); + /// Disconnects from the Redis server. + + template + T execute(const Array& command) + /// Sends the Redis Command to the server. It gets the reply + /// and tries to convert it to the given template type. + /// + /// A specialization exists for type void, which doesn't read + /// the reply. If the server sends a reply, it is your + /// responsibility to read it ... (Use this for pipelining) + /// + /// A BadCastException will be thrown when the reply couldn't be + /// converted. Supported types are Int64, std::string, BulkString, + /// Array and void. When the reply is an Error, it will throw + /// a RedisException. + { + T result; + writeCommand(command, true); + readReply(result); + return result; + } + + void flush(); + /// Flush the output buffer to Redis. Use this when commands + /// are stored in the buffer to send them all at once to Redis. + + RedisType::Ptr sendCommand(const Array& command); + /// Sends a Redis command to the server and returns the reply. + /// Use this when the type of the reply isn't known. + + RedisType::Ptr readReply(); + /// Read a reply from the Redis server. + + template + void readReply(T& result) + /// Read a reply from the Redis server and tries to convert that reply + /// to the template type. When the reply is a Redis error, it will + /// throw a RedisException. A BadCastException will be thrown, when + /// the reply is not of the given type. + { + RedisType::Ptr redisResult = readReply(); + if (redisResult->type() == RedisTypeTraits::TypeId) + { + Type* error = dynamic_cast*>(redisResult.get()); + throw RedisException(error->value().getMessage()); + } + + if (redisResult->type() == RedisTypeTraits::TypeId) + { + Type* type = dynamic_cast*>(redisResult.get()); + if (type != NULL) result = type->value(); + } + else throw BadCastException(); + } + + Array sendCommands(const std::vector& commands); + /// Sends all commands (pipelining) to the Redis server before + /// getting all replies. + + void setReceiveTimeout(const Timespan& timeout); + /// Sets a receive timeout. + +private: + + Client(const Client&); + Client& operator = (const Client&); + + Net::SocketAddress _address; + Net::StreamSocket _socket; + + void connect(); + /// Connects to the Redis server + + void connect(const Timespan& timeout); + /// Connects to the Redis server and sets a timeout. + + void writeCommand(const Array& command, bool flush); + /// Sends a request to the Redis server. Use readReply to get the + /// answer. Can also be used for pipelining commands. Make sure you + /// call readReply as many times as you called writeCommand, even when + /// an error occurred on a command. + + RedisInputStream* _input; + RedisOutputStream* _output; +}; + + +inline Net::SocketAddress Client::address() const +{ + return _address; +} + +template<> inline +void Client::execute(const Array& command) +{ + writeCommand(command, false); +} + +inline void Client::flush() +{ + poco_assert(_output); + _output->flush(); +} + +inline void Client::setReceiveTimeout(const Timespan& timeout) +{ + _socket.setReceiveTimeout(timeout); +} + + +} } // namespace Poco::Redis + + +#endif //Redis_Client_INCLUDED diff --git a/Redis/include/Poco/Redis/Command.h b/Redis/include/Poco/Redis/Command.h new file mode 100644 index 000000000..2c67b871d --- /dev/null +++ b/Redis/include/Poco/Redis/Command.h @@ -0,0 +1,256 @@ +// +// Command.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Command +// +// Definition of the Command class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_Command_INCLUDED +#define Redis_Command_INCLUDED + +#include "Poco/Redis/Redis.h" +#include "Poco/Redis/Array.h" + +#include +#include + +namespace Poco { +namespace Redis { + +class Redis_API Command : public Array + /// Helper class for creating commands. This class contains + /// factory methods for commonly used Redis commands. + /// There are two ways of building commands: + /// 1. Use this class and the factory methods + /// 2. Use the Array or Command class and build the command using the add + /// method or << operator. + /// For example: + /// + /// Command cmd = Command::set("mykey", "Hello"); + /// + /// is the same as: + /// + /// Array cmd; + /// cmd << "SET" << "mykey" << "Hello"; + /// +{ +public: + + typedef std::vector StringVec; + + Command(const std::string& command); + /// Constructor + + Command(const Command& copy); + /// Copy constructor + + virtual ~Command(); + /// Destructor + + static Command append(const std::string& key, const std::string& value); + /// Returns an APPEND command + + static Command blpop(const StringVec& lists, Int64 timeout = 0); + /// Returns a BLPOP command + + static Command brpop(const StringVec& lists, Int64 timeout = 0); + /// Returns a BRPOP command + + static Command brpoplpush(const std::string& sourceList, const std::string& destinationList, Int64 timeout = 0); + /// Returns a BRPOPLPUSH command + + static Command decr(const std::string& key, Int64 by = 0); + /// Returns an DECR or DECRBY command. Calls DECR when by is omitted or zero. + + static Command del(const std::string& key); + /// Returns an DEL command + + static Command del(const StringVec& keys); + /// Returns an DEL command + + static Command get(const std::string& key); + /// Returns an GET command + + static Command hdel(const std::string& hash, const std::string& field); + /// Returns an HDEL command + + static Command hdel(const std::string& hash, const StringVec& fields); + /// Returns an HDEL command + + static Command hexists(const std::string& hash, const std::string& field); + /// Returns an HEXISTS command + + static Command hget(const std::string& hash, const std::string& field); + /// Returns an HGET command + + static Command hgetall(const std::string& hash); + /// Returns an HGETALL command + + static Command hincrby(const std::string& hash, const std::string& field, Int64 by = 1); + /// Returns an HINCRBY command + + static Command hkeys(const std::string& hash); + /// Returns an HKEYS command + + static Command hlen(const std::string& hash); + /// Returns an HLEN command + + static Command hmget(const std::string& hash, const StringVec& fields); + /// Returns an HMGET command + + static Command hmset(const std::string& hash, std::map& fields); + /// Returns a HMSET command + + static Command hset(const std::string& hash, const std::string& field, const std::string& value, bool create = true); + /// Returns an HSET or HSETNX (when create is false) command + + static Command hset(const std::string& hash, const std::string& field, Int64 value, bool create = true); + /// Returns an HSET or HSETNX (when create is false) command + + static Command hstrlen(const std::string& hash, const std::string& field); + /// Returns an HSTRLEN command (Available for Redis 3.2) + + static Command hvals(const std::string& hash); + /// Returns an HVALS command + + static Command incr(const std::string& key, Int64 by = 0); + /// Returns an INCR or INCRBY command. Calls INCR when by is omitted or zero. + + static Command lindex(const std::string& list, Int64 index = 0); + /// Returns a LINDEX command + + static Command linsert(const std::string& list, bool before, const std::string& reference, const std::string& value); + /// Returns a LINSERT command + + static Command llen(const std::string& list); + /// Returns a LLEN command + + static Command lpop(const std::string& list); + /// Returns a LPOP command + + static Command lpush(const std::string& list, const std::string& value, bool create = true); + /// Returns a LPUSH or LPUSHX (when create is false) command + + static Command lpush(const std::string& list, const StringVec& value, bool create = true); + /// Returns a LPUSH or LPUSHX (when create is false) command + + static Command lrange(const std::string& list, Int64 start = 0, Int64 stop = -1); + /// Returns a LRANGE command. When start and stop is omitted an LRANGE + /// command will returned that returns all items of the list. + + static Command lrem(const std::string& list, Int64 count, const std::string& value); + /// Returns a LREM command + + static Command lset(const std::string& list, Int64 index, const std::string& value); + /// Returns a LSET command + + static Command ltrim(const std::string& list, Int64 start = 0, Int64 stop = -1); + /// Returns a LTRIM command + + static Command mget(const StringVec& keys); + /// Returns a MGET command + + static Command mset(const std::map& keyvalues, bool create = true); + /// Returns a MSET or MSETNX (when create is false) command + + static Command sadd(const std::string& set, const std::string& value); + /// Returns a SADD command + + static Command sadd(const std::string& set, const StringVec& values); + /// Returns a SADD command + + static Command scard(const std::string& set); + /// Returns a SCARD command + + static Command sdiff(const std::string& set1, const std::string& set2); + /// Returns a SDIFF command + + static Command sdiff(const std::string& set, const StringVec& sets); + /// Returns a SDIFF command + + static Command sdiffstore(const std::string& set, const std::string& set1, const std::string& set2); + /// Returns a SDIFFSTORE command + + static Command sdiffstore(const std::string& set, const StringVec& sets); + /// Returns a SDIFFSTORE command + + static Command set(const std::string& key, const std::string& value, bool overwrite = true, const Poco::Timespan& expireTime = 0, bool create = true); + /// Returns a SET command to set the key with a value + + static Command set(const std::string& key, Int64 value, bool overwrite = true, const Poco::Timespan& expireTime = 0, bool create = true); + /// Returns a SET command to set the key with a value + + static Command sinter(const std::string& set1, const std::string& set2); + /// Returns a SINTER command + + static Command sinter(const std::string& set, const StringVec& sets); + /// Returns a SINTER command + + static Command sinterstore(const std::string& set, const std::string& set1, const std::string& set2); + /// Returns a SINTERSTORE command + + static Command sinterstore(const std::string& set, const StringVec& sets); + /// Returns a SINTERSTORE command + + static Command sismember(const std::string& set, const std::string& member); + /// Returns a SISMEMBER command + + static Command smembers(const std::string& set); + /// Returns a SMEMBERS command + + static Command smove(const std::string& source, const std::string& destination, const std::string& member); + /// Returns a SMOVE command + + static Command spop(const std::string& set, Int64 count = 0); + /// Returns a SPOP command + + static Command srandmember(const std::string& set, Int64 count = 0); + /// Returns a SRANDMEMBER command + + static Command srem(const std::string& set, const std::string& member); + /// Returns a SREM command + + static Command srem(const std::string& set, const StringVec& members); + /// Returns a SREM command + + static Command sunion(const std::string& set1, const std::string& set2); + /// Returns a SUNION command + + static Command sunion(const std::string& set, const StringVec& sets); + /// Returns a SUNION command + + static Command sunionstore(const std::string& set, const std::string& set1, const std::string& set2); + /// Returns a SUNIONSTORE command + + static Command sunionstore(const std::string& set, const StringVec& sets); + /// Returns a SUNIONSTORE command + + static Command rename(const std::string& key, const std::string& newName, bool overwrite = true); + /// Returns a RENAME or RENAMENX when overwrite is false + + static Command rpop(const std::string& list); + /// Returns a RPOP command + + static Command rpoplpush(const std::string& sourceList, const std::string& destinationList); + /// Returns a RPOPLPUSH command + + static Command rpush(const std::string& list, const std::string& value, bool create = true); + /// Returns a RPUSH or RPUSHX (when create is false) command + + static Command rpush(const std::string& list, const StringVec& value, bool create = true); + /// Returns a RPUSH or RPUSHX (when create is false) command +}; + +}} // namespace Poco::Redis + +#endif // Redis_Command_INCLUDED diff --git a/Redis/include/Poco/Redis/Error.h b/Redis/include/Poco/Redis/Error.h new file mode 100644 index 000000000..fd1edc7ce --- /dev/null +++ b/Redis/include/Poco/Redis/Error.h @@ -0,0 +1,81 @@ +// +// Error.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Error +// +// Definition of the Error class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_Error_INCLUDED +#define Redis_Error_INCLUDED + +#include "Poco/Redis/Type.h" + +namespace Poco { +namespace Redis { + +class Redis_API Error + /// Represent a Redis error +{ +public: + + Error(); + /// Constructor + + Error(const std::string& message); + /// Constructor + + virtual ~Error(); + /// Destructor + + std::string getMessage() const; + /// Returns the error message + + void setMessage(const std::string& message); + /// Sets the error message + +private: + + std::string _message; +}; + +inline std::string Error::getMessage() const +{ + return _message; +} + +inline void Error::setMessage(const std::string& message) +{ + _message = message; +} + +template<> +struct RedisTypeTraits +{ + enum { TypeId = RedisType::REDIS_ERROR }; + + static const char marker = '-'; + + static std::string toString(const Error& value) + { + return marker + value.getMessage() + LineEnding::NEWLINE_CRLF; + } + + static void read(RedisInputStream& input, Error& value) + { + value.setMessage(input.getline()); + } +}; + +}} // Namespace Poco::Redis + +#endif // Redis_Error_INCLUDED \ No newline at end of file diff --git a/Redis/include/Poco/Redis/Exception.h b/Redis/include/Poco/Redis/Exception.h new file mode 100644 index 000000000..d45d85348 --- /dev/null +++ b/Redis/include/Poco/Redis/Exception.h @@ -0,0 +1,32 @@ +// +// Exception.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Exception +// +// Definition of the Exception class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_Exception_INCLUDED +#define Redis_Exception_INCLUDED + +#include "Poco/Redis/Redis.h" +#include +#include "Poco/Exception.h" + +namespace Poco { +namespace Redis { + +POCO_DECLARE_EXCEPTION(Redis_API, RedisException, Exception) + +}} + +#endif // Redis_Exception_INCLUDED diff --git a/Redis/include/Poco/Redis/PoolableConnectionFactory.h b/Redis/include/Poco/Redis/PoolableConnectionFactory.h new file mode 100644 index 000000000..2dad8b450 --- /dev/null +++ b/Redis/include/Poco/Redis/PoolableConnectionFactory.h @@ -0,0 +1,111 @@ +// +// PoolableConnectionFactory.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: PoolableConnectionFactory +// +// Definition of the PoolableConnectionFactory class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef Redis_PoolableConnectionFactory_INCLUDED +#define Redis_PoolableConnectionFactory_INCLUDED + + +#include "Poco/Redis/Client.h" +#include "Poco/ObjectPool.h" + + +namespace Poco { + + +template<> +class PoolableObjectFactory + /// PoolableObjectFactory specialisation for Client. New connections + /// are created with the given address. +{ +public: + PoolableObjectFactory(Net::SocketAddress& address) + : _address(address) + { + } + + PoolableObjectFactory(const std::string& address) + : _address(address) + { + } + + Redis::Client::Ptr createObject() + { + return new Redis::Client(_address); + } + + bool validateObject(Redis::Client::Ptr pObject) + { + return true; + } + + void activateObject(Redis::Client::Ptr pObject) + { + } + + void deactivateObject(Redis::Client::Ptr pObject) + { + } + + void destroyObject(Redis::Client::Ptr pObject) + { + } + +private: + Net::SocketAddress _address; +}; + + +namespace Redis { + + +class Redis_API PooledConnection + /// Helper class for borrowing and returning a connection automatically from a pool. +{ +public: + PooledConnection(ObjectPool& pool, long timeoutMilliseconds = 0) : _pool(pool) + { + _client = _pool.borrowObject(timeoutMilliseconds); + } + + virtual ~PooledConnection() + { + try + { + _pool.returnObject(_client); + } + catch (...) + { + poco_unexpected(); + } + } + + operator Client::Ptr () + { + return _client; + } + +private: + ObjectPool& _pool; + Client::Ptr _client; +}; + + +} // namespace Redis +} // namespace Poco + +#endif // Redis_PoolableConnectionFactory_INCLUDED diff --git a/Redis/include/Poco/Redis/Redis.h b/Redis/include/Poco/Redis/Redis.h new file mode 100644 index 000000000..a25cd1598 --- /dev/null +++ b/Redis/include/Poco/Redis/Redis.h @@ -0,0 +1,66 @@ +// +// Redis.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Redis +// +// Basic definitions for the Poco Redis library. +// This file must be the first file included by every other Redis +// header file. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef RedisRedis_INCLUDED +#define RedisRedis_INCLUDED + + +#include "Poco/Foundation.h" + + +// +// The following block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the Redis_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// Redis_API functions as being imported from a DLL, wheras this DLL sees symbols +// defined with this macro as being exported. +// + + +#if defined(_WIN32) && defined(POCO_DLL) + #if defined(Redis_EXPORTS) + #define Redis_API __declspec(dllexport) + #else + #define Redis_API __declspec(dllimport) + #endif +#endif + + +#if !defined(Redis_API) + #if !defined(POCO_NO_GCC_API_ATTRIBUTE) && defined (__GNUC__) && (__GNUC__ >= 4) + #define Redis_API __attribute__ ((visibility ("default"))) + #else + #define Redis_API + #endif +#endif + + +// +// Automatically link Redis library. +// +#if defined(_MSC_VER) + #if !defined(POCO_NO_AUTOMATIC_LIBS) && !defined(Redis_EXPORTS) + #pragma comment(lib, "PocoRedis" POCO_LIB_SUFFIX) + #endif +#endif + + +#endif // RedisRedis_INCLUDED diff --git a/Redis/include/Poco/Redis/RedisEventArgs.h b/Redis/include/Poco/Redis/RedisEventArgs.h new file mode 100644 index 000000000..eca6c9877 --- /dev/null +++ b/Redis/include/Poco/Redis/RedisEventArgs.h @@ -0,0 +1,85 @@ +// +// RedisEventArgs.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: RedisEventArgs +// +// Definition of the RedisEventArgs class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_RedisEventArgs_INCLUDED +#define Redis_RedisEventArgs_INCLUDED + +#include "Poco/Redis/Type.h" + +namespace Poco { +namespace Redis { + +class Redis_API RedisEventArgs +{ +public: + RedisEventArgs(RedisType::Ptr message); + /// Constructor + + RedisEventArgs(Exception* e); + /// Constructor + + ~RedisEventArgs(); + /// Destructor + + RedisType::Ptr message() const; + /// Returns the message retrieved from the Redis server. + /// This can be a NULL pointer when this event is about an exception. + + const Exception* exception() const; + /// Returns the exception if any, otherwise it returns null pointer + + void stop(); + /// When called, the AsyncReader will stop. + /// Note: The AsyncReader will always stop when this is an exception + /// event. Use this for example for pub/sub when there are no + /// subcribers anymore ... + + bool isStopped() const; + /// Returns true when the AsyncReader will stop + +private: + RedisType::Ptr _message; + + Exception* _exception; + + bool _stop; +}; + +inline RedisType::Ptr RedisEventArgs::message() const +{ + return _message; +} + +inline const Exception* RedisEventArgs::exception() const +{ + return _exception; +} + +inline bool RedisEventArgs::isStopped() const +{ + return _stop; +} + +inline void RedisEventArgs::stop() +{ + _stop = true; +} + + +}} // namespace Poco::Redis + +#endif // Redis_RedisEventArgs_INCLUDED diff --git a/Redis/include/Poco/Redis/RedisStream.h b/Redis/include/Poco/Redis/RedisStream.h new file mode 100644 index 000000000..c75321368 --- /dev/null +++ b/Redis/include/Poco/Redis/RedisStream.h @@ -0,0 +1,112 @@ +// +// RedisStream.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: RedisStream +// +// Definition of the RedisStream class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_RedisStream_INCLUDED +#define Redis_RedisStream_INCLUDED + +#include "Poco/BufferedStreamBuf.h" +#include "Poco/Net/StreamSocket.h" + +#include +#include + +namespace Poco { +namespace Redis { + +class RedisStreamBuf : public BufferedStreamBuf + /// BufferedStreamBuf for Redis +{ +public: + RedisStreamBuf(Net::StreamSocket& redis); + /// Constructor + + ~RedisStreamBuf(); + /// Destructor + + std::string readLine(); + /// Reads a line from Redis (until \r\n is encountered). + +protected: + int readFromDevice(char* buffer, std::streamsize length); + + int writeToDevice(const char* buffer, std::streamsize length); + +private: + + enum + { + STREAM_BUFFER_SIZE = 1024 + }; + + Net::StreamSocket& _redis; +}; + +class RedisIOS: public virtual std::ios +{ +public: + RedisIOS(Net::StreamSocket& redis); + /// Creates the RedisIOS with the given socket. + + ~RedisIOS(); + /// Destroys the RedisIOS. + /// + /// Flushes the buffer, but does not close the socket. + + RedisStreamBuf* rdbuf(); + /// Returns a pointer to the internal RedisStreamBuf. + + void close(); + /// Flushes the stream. + +protected: + RedisStreamBuf _buf; +}; + + +class RedisOutputStream: public RedisIOS, public std::ostream + /// An output stream for writing to a Redis server. +{ +public: + RedisOutputStream(Net::StreamSocket& redis); + /// Creates the RedisOutputStream with the given socket. + + ~RedisOutputStream(); + /// Destroys the RedisOutputStream. + /// + /// Flushes the buffer. +}; + + +class RedisInputStream: public RedisIOS, public std::istream + /// An input stream for reading from a Redis server. +{ +public: + RedisInputStream(Net::StreamSocket& redis); + /// Creates the RedisInputStream with the given socket. + + ~RedisInputStream(); + /// Destroys the RedisInputStream. + + std::string getline(); + /// Redis uses /r/n as delimiter. This getline version removes + /// the /r from the result. +}; + + +}} // Poco::Redis + +#endif // Redis_RedisStream_INCLUDED \ No newline at end of file diff --git a/Redis/include/Poco/Redis/Type.h b/Redis/include/Poco/Redis/Type.h new file mode 100644 index 000000000..b926c89a3 --- /dev/null +++ b/Redis/include/Poco/Redis/Type.h @@ -0,0 +1,276 @@ +// +// Type.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Type +// +// Definition of the Type class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#ifndef Redis_Type_INCLUDED +#define Redis_Type_INCLUDED + +#include "Poco/LineEndingConverter.h" +#include "Poco/NumberFormatter.h" +#include "Poco/NumberParser.h" +#include "Poco/SharedPtr.h" +#include "Poco/Nullable.h" + +#include "Poco/Redis/Redis.h" +#include "Poco/Redis/RedisStream.h" + +namespace Poco { +namespace Redis { + + +class Redis_API RedisType + /// Base class for all Redis types. This class makes it possible to store + /// element with different types in Array. +{ +public: + + enum Types { + REDIS_INTEGER, // Redis Integer + REDIS_SIMPLE_STRING, // Redis Simple String + REDIS_BULK_STRING, // Redis Bulkstring + REDIS_ARRAY, // Redis Array + REDIS_ERROR // Redis Error + }; + + typedef SharedPtr Ptr; + + RedisType(); + /// Constructor + + virtual ~RedisType(); + /// Destructor + + bool isArray() const; + /// Returns true when the value is a Redis array. + + bool isBulkString() const; + /// Returns true when the value is a Redis bulkstring. + + bool isError() const; + /// Returns true when the value is a Redis error. + + bool isInteger() const; + /// Returns true when the value is a Redis integer (64 bit integer) + + bool isSimpleString() const; + /// Returns true when the value is a simple string. + + virtual int type() const = 0; + /// Returns the type of the value. + + virtual void read(RedisInputStream& input) = 0; + /// Reads the value from the stream. + + virtual std::string toString() const = 0; + /// Converts the value to a RESP (REdis Serialization Protocol) string. + + static RedisType::Ptr createRedisType(char marker); + /// Create a Redis type based on the marker : + /// + : a simple string (std::string) + /// - : an error (Error) + /// $ : a bulk string (BulkString) + /// * : an array (Array) + /// : : a signed 64 bit integer (Int64) + +private: + +}; + +inline bool RedisType::isArray() const +{ + return type() == REDIS_ARRAY; +} + +inline bool RedisType::isBulkString() const +{ + return type() == REDIS_BULK_STRING; +} + +inline bool RedisType::isError() const +{ + return type() == REDIS_ERROR; +} + +inline bool RedisType::isInteger() const +{ + return type() == REDIS_INTEGER; +} + +inline bool RedisType::isSimpleString() const +{ + return type() == REDIS_SIMPLE_STRING; +} + +template +struct RedisTypeTraits +{ +}; + + +template<> +struct RedisTypeTraits +{ + enum { TypeId = RedisType::REDIS_INTEGER }; + + static const char marker = ':'; + + static std::string toString(const Int64& value) + { + return marker + NumberFormatter::format(value) + "\r\n"; + } + + static void read(RedisInputStream& input, Int64& value) + { + std::string number = input.getline(); + value = NumberParser::parse64(number); + } + +}; + + +template<> +struct RedisTypeTraits +{ + enum { TypeId = RedisType::REDIS_SIMPLE_STRING }; + + static const char marker = '+'; + + static std::string toString(const std::string& value) + { + return marker + value + LineEnding::NEWLINE_CRLF; + } + + static void read(RedisInputStream& input, std::string& value) + { + value = input.getline(); + } +}; + + +typedef Nullable BulkString; + /// A bulk string is a string that can contain a NULL value. + /// So, BulkString is a typedef for Nullable. + + +template<> +struct RedisTypeTraits +{ + enum { TypeId = RedisType::REDIS_BULK_STRING }; + + static const char marker = '$'; + + static std::string toString(const BulkString& value) + { + if ( value.isNull() ) + { + return marker + std::string("-1") + LineEnding::NEWLINE_CRLF; + } + else + { + std::string s = value.value(); + return marker + + NumberFormatter::format(s.length()) + + LineEnding::NEWLINE_CRLF + + s + + LineEnding::NEWLINE_CRLF; + } + } + + static void read(RedisInputStream& input, BulkString& value) + { + value.clear(); + + std::string line = input.getline(); + int length = NumberParser::parse(line); + + if ( length >= 0 ) + { + std::string s; + s.resize(length, ' '); + input.read(&*s.begin(), length); + value.assign(s); + + input.getline(); // Read and ignore /r/n + } + } +}; + + +template +class Type : public RedisType + /// Template class for all Redis types. This class will use + /// RedisTypeTraits structure for calling the type specific code. +{ +public: + + Type() + /// Constructor + { + } + + Type(const T& t) : _value(t) + /// Constructor + { + } + + Type(const Type& copy) : _value(copy._value) + /// Copy Constructor + { + } + + virtual ~Type() + /// Destructor + { + } + + int type() const + /// Returns the type of the value + { + return RedisTypeTraits::TypeId; + } + + void read(RedisInputStream& socket) + /// Reads the value from the stream (RESP). + { + RedisTypeTraits::read(socket, _value); + } + + std::string toString() const + /// Converts the value to a string based on the RESP protocol. + { + return RedisTypeTraits::toString(_value); + } + + T& value() + /// Returns the value + { + return _value; + } + + const T& value() const + /// Returns a const value + { + return _value; + } + +private: + + T _value; +}; + +}} // Namespace Poco/Redis + +#endif // Redis_Type_INCLUDED diff --git a/Redis/src/Array.cpp b/Redis/src/Array.cpp new file mode 100644 index 000000000..961ba3059 --- /dev/null +++ b/Redis/src/Array.cpp @@ -0,0 +1,65 @@ +// +// Array.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Array +// +// Implementation of the Array class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/Array.h" + +namespace Poco { +namespace Redis { + + +Array::Array() +{ +} + + +Array::Array(const Array& copy) : _elements(copy._elements) +{ +} + + +Array::~Array() +{ +} + + +Array& Array::addRedisType(RedisType::Ptr value) +{ + checkNull(); + + _elements.value().push_back(value); + + return *this; +} + + +int Array::getType(size_t pos) const +{ + if ( _elements.isNull() ) throw NullValueException(); + + if ( pos >= _elements.value().size() ) throw InvalidArgumentException(); + + RedisType::Ptr element = _elements.value().at(pos); + return element->type(); +} + + +std::string Array::toString() const +{ + return RedisTypeTraits::toString(*this); +} + +} } diff --git a/Redis/src/AsyncReader.cpp b/Redis/src/AsyncReader.cpp new file mode 100644 index 000000000..41a3ebcba --- /dev/null +++ b/Redis/src/AsyncReader.cpp @@ -0,0 +1,60 @@ +// +// AsyncReader.cpp +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: AsyncReader +// +// Implementation of the AsyncReader class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/AsyncReader.h" + +namespace Poco { +namespace Redis { + + +AsyncReader::AsyncReader(Client& client) : _client(client), + _activity(this, &AsyncReader::runActivity) +{ +} + + +AsyncReader::~AsyncReader() +{ + stop(); +} + + +void AsyncReader::runActivity() +{ + while(!_activity.isStopped()) + { + try + { + RedisType::Ptr reply = _client.readReply(); + + RedisEventArgs args(reply); + redisResponse.notify(this, args); + + if ( args.isStopped() ) stop(); + } + catch(Exception& e) + { + RedisEventArgs args(&e); + redisException.notify(this, args); + stop(); + } + if (!_activity.isStopped()) Thread::trySleep(100); + } +} + + +} } // Poco::Redis diff --git a/Redis/src/Client.cpp b/Redis/src/Client.cpp new file mode 100644 index 000000000..80a6c6222 --- /dev/null +++ b/Redis/src/Client.cpp @@ -0,0 +1,176 @@ +// +// Client.cpp +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Client +// +// Implementation of the Client class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/Client.h" +#include "Poco/Redis/Exception.h" + +namespace Poco { +namespace Redis { + + +Client::Client() : _address(), _socket(), _input(0), _output(0) +{ +} + + +Client::Client(const std::string& hostAndPort) : _address(hostAndPort), _socket(), _input(0), _output(0) +{ + connect(); +} + + +Client::Client(const std::string& host, int port) : _address(host, port), _socket(), _input(0), _output(0) +{ + connect(); +} + + +Client::Client(const Net::SocketAddress& addrs) : _address(addrs), _socket(), _input(0), _output(0) +{ + connect(); +} + + +Client::~Client() +{ + delete _input; + delete _output; +} + + +void Client::connect() +{ + poco_assert(! _input); + poco_assert(! _output); + + _socket.connect(_address); + _input = new RedisInputStream(_socket); + _output = new RedisOutputStream(_socket); +} + +void Client::connect(const std::string& hostAndPort) +{ + _address = Net::SocketAddress(hostAndPort); + connect(); +} + + +void Client::connect(const std::string& host, int port) +{ + _address = Net::SocketAddress(host, port); + connect(); +} + + +void Client::connect(const Net::SocketAddress& addrs) +{ + _address = addrs; + connect(); +} + +void Client::connect(const Timespan& timeout) +{ + poco_assert(! _input); + poco_assert(! _output); + + _socket.connect(_address, timeout); + _input = new RedisInputStream(_socket); + _output = new RedisOutputStream(_socket); +} + +void Client::connect(const std::string& hostAndPort, const Timespan& timeout) +{ + _address = Net::SocketAddress(hostAndPort); + connect(timeout); +} + + +void Client::connect(const std::string& host, int port, const Timespan& timeout) +{ + _address = Net::SocketAddress(host, port); + connect(timeout); +} + + +void Client::connect(const Net::SocketAddress& addrs, const Timespan& timeout) +{ + _address = addrs; + connect(timeout); +} + +void Client::disconnect() +{ + delete _input; + _input = 0; + + delete _output; + _output = 0; + + _socket.close(); +} + +void Client::writeCommand(const Array& command, bool flush) +{ + poco_assert(_output); + + std::string commandStr = command.toString(); + + _output->write(commandStr.c_str(), commandStr.length()); + if ( flush ) _output->flush(); +} + +RedisType::Ptr Client::readReply() +{ + poco_assert(_input); + + int c = _input->get(); + RedisType::Ptr result = RedisType::createRedisType(c); + if ( result.isNull() ) + { + throw RedisException("Invalid Redis type returned"); + } + + result->read(*_input); + + return result; +} + +RedisType::Ptr Client::sendCommand(const Array& command) +{ + writeCommand(command, true); + return readReply(); +} + +Array Client::sendCommands(const std::vector& commands) +{ + Array results; + + for(std::vector::const_iterator it = commands.begin(); it != commands.end(); ++it) + { + writeCommand(*it, false); + } + _output->flush(); + + for(int i = 0; i < commands.size(); ++i) + { + results.addRedisType(readReply()); + } + + return results; +} + +} } // Poco::Redis diff --git a/Redis/src/Command.cpp b/Redis/src/Command.cpp new file mode 100644 index 000000000..0705b3c48 --- /dev/null +++ b/Redis/src/Command.cpp @@ -0,0 +1,620 @@ +// +// Command.cpp +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Command +// +// Implementation of the Command class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/Command.h" +#include "Poco/NumberFormatter.h" + +namespace Poco { +namespace Redis { + +Command::Command(const std::string& command) : Array() +{ + add(command); +} + +Command::Command(const Command& copy) : Array(copy) +{ +} + +Command::~Command() +{ +} + +Command Command::append(const std::string& key, const std::string& value) +{ + Command cmd("APPEND"); + + cmd << key << value; + + return cmd; +} + +Command Command::blpop(const StringVec& lists, Int64 timeout) +{ + Command cmd("BLPOP"); + + cmd << lists << NumberFormatter::format(timeout); + + return cmd; +} + +Command Command::brpop(const StringVec& lists, Int64 timeout) +{ + Command cmd("BRPOP"); + + cmd << lists << NumberFormatter::format(timeout); + + return cmd; +} + +Command Command::brpoplpush(const std::string& sourceList, const std::string& destinationList, Int64 timeout) +{ + Command cmd("BRPOPLPUSH"); + + cmd << sourceList << destinationList << NumberFormatter::format(timeout); + + return cmd; +} + +Command Command::decr(const std::string& key, Int64 by) +{ + Command cmd(by == 0 ? "DECR" : "DECRBY"); + + cmd << key; + if ( by > 0 ) cmd << NumberFormatter::format(by); + + return cmd; +} + +Command Command::del(const std::string& key) +{ + Command cmd("DEL"); + + cmd << key; + + return cmd; +} + +Command Command::del(const StringVec& keys) +{ + Command cmd("DEL"); + + cmd << keys; + + return cmd; +} + +Command Command::get(const std::string& key) +{ + Command cmd("GET"); + + cmd << key; + + return cmd; +} + +Command Command::hdel(const std::string& hash, const std::string& field) +{ + Command cmd("HDEL"); + + cmd << hash << field; + + return cmd; +} + +Command Command::hdel(const std::string& hash, const StringVec& fields) +{ + Command cmd("HDEL"); + + cmd << hash << fields; + + return cmd; +} + +Command Command::hexists(const std::string& hash, const std::string& field) +{ + Command cmd("HEXISTS"); + + cmd << hash << field; + + return cmd; +} + +Command Command::hget(const std::string& hash, const std::string& field) +{ + Command cmd("HGET"); + + cmd << hash << field; + + return cmd; +} + +Command Command::hgetall(const std::string& hash) +{ + Command cmd("HGETALL"); + + cmd << hash; + + return cmd; +} + +Command Command::hincrby(const std::string& hash, const std::string& field, Int64 by) +{ + Command cmd("HINCRBY"); + + cmd << hash << field << NumberFormatter::format(by); + + return cmd; +} + +Command Command::hkeys(const std::string& hash) +{ + Command cmd("HKEYS"); + + cmd << hash; + + return cmd; +} + +Command Command::hlen(const std::string& hash) +{ + Command cmd("HLEN"); + + cmd << hash; + + return cmd; +} + +Command Command::hmget(const std::string& hash, const StringVec& fields) +{ + Command cmd("HMGET"); + + cmd << hash << fields; + + return cmd; +} + +Command Command::hmset(const std::string& hash, std::map& fields) +{ + Command cmd("HMSET"); + + cmd << hash; + for(std::map::const_iterator it = fields.begin(); it != fields.end(); ++it) + { + cmd << it->first << it->second; + } + + return cmd; +} + +Command Command::hset(const std::string& hash, const std::string& field, const std::string& value, bool create) +{ + Command cmd(create ? "HSET" : "HSETNX"); + + cmd << hash << field << value; + + return cmd; +} + +Command Command::hset(const std::string& hash, const std::string& field, Int64 value, bool create) +{ + return hset(hash, field, NumberFormatter::format(value), create); +} + +Command Command::hstrlen(const std::string& hash, const std::string& field) +{ + Command cmd("HSTRLEN"); + + cmd << hash << field; + + return cmd; +} + +Command Command::hvals(const std::string& hash) +{ + Command cmd("HVALS"); + + cmd << hash; + + return cmd; +} + +Command Command::incr(const std::string& key, Int64 by) +{ + Command cmd(by == 0 ? "INCR" : "INCRBY"); + + cmd << key; + if ( by > 0 ) cmd << NumberFormatter::format(by); + + return cmd; +} + +Command Command::lindex(const std::string& list, Int64 index) +{ + Command cmd("LINDEX"); + + cmd << list << NumberFormatter::format(index); + + return cmd; +} + +Command Command::linsert(const std::string& list, bool before, const std::string& reference, const std::string& value) +{ + Command cmd("LINSERT"); + + cmd << list << (before ? "BEFORE" : "AFTER") << reference << value; + return cmd; +} + +Command Command::llen(const std::string& list) +{ + Command cmd("LLEN"); + + cmd << list; + + return cmd; +} + +Command Command::lpop(const std::string& list) +{ + Command cmd("LPOP"); + + cmd << list; + + return cmd; +} + +Command Command::lpush(const std::string& list, const std::string& value, bool create) +{ + Command cmd(create ? "LPUSH" : "LPUSHX"); + + cmd << list << value; + + return cmd; +} + +Command Command::lpush(const std::string& list, const StringVec& values, bool create) +{ + Command cmd(create ? "LPUSH" : "LPUSHX"); + + cmd << list << values; + + return cmd; +} + +Command Command::lrange(const std::string& list, Int64 start, Int64 stop) +{ + Command cmd("LRANGE"); + + cmd << list << NumberFormatter::format(start) << NumberFormatter::format(stop); + + return cmd; +} + +Command Command::lrem(const std::string& list, Int64 count, const std::string& value) +{ + Command cmd("LREM"); + + cmd << list << NumberFormatter::format(count) << value; + + return cmd; +} + +Command Command::lset(const std::string& list, Int64 index, const std::string& value) +{ + Command cmd("LSET"); + + cmd << list << NumberFormatter::format(index) << value; + + return cmd; +} + +Command Command::ltrim(const std::string& list, Int64 start, Int64 stop) +{ + Command cmd("LTRIM"); + + cmd << list << NumberFormatter::format(start) << NumberFormatter::format(stop); + + return cmd; +} + +Command Command::mget(const StringVec& keys) +{ + Command cmd("MGET"); + + cmd << keys; + + return cmd; +} + +Command Command::mset(const std::map& keyvalues, bool create) +{ + Command cmd(create ? "MSET" : "MSETNX"); + + for(std::map::const_iterator it = keyvalues.begin(); it != keyvalues.end(); ++it) + { + cmd << it->first << it->second; + } + + return cmd; +} + +Command Command::sadd(const std::string& set, const std::string& value) +{ + Command cmd("SADD"); + + cmd << set << value; + + return cmd; +} + +Command Command::sadd(const std::string& set, const StringVec& values) +{ + Command cmd("SADD"); + + cmd << set << values; + + return cmd; +} + +Command Command::scard(const std::string& set) +{ + Command cmd("SCARD"); + + cmd << set; + + return cmd; +} + +Command Command::sdiff(const std::string& set1, const std::string& set2) +{ + Command cmd("SDIFF"); + + cmd << set1 << set2; + + return cmd; +} + +Command Command::sdiff(const std::string& set, const StringVec& sets) +{ + Command cmd("SDIFF"); + + cmd << set << sets; + + return cmd; +} + +Command Command::sdiffstore(const std::string& set, const std::string& set1, const std::string& set2) +{ + Command cmd("SDIFFSTORE"); + + cmd << set << set1 << set2; + + return cmd; +} + +Command Command::sdiffstore(const std::string& set, const StringVec& sets) +{ + Command cmd("SDIFFSTORE"); + + cmd << set << sets; + + return cmd; +} + +Command Command::set(const std::string& key, const std::string& value, bool overwrite, const Poco::Timespan& expireTime, bool create) +{ + Command cmd("SET"); + + cmd << key << value; + if (! overwrite) cmd << "NX"; + if (! create) cmd << "XX"; + if (expireTime.totalMicroseconds() > 0) cmd << "PX" << expireTime.totalMilliseconds(); + + return cmd; +} + +Command Command::set(const std::string& key, Int64 value, bool overwrite, const Poco::Timespan& expireTime, bool create) +{ + return set(key, NumberFormatter::format(value), overwrite, expireTime, create); +} + +Command Command::sinter(const std::string& set1, const std::string& set2) +{ + Command cmd("SINTER"); + + cmd << set1 << set2; + + return cmd; +} + +Command Command::sinter(const std::string& set, const StringVec& sets) +{ + Command cmd("SINTER"); + + cmd << set << sets; + + return cmd; +} + +Command Command::sinterstore(const std::string& set, const std::string& set1, const std::string& set2) +{ + Command cmd("SINTERSTORE"); + + cmd << set << set1 << set2; + + return cmd; +} + +Command Command::sinterstore(const std::string& set, const StringVec& sets) +{ + Command cmd("SINTERSTORE"); + + cmd << set << sets; + + return cmd; +} + +Command Command::sismember(const std::string& set, const std::string& member) +{ + Command cmd("SISMEMBER"); + + cmd << set << member; + + return cmd; +} + +Command Command::smembers(const std::string& set) +{ + Command cmd("SMEMBERS"); + + cmd << set; + + return cmd; +} + +Command Command::smove(const std::string& source, const std::string& destination, const std::string& member) +{ + Command cmd("SMOVE"); + + cmd << source << destination << member; + + return cmd; +} + +Command Command::spop(const std::string& set, Int64 count) +{ + Command cmd("SPOP"); + + cmd << set; + if( count != 0 ) cmd << NumberFormatter::format(count); + + return cmd; +} + +Command Command::srandmember(const std::string& set, Int64 count) +{ + Command cmd("SRANDMEMBER"); + + cmd << set; + if( count != 0 ) cmd << NumberFormatter::format(count); + + return cmd; +} + +Command Command::srem(const std::string& set1, const std::string& member) +{ + Command cmd("SREM"); + + cmd << set1 << member; + + return cmd; +} + +Command Command::srem(const std::string& set, const StringVec& members) +{ + Command cmd("SREM"); + + cmd << set << members; + + return cmd; +} + +Command Command::sunion(const std::string& set1, const std::string& set2) +{ + Command cmd("SUNION"); + + cmd << set1 << set2; + + return cmd; +} + +Command Command::sunion(const std::string& set, const StringVec& sets) +{ + Command cmd("SUNION"); + + cmd << set << sets; + + return cmd; +} + +Command Command::sunionstore(const std::string& set, const std::string& set1, const std::string& set2) +{ + Command cmd("SUNIONSTORE"); + + cmd << set << set1 << set2; + + return cmd; +} + +Command Command::sunionstore(const std::string& set, const StringVec& sets) +{ + Command cmd("SUNIONSTORE"); + + cmd << set << sets; + + return cmd; +} + +Command Command::rename(const std::string& key, const std::string& newName, bool overwrite) +{ + Command cmd(overwrite ? "RENAME" : "RENAMENX"); + + cmd << key << newName; + + return cmd; +} + + +Command Command::rpop(const std::string& list) +{ + Command cmd("RPOP"); + + cmd << list; + + return cmd; +} + +Command Command::rpoplpush(const std::string& sourceList, const std::string& destinationList) +{ + Command cmd("RPOPLPUSH"); + + cmd << sourceList << destinationList; + + return cmd; +} + +Command Command::rpush(const std::string& list, const std::string& value, bool create) +{ + Command cmd(create ? "RPUSH" : "RPUSHX"); + + cmd << list << value; + + return cmd; +} + +Command Command::rpush(const std::string& list, const StringVec& values, bool create) +{ + Command cmd(create ? "RPUSH" : "RPUSHX"); + + cmd << list << values; + + return cmd; +} + + +}} // Poco::Redis diff --git a/Redis/src/Error.cpp b/Redis/src/Error.cpp new file mode 100644 index 000000000..51c9f7a40 --- /dev/null +++ b/Redis/src/Error.cpp @@ -0,0 +1,35 @@ +// +// Error.cpp +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Error +// +// Implementation of the Error class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/Error.h" + +namespace Poco { +namespace Redis { + +Error::Error() +{ +} + +Error::Error(const std::string& message) : _message(message) +{ +} + +Error::~Error() +{ +} + +} } \ No newline at end of file diff --git a/Redis/src/Exception.cpp b/Redis/src/Exception.cpp new file mode 100644 index 000000000..c27e5743c --- /dev/null +++ b/Redis/src/Exception.cpp @@ -0,0 +1,25 @@ +// +// Exception.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Exception +// +// Implementation of the Exception class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/Exception.h" + +namespace Poco { +namespace Redis { + +POCO_IMPLEMENT_EXCEPTION(RedisException, Exception, "Redis Exception") + +}} diff --git a/Redis/src/RedisEventArgs.cpp b/Redis/src/RedisEventArgs.cpp new file mode 100644 index 000000000..4a7cd7e18 --- /dev/null +++ b/Redis/src/RedisEventArgs.cpp @@ -0,0 +1,43 @@ +// +// RedisEventArgs.cpp +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: RedisEventArgs +// +// Implementation of the RedisEventArgs class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/RedisEventArgs.h" + +namespace Poco { +namespace Redis { + +RedisEventArgs::RedisEventArgs(RedisType::Ptr message) : + _message(message), + _exception(0), + _stop(false) +{ +} + +RedisEventArgs::RedisEventArgs(Exception* exception) : + _message(), + _exception(exception ? exception->clone() : 0), + _stop(false) +{ +} + +RedisEventArgs::~RedisEventArgs() +{ + delete _exception; +} + + +}} // namespace Poco::Redis \ No newline at end of file diff --git a/Redis/src/RedisStream.cpp b/Redis/src/RedisStream.cpp new file mode 100644 index 000000000..b794bb210 --- /dev/null +++ b/Redis/src/RedisStream.cpp @@ -0,0 +1,108 @@ +// +// RedisStream.cpp +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: RedisStream +// +// Implementation of the RedisStream class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// +#include +#include "Poco/Redis/RedisStream.h" + +namespace Poco { +namespace Redis { + + +RedisStreamBuf::RedisStreamBuf(Net::StreamSocket& redis): + BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in | std::ios::out), + _redis(redis) +{ +} + + +RedisStreamBuf::~RedisStreamBuf() +{ +} + + +int RedisStreamBuf::readFromDevice(char* buffer, std::streamsize len) +{ + return _redis.receiveBytes(buffer, len); +} + + +int RedisStreamBuf::writeToDevice(const char* buffer, std::streamsize length) +{ + return _redis.sendBytes(buffer, length); +} + +RedisIOS::RedisIOS(Net::StreamSocket& redis): + _buf(redis) +{ + poco_ios_init(&_buf); +} + + +RedisIOS::~RedisIOS() +{ + try + { + _buf.sync(); + } + catch (...) + { + } +} + + +RedisStreamBuf* RedisIOS::rdbuf() +{ + return &_buf; +} + + +void RedisIOS::close() +{ + _buf.sync(); +} + +RedisOutputStream::RedisOutputStream(Net::StreamSocket& redis): + RedisIOS(redis), + std::ostream(&_buf) +{ +} + + +RedisOutputStream::~RedisOutputStream() +{ +} + +RedisInputStream::RedisInputStream(Net::StreamSocket& redis): + RedisIOS(redis), + std::istream(&_buf) +{ +} + + +RedisInputStream::~RedisInputStream() +{ +} + +std::string RedisInputStream::getline() +{ + std::string line; + std::getline(*this, line); + if ( line.size() > 0 ) line.erase(line.end() - 1); + return line; +} + + +}} \ No newline at end of file diff --git a/Redis/src/Type.cpp b/Redis/src/Type.cpp new file mode 100644 index 000000000..68ce05a90 --- /dev/null +++ b/Redis/src/Type.cpp @@ -0,0 +1,61 @@ +// +// Type.h +// +// $Id$ +// +// Library: Redis +// Package: Redis +// Module: Type +// +// Implementation of the Type class. +// +// Copyright (c) 2012, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + +#include "Poco/Redis/Type.h" +#include "Poco/Redis/Error.h" +#include "Poco/Redis/Array.h" + +namespace Poco { +namespace Redis { + + +RedisType::RedisType() +{ +} + +RedisType::~RedisType() +{ +} + + +RedisType::Ptr RedisType::createRedisType(char marker) +{ + RedisType::Ptr result; + + switch(marker) + { + case RedisTypeTraits::marker : + result = new Type(); + break; + case RedisTypeTraits::marker : + result = new Type(); + break; + case RedisTypeTraits::marker : + result = new Type(); + break; + case RedisTypeTraits::marker : + result = new Type(); + break; + case RedisTypeTraits::marker : + result = new Type(); + break; + } + return result; +} + + +}} \ No newline at end of file diff --git a/Redis/testsuite/CMakeLists.txt b/Redis/testsuite/CMakeLists.txt new file mode 100644 index 000000000..2cd26aefe --- /dev/null +++ b/Redis/testsuite/CMakeLists.txt @@ -0,0 +1,23 @@ +set(TESTUNIT "${LIBNAME}-testrunner") + +# Sources +file(GLOB SRCS_G "src/*.cpp") +POCO_SOURCES_AUTO( TEST_SRCS ${SRCS_G}) + +# Headers +file(GLOB_RECURSE HDRS_G "src/*.h" ) +POCO_HEADERS_AUTO( TEST_SRCS ${HDRS_G}) + +POCO_SOURCES_AUTO_PLAT( TEST_SRCS OFF + src/WinDriver.cpp +) + +POCO_SOURCES_AUTO_PLAT( TEST_SRCS WINCE + src/WinCEDriver.cpp +) + +set(TESTUNIT "${LIBNAME}-testrunner") + +add_executable( ${TESTUNIT} ${TEST_SRCS} ) +add_test(NAME ${LIBNAME} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${TESTUNIT} -all) +target_link_libraries( ${TESTUNIT} PocoRedis PocoFoundation CppUnit ) diff --git a/Redis/testsuite/Makefile b/Redis/testsuite/Makefile new file mode 100644 index 000000000..e3760770d --- /dev/null +++ b/Redis/testsuite/Makefile @@ -0,0 +1,17 @@ +# +# Makefile +# +# $Id$ +# +# Makefile for Poco Redis testsuite +# + +include $(POCO_BASE)/build/rules/global + +objects = Driver RedisTest RedisTestSuite + +target = testrunner +target_version = 1 +target_libs = PocoRedis PocoFoundation PocoNet CppUnit + +include $(POCO_BASE)/build/rules/exec diff --git a/Redis/testsuite/TestSuite.progen b/Redis/testsuite/TestSuite.progen new file mode 100644 index 000000000..d55b05017 --- /dev/null +++ b/Redis/testsuite/TestSuite.progen @@ -0,0 +1,9 @@ +vc.project.guid = ${vc.project.guidFromName} +vc.project.name = TestSuite +vc.project.target = TestSuite +vc.project.type = testsuite +vc.project.pocobase = ..\\.. +vc.project.platforms = Win32, x64 +vc.project.configurations = debug_shared, release_shared, debug_static_mt, release_static_mt, debug_static_md, release_static_md +vc.project.prototype = TestSuite_vs90.vcproj +vc.project.compiler.include = ..\\..\\Foundation\\include;..\\..\\Net\\include diff --git a/Redis/testsuite/TestSuite_x64_vs90.vcproj b/Redis/testsuite/TestSuite_x64_vs90.vcproj new file mode 100644 index 000000000..8cc28cca7 --- /dev/null +++ b/Redis/testsuite/TestSuite_x64_vs90.vcproj @@ -0,0 +1,464 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Redis/testsuite/src/Driver.cpp b/Redis/testsuite/src/Driver.cpp new file mode 100644 index 000000000..a3190c5cb --- /dev/null +++ b/Redis/testsuite/src/Driver.cpp @@ -0,0 +1,19 @@ +// +// Driver.cpp +// +// $Id$ +// +// Console-based test driver for Poco MongoDB. +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "CppUnit/TestRunner.h" +#include "RedisTestSuite.h" + + +CppUnitMain(RedisTestSuite) diff --git a/Redis/testsuite/src/RedisTest.cpp b/Redis/testsuite/src/RedisTest.cpp new file mode 100644 index 000000000..e4c61b533 --- /dev/null +++ b/Redis/testsuite/src/RedisTest.cpp @@ -0,0 +1,2958 @@ +// +// RedisTest.cpp +// +// $Id$ +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// +#include + +#include "Poco/Exception.h" +#include "Poco/Delegate.h" +#include "Poco/Thread.h" + +#include "RedisTest.h" +#include "Poco/Redis/AsyncReader.h" +#include "Poco/Redis/Command.h" +#include "Poco/Redis/PoolableConnectionFactory.h" + +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" + +using namespace Poco::Redis; + + +bool RedisTest::_connected = false; +Poco::Redis::Client RedisTest::_redis; + + +RedisTest::RedisTest(const std::string& name): + CppUnit::TestCase("Redis"), + _host("localhost"), + _port(6379) +{ + if (!_connected) + { + try + { + Poco::Timespan t(10, 0); // Connect within 10 seconds + _redis.connect(_host, _port, t); + _connected = true; + std::cout << "Connected to [" << _host << ':' << _port << ']' << std::endl; + } + catch (Poco::Exception& e) + { + std::cout << "Couldn't connect to [" << _host << ':' << _port << ']' << e.message() << ". " << std::endl; + } + } +} + + +RedisTest::~RedisTest() +{ + if (_connected) + { + _redis.disconnect(); + _connected = false; + std::cout << "Disconnected from [" << _host << ':' << _port << ']' << std::endl; + } +} + + +void RedisTest::setUp() +{ + +} + + +void RedisTest::tearDown() +{ +} + + +void RedisTest::testAPPEND() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("mykey"); + + Command setCommand = Command::set("mykey", "Hello"); + try + { + std::string result = _redis.execute(setCommand); + assert(result.compare("OK") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + Command appendCommand = Command::append("mykey", " World"); + try + { + Poco::Int64 result = _redis.execute(appendCommand); + assert(result == 11); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + Command getCommand = Command::get("mykey"); + try + { + BulkString result = _redis.execute(getCommand); + assert(result.value().compare("Hello World") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } +} + +void RedisTest::testBLPOP() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the lists are not there yet ... + std::vector lists; + lists.push_back("list1"); + lists.push_back("list2"); + Command delCommand = Command::del(lists); + try + { + _redis.execute(delCommand); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + std::vector values; + values.push_back("a"); + values.push_back("b"); + values.push_back("c"); + + try + { + Command rpush = Command::rpush("list1", values); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command blpop = Command::blpop(lists); + try + { + Array result = _redis.execute(blpop); + assert(result.size() == 2); + assert(result.get(0).value().compare("list1") == 0); + assert(result.get(1).value().compare("a") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testBRPOP() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the lists are not there yet ... + std::vector lists; + lists.push_back("list1"); + lists.push_back("list2"); + Command delCommand = Command::del(lists); + try + { + _redis.execute(delCommand); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + std::vector values; + values.push_back("a"); + values.push_back("b"); + values.push_back("c"); + + try + { + Command rpush = Command::rpush("list1", values); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command brpop = Command::brpop(lists); + try + { + Array result = _redis.execute(brpop); + assert(result.size() == 2); + assert(result.get(0).value().compare("list1") == 0); + assert(result.get(1).value().compare("c") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testDECR() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command set = Command::set("mykey", 10); + try + { + std::string result = _redis.execute(set); + assert(result.compare("OK") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + + Command decr = Command::decr("mykey"); + try + { + Poco::Int64 result = _redis.execute(decr); + assert(result == 9); + } + catch(RedisException& e) + { + fail(e.message()); + } + + set = Command::set("mykey", "234293482390480948029348230948"); + try + { + std::string result = _redis.execute(set); + assert(result.compare("OK") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + + try + { + Poco::Int64 result = _redis.execute(decr); + fail("This must fail"); + } + catch(RedisException& e) + { + // ERR value is not an integer or out of range + } + +} + +void RedisTest::testECHO() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Array command; + command.add("ECHO") + .add("Hello World"); + + try + { + BulkString result = _redis.execute(command); + assert(!result.isNull()); + assert(result.value().compare("Hello World") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testError() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Array command; + command.add("Wrong Command"); + + try + { + BulkString result = _redis.execute(command); + fail("Invalid command must throw RedisException"); + } + catch(RedisException &e) + { + // Must fail + } +} + +void RedisTest::testEVAL() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command cmd("EVAL"); + cmd << "return {1, 2, {3, 'Hello World!'}}" << Poco::NumberFormatter::format(0); + + try + { + Array value = _redis.execute(cmd); + assert(value.size() == 3); + + Poco::Int64 i = value.get(0); + assert(i == 1); + i = value.get(1); + assert(i == 2); + + Array a = value.get(2); + assert(a.size() == 2); + i = a.get(0); + assert(i == 3); + BulkString s = a.get(1); + assert(s.value().compare("Hello World!") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + +} + +void RedisTest::testHDEL() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field1", "foo"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hdel = Command::hdel("myhash", "field1"); + try + { + Poco::Int64 result = _redis.execute(hdel); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hdel = Command::hdel("myhash", "field2"); + try + { + Poco::Int64 result = _redis.execute(hdel); + assert(result == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHEXISTS() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field1", "foo"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hexists = Command::hexists("myhash", "field1"); + try + { + Poco::Int64 result = _redis.execute(hexists); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hexists = Command::hexists("myhash", "field2"); + try + { + Poco::Int64 result = _redis.execute(hexists); + assert(result == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHGETALL() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field1", "Hello"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hset = Command::hset("myhash", "field2", "World"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hgetall = Command::hgetall("myhash"); + try + { + Array result = _redis.execute(hgetall); + assert(result.size() == 4); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHINCRBY() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field", 5); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hincrby = Command::hincrby("myhash", "field"); + try + { + Poco::Int64 n = _redis.execute(hincrby); + assert(n == 6); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hincrby = Command::hincrby("myhash", "field", -1); + try + { + Poco::Int64 n = _redis.execute(hincrby); + assert(n == 5); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hincrby = Command::hincrby("myhash", "field", -10); + try + { + Poco::Int64 n = _redis.execute(hincrby); + assert(n == -5); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHKEYS() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field1", "Hello"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hset = Command::hset("myhash", "field2", "World"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hlen = Command::hlen("myhash"); + try + { + Poco::Int64 value = _redis.execute(hlen); + assert(value == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hkeys = Command::hkeys("myhash"); + try + { + Array result = _redis.execute(hkeys); + assert(result.size() == 2); + assert(result.get(0).value().compare("field1") == 0); + assert(result.get(1).value().compare("field2") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHMGET() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field1", "Hello"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hset = Command::hset("myhash", "field2", "World"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector fields; + fields.push_back("field1"); + fields.push_back("field2"); + fields.push_back("field3"); + Command hmget = Command::hmget("myhash", fields); + try + { + Array result = _redis.execute(hmget); + assert(result.size() == 3); + + assert(result.get(0).value().compare("Hello") == 0); + assert(result.get(1).value().compare("World") == 0); + assert(result.get(2).isNull()); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHSET() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + Command hset = Command::hset("myhash", "field1", "Hello"); + try + { + Poco::Int64 value = _redis.execute(hset); + assert(value == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hget = Command::hget("myhash", "field1"); + try + { + BulkString s = _redis.execute(hget); + assert(s.value().compare("Hello") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHMSET() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + std::map fields; + fields.insert(std::make_pair("field1", "Hello")); + fields.insert(std::make_pair("field2", "World")); + + Command hmset = Command::hmset("myhash", fields); + try + { + std::string result = _redis.execute(hmset); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hget = Command::hget("myhash", "field1"); + try + { + BulkString s = _redis.execute(hget); + assert(s.value().compare("Hello") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hget = Command::hget("myhash", "field2"); + try + { + BulkString s = _redis.execute(hget); + assert(s.value().compare("World") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + +} + +void RedisTest::testHSTRLEN() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + std::map fields; + fields.insert(std::make_pair("f1", "HelloWorld")); + fields.insert(std::make_pair("f2", "99")); + fields.insert(std::make_pair("f3", "-256")); + + Command hmset = Command::hmset("myhash", fields); + try + { + std::string result = _redis.execute(hmset); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hstrlen = Command::hstrlen("myhash", "f1"); + try + { + Poco::Int64 len = _redis.execute(hstrlen); + assert(len == 10); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hstrlen = Command::hstrlen("myhash", "f2"); + try + { + Poco::Int64 len = _redis.execute(hstrlen); + assert(len == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + hstrlen = Command::hstrlen("myhash", "f3"); + try + { + Poco::Int64 len = _redis.execute(hstrlen); + assert(len == 4); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testHVALS() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myhash"); + + std::map fields; + fields.insert(std::make_pair("field1", "Hello")); + fields.insert(std::make_pair("field2", "World")); + + Command hmset = Command::hmset("myhash", fields); + try + { + std::string result = _redis.execute(hmset); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command hvals = Command::hvals("myhash"); + try + { + Array result = _redis.execute(hvals); + assert(result.size() == 2); + assert(result.get(0).value().compare("Hello") == 0); + assert(result.get(1).value().compare("World") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testINCR() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command command = Command::set("mykey", "10"); + // A set responds with a simple OK string + try + { + std::string result = _redis.execute(command); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + command = Command::incr("mykey"); + try + { + Poco::Int64 value = _redis.execute(command); + assert(value == 11); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testINCRBY() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command command = Command::set("mykey", "10"); + // A set responds with a simple OK string + try + { + std::string result = _redis.execute(command); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + command = Command::incr("mykey", 5); + try + { + Poco::Int64 value = _redis.execute(command); + assert(value == 15); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testPING() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Array command; + command.add("PING"); + + // A PING without a custom strings, responds with a simple "PONG" string + try + { + std::string result = _redis.execute(command); + assert(result.compare("PONG") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + // A PING with a custom string responds with a bulk string + command.add("Hello"); + try + { + BulkString result = _redis.execute(command); + assert(!result.isNull()); + assert(result.value().compare("Hello") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + +} + +void RedisTest::testLPOP() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command rpush = Command::rpush("mylist", "one"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "two"); + result = _redis.execute(rpush); + assert(result == 2); + + rpush = Command::rpush("mylist", "three"); + result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lpop = Command::lpop("mylist"); + try + { + BulkString result = _redis.execute(lpop); + assert(result.value().compare("one") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 2); + assert(result.get(0).value().compare("two") == 0); + assert(result.get(1).value().compare("three") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } + +} + +void RedisTest::testLSET() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command rpush = Command::rpush("mylist", "one"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "two"); + result = _redis.execute(rpush); + assert(result == 2); + + rpush = Command::rpush("mylist", "three"); + result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException& e) + { + fail(e.message()); + } + + Command lset = Command::lset("mylist", 0, "four"); + try + { + std::string result = _redis.execute(lset); + } + catch(RedisException& e) + { + fail(e.message()); + } + + lset = Command::lset("mylist", -2, "five"); + try + { + std::string result = _redis.execute(lset); + } + catch(RedisException& e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 3); + assert(result.get(0).value().compare("four") == 0); + assert(result.get(1).value().compare("five") == 0); + assert(result.get(2).value().compare("three") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } + +} +void RedisTest::testLINDEX() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command lpush = Command::lpush("mylist", "World"); + Poco::Int64 result = _redis.execute(lpush); + assert(result == 1); + + lpush = Command::lpush("mylist", "Hello"); + result = _redis.execute(lpush); + assert(result == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lindex = Command::lindex("mylist", 0); + try + { + BulkString result = _redis.execute(lindex); + assert(result.value().compare("Hello") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testLINSERT() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command rpush = Command::rpush("mylist", "Hello"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "World"); + result = _redis.execute(rpush); + assert(result == 2); + + Command linsert = Command::linsert("mylist", true, "World", "There"); + result = _redis.execute(linsert); + assert(result == 3); + + Command lrange = Command::lrange("mylist", 0, -1); + Array range = _redis.execute(lrange); + assert(range.size() == 3); + + assert(range.get(0).value().compare("Hello") == 0); + assert(range.get(1).value().compare("There") == 0); + assert(range.get(2).value().compare("World") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::BadCastException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } +} + +void RedisTest::testLREM() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + std::vector list; + list.push_back("hello"); + list.push_back("hello"); + list.push_back("foo"); + list.push_back("hello"); + Command rpush = Command::rpush("mylist", list); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 4); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lrem = Command::lrem("mylist", -2, "hello"); + try + { + Poco::Int64 n = _redis.execute(lrem); + assert(n == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 2); + assert(result.get(0).value().compare("hello") == 0); + assert(result.get(1).value().compare("foo") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } +} + +void RedisTest::testLTRIM() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command rpush = Command::rpush("mylist", "one"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "two"); + result = _redis.execute(rpush); + assert(result == 2); + + rpush = Command::rpush("mylist", "three"); + result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command ltrim = Command::ltrim("mylist", 1); + try + { + std::string result = _redis.execute(ltrim); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 2); + assert(result.get(0).value().compare("two") == 0); + assert(result.get(1).value().compare("three") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } + +} + +void RedisTest::testMSET() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command command("MSET"); + command << "key1" << "Hello" << "key2" << "World"; + + // A MSET responds with a simple OK string + try + { + std::string result = _redis.execute(command); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + command.clear(); + command.add("MGET") + .add("key1") + .add("key2") + .add("nonexisting"); + try + { + Array result = _redis.execute(command); + + assert(result.size() == 3); + BulkString value = result.get(0); + assert(value.value().compare("Hello") == 0); + + value = result.get(1); + assert(value.value().compare("World") == 0); + + value = result.get(2); + assert(value.isNull()); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } +} + +void RedisTest::testMSETWithMap() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + std::map keyValuePairs; + keyValuePairs.insert(std::make_pair("key1", "Hello")); + keyValuePairs.insert(std::make_pair("key2", "World")); + + Command mset = Command::mset(keyValuePairs); + + // A MSET responds with a simple OK string + try + { + std::string result = _redis.execute(mset); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector keys; + keys.push_back("key1"); + keys.push_back("key2"); + keys.push_back("nonexisting"); + + Command mget = Command::mget(keys); + try + { + Array result = _redis.execute(mget); + + assert(result.size() == 3); + BulkString value = result.get(0); + assert(value.value().compare("Hello") == 0); + + value = result.get(1); + assert(value.value().compare("World") == 0); + + value = result.get(2); + assert(value.isNull()); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } +} + +void RedisTest::testMULTI() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure keys are gone from a previous testrun ... + delKey("foo"); + delKey("bar"); + + Array command; + command.add("MULTI"); + try + { + std::string result = _redis.execute(command); + assert(result.compare("OK") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + command.clear(); + command.add("INCR") + .add("foo"); + try + { + std::string result = _redis.execute(command); + assert(result.compare("QUEUED") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + command.clear(); + command.add("INCR") + .add("bar"); + try + { + std::string result = _redis.execute(command); + assert(result.compare("QUEUED") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + command.clear(); + command.add("EXEC"); + try + { + Array result = _redis.execute(command); + assert(result.size() == 2); + + Poco::Int64 v = result.get(0); + assert(v == 1); + v = result.get(1); + assert(v == 1); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } +} + +void RedisTest::testPipeliningWithSendCommands() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + std::vector commands; + + Array ping; + ping.add("PING"); + commands.push_back(ping); + commands.push_back(ping); + + Array result = _redis.sendCommands(commands); + + // We expect 2 results + assert(result.size() == 2); + + // The 2 results must be simple PONG strings + for(size_t i = 0; i < 2; ++i) + { + try + { + std::string pong = result.get(i); + assert(pong.compare("PONG") == 0); + } + catch(...) + { + fail("An exception occurred"); + } + } +} + +void RedisTest::testPipeliningWithWriteCommand() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Array ping; + ping.add("PING"); + + _redis.execute(ping); + _redis.execute(ping); + _redis.flush(); + + // We expect 2 results with simple "PONG" strings + for(int i = 0; i < 2; ++i) + { + std::string pong; + try + { + _redis.readReply(pong); + assert(pong.compare("PONG") == 0); + } + catch(RedisException& e) + { + fail(e.message()); + } + } +} + +class RedisSubscriber +{ +public: + + void onMessage(const void* pSender, RedisEventArgs& args) + { + if ( ! args.message().isNull() ) + { + Type* arrayType = dynamic_cast*>(args.message().get()); + if ( arrayType != NULL ) + { + Array& array = arrayType->value(); + if ( array.size() == 3 ) + { + BulkString type = array.get(0); + if ( type.value().compare("unsubscribe") == 0 ) + { + Poco::Int64 n = array.get(2); + // When 0, no subscribers anymore, so stop reading ... + if ( n == 0 ) args.stop(); + } + } + else + { + // Wrong array received. Stop the reader + args.stop(); + } + } + else + { + // Invalid type of message received. Stop the reader ... + args.stop(); + } + } + } + + void onError(const void* pSender, RedisEventArgs& args) + { + std::cout << args.exception()->className() << std::endl; + // No need to call stop, AsyncReader stops automatically when an + // exception is received. + } + +}; + +void RedisTest::testPubSub() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + RedisSubscriber subscriber; + + Array subscribe; + subscribe.add("SUBSCRIBE").add("test"); + + _redis.execute(subscribe); + _redis.flush(); + + AsyncReader reader(_redis); + reader.redisResponse += Poco::delegate(&subscriber, &RedisSubscriber::onMessage); + reader.redisException += Poco::delegate(&subscriber, &RedisSubscriber::onError); + reader.start(); + + std::cout << "Sleeping ..." << std::endl; + Poco::Thread::sleep(10000); + + Array unsubscribe; + unsubscribe.add("UNSUBSCRIBE"); + + _redis.execute(unsubscribe); + _redis.flush(); +} + +void RedisTest::testSADD() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + Command sadd = Command::sadd("myset", "Hello"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "World"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "World"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSCARD() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + Command sadd = Command::sadd("myset", "Hello"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "World"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command scard = Command::scard("myset"); + try + { + Poco::Int64 result = _redis.execute(scard); + assert(result == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSDIFF() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("key1"); + delKey("key2"); + + std::vector values1; + values1.push_back("a"); + values1.push_back("b"); + values1.push_back("c"); + Command sadd = Command::sadd("key1", values1); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector values2; + values2.push_back("c"); + values2.push_back("d"); + values2.push_back("e"); + sadd = Command::sadd("key2", values2); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sdiff = Command::sdiff("key1", "key2"); + try + { + Array result = _redis.execute(sdiff); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSDIFFSTORE() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("key"); + delKey("key1"); + delKey("key2"); + + std::vector values1; + values1.push_back("a"); + values1.push_back("b"); + values1.push_back("c"); + Command sadd = Command::sadd("key1", values1); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector values2; + values2.push_back("c"); + values2.push_back("d"); + values2.push_back("e"); + sadd = Command::sadd("key2", values2); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sdiffstore = Command::sdiffstore("key", "key1", "key2"); + try + { + Poco::Int64 result = _redis.execute(sdiffstore); + assert(result == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("key"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSET() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Array command; + command.add("SET").add("mykey").add("Hello"); + + // A set responds with a simple OK string + try + { + std::string result = _redis.execute(command); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + command.add("NX"); + // A set NX responds with a Null bulk string + // when the key is already set + try + { + BulkString result = _redis.execute(command); + assert(result.isNull()); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + + +void RedisTest::testSINTER() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("key1"); + delKey("key2"); + + std::vector values1; + values1.push_back("a"); + values1.push_back("b"); + values1.push_back("c"); + Command sadd = Command::sadd("key1", values1); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector values2; + values2.push_back("c"); + values2.push_back("d"); + values2.push_back("e"); + sadd = Command::sadd("key2", values2); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sinter = Command::sinter("key1", "key2"); + try + { + Array result = _redis.execute(sinter); + assert(result.size() == 1); + assert(result.get(0).value().compare("c") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSINTERSTORE() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("key"); + delKey("key1"); + delKey("key2"); + + std::vector values1; + values1.push_back("a"); + values1.push_back("b"); + values1.push_back("c"); + Command sadd = Command::sadd("key1", values1); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector values2; + values2.push_back("c"); + values2.push_back("d"); + values2.push_back("e"); + sadd = Command::sadd("key2", values2); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sinterstore = Command::sinterstore("key", "key1", "key2"); + try + { + Poco::Int64 result = _redis.execute(sinterstore); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("key"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 1); + assert(result.get(0).value().compare("c") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSISMEMBER() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + Command sadd = Command::sadd("myset", "one"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sismember = Command::sismember("myset", "one"); + try + { + Poco::Int64 result = _redis.execute(sismember); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sismember = Command::sismember("myset", "two"); + try + { + Poco::Int64 result = _redis.execute(sismember); + assert(result == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSMEMBERS() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + Command sadd = Command::sadd("myset", "Hello"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "World"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("myset"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSMOVE() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + delKey("myotherset"); + + Command sadd = Command::sadd("myset", "one"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "two"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myotherset", "three"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smove = Command::smove("myset", "myotherset", "two"); + try + { + Poco::Int64 result = _redis.execute(smove); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("myset"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 1); + assert(result.get(0).value().compare("one") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + smembers = Command::smembers("myotherset"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSPOP() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + Command sadd = Command::sadd("myset", "one"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "two"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "three"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command spop = Command::spop("myset"); + try + { + BulkString result = _redis.execute(spop); + assert(!result.isNull()); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("myset"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "four"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + sadd = Command::sadd("myset", "five"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } +// Redis server 3.0.5 doesn't support this yet .. +/* + spop = Command::spop("myset", 3); + try + { + Array result = _redis.execute(spop); + assert(result.size() == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } +*/ +} + +void RedisTest::testSRANDMEMBER() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + std::vector members; + members.push_back("one"); + members.push_back("two"); + members.push_back("three"); + + Command sadd = Command::sadd("myset", members); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command srandmember = Command::srandmember("myset"); + try + { + BulkString result = _redis.execute(srandmember); + assert(!result.isNull()); + } + catch(RedisException &e) + { + fail(e.message()); + } + + srandmember = Command::srandmember("myset", 2); + try + { + Array result = _redis.execute(srandmember); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + srandmember = Command::srandmember("myset", -5); + try + { + Array result = _redis.execute(srandmember); + assert(result.size() == 5); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSTRLEN() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Array command; + command.add("SET").add("mykey").add("Hello World"); + + // A set responds with a simple OK string + try + { + std::string result = _redis.execute(command); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + command.clear(); + command.add("STRLEN") + .add("mykey"); + + try + { + Poco::Int64 result = _redis.execute(command); + + assert(result == 11); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSREM() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("myset"); + + Command sadd = Command::sadd("myset", "one"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + sadd = Command::sadd("myset", "two"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + sadd = Command::sadd("myset", "three"); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command srem = Command::srem("myset", "one"); + try + { + Poco::Int64 result = _redis.execute(srem); + assert(result == 1); + } + catch(RedisException &e) + { + fail(e.message()); + } + + srem = Command::srem("myset", "four"); + try + { + Poco::Int64 result = _redis.execute(srem); + assert(result == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("myset"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSUNION() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("key1"); + delKey("key2"); + + std::vector values1; + values1.push_back("a"); + values1.push_back("b"); + values1.push_back("c"); + Command sadd = Command::sadd("key1", values1); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector values2; + values2.push_back("c"); + values2.push_back("d"); + values2.push_back("e"); + sadd = Command::sadd("key2", values2); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sunion = Command::sunion("key1", "key2"); + try + { + Array result = _redis.execute(sunion); + assert(result.size() == 5); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testSUNIONSTORE() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + delKey("key"); + delKey("key1"); + delKey("key2"); + + std::vector values1; + values1.push_back("a"); + values1.push_back("b"); + values1.push_back("c"); + Command sadd = Command::sadd("key1", values1); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + std::vector values2; + values2.push_back("c"); + values2.push_back("d"); + values2.push_back("e"); + sadd = Command::sadd("key2", values2); + try + { + Poco::Int64 result = _redis.execute(sadd); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command sunionstore = Command::sunionstore("key", "key1", "key2"); + try + { + Poco::Int64 result = _redis.execute(sunionstore); + assert(result == 5); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command smembers = Command::smembers("key"); + try + { + Array result = _redis.execute(smembers); + assert(result.size() == 5); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testRENAME() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command set = Command::set("mykey", "Hello"); + try + { + std::string result = _redis.execute(set); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command rename = Command::rename("mykey", "myotherkey"); + try + { + std::string result = _redis.execute(rename); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command get = Command::get("myotherkey"); + try + { + BulkString result = _redis.execute(get); + assert(result.value().compare("Hello") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testRENAMENX() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + Command set = Command::set("mykey", "Hello"); + try + { + std::string result = _redis.execute(set); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + set = Command::set("myotherkey", "World"); + try + { + std::string result = _redis.execute(set); + assert(result.compare("OK") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command rename = Command::rename("mykey", "myotherkey", false); + try + { + Poco::Int64 result = _redis.execute(rename); + assert(result == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command get = Command::get("myotherkey"); + try + { + BulkString result = _redis.execute(get); + assert(result.value().compare("World") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } +} + +void RedisTest::testRPOP() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command rpush = Command::rpush("mylist", "one"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "two"); + result = _redis.execute(rpush); + assert(result == 2); + + rpush = Command::rpush("mylist", "three"); + result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command rpop = Command::rpop("mylist"); + try + { + BulkString result = _redis.execute(rpop); + assert(result.value().compare("three") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 2); + assert(result.get(0).value().compare("one") == 0); + assert(result.get(1).value().compare("two") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } + +} + +void RedisTest::testRPOPLPUSH() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the lists are not there yet ... + std::vector lists; + lists.push_back("mylist"); + lists.push_back("myotherlist"); + Command delCommand = Command::del(lists); + try + { + _redis.execute(delCommand); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + try + { + Command rpush = Command::rpush("mylist", "one"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "two"); + result = _redis.execute(rpush); + assert(result == 2); + + rpush = Command::rpush("mylist", "three"); + result = _redis.execute(rpush); + assert(result == 3); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command rpoplpush = Command::rpoplpush("mylist", "myotherlist"); + try + { + BulkString result = _redis.execute(rpoplpush); + assert(result.value().compare("three") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 2); + assert(result.get(0).value().compare("one") == 0); + assert(result.get(1).value().compare("two") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } + + lrange = Command::lrange("myotherlist"); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 1); + assert(result.get(0).value().compare("three") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } +} + +void RedisTest::testRPUSH() +{ + if (!_connected) + { + std::cout << "Not connected, test skipped." << std::endl; + return; + } + + // Make sure the list is not there yet ... + delKey("mylist"); + + try + { + Command rpush = Command::rpush("mylist", "World"); + Poco::Int64 result = _redis.execute(rpush); + assert(result == 1); + + rpush = Command::rpush("mylist", "Hello"); + result = _redis.execute(rpush); + assert(result == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + + Command llen = Command::llen("mylist"); + try + { + Poco::Int64 n = _redis.execute(llen); + assert(n == 2); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } + + Command lrange = Command::lrange("mylist", 0, -1); + try + { + Array result = _redis.execute(lrange); + + assert(result.size() == 2); + assert(result.get(0).value().compare("World") == 0); + assert(result.get(1).value().compare("Hello") == 0); + } + catch(RedisException &e) + { + fail(e.message()); + } + catch(Poco::NullValueException &e) + { + fail(e.message()); + } +} + +void RedisTest::testPool() +{ + Poco::Net::SocketAddress sa(_host, _port); + Poco::PoolableObjectFactory factory(sa); + Poco::ObjectPool pool(factory, 10, 15); + + delKey("mypoolkey"); + + PooledConnection pclient1(pool); + PooledConnection pclient2(pool); + assert(pool.size() == 2); + + Command set = Command::set("mypoolkey", "Hello"); + std::string result = ((Client::Ptr) pclient1)->execute(set); + assert(result.compare("OK") == 0); + + Array get; + get << "GET" << "mypoolkey"; + BulkString keyValue = ((Client::Ptr) pclient2)->execute(get); + assert(keyValue.value().compare("Hello") == 0); +} + +void RedisTest::delKey(const std::string& key) +{ + Command delCommand = Command::del(key); + try + { + _redis.execute(delCommand); + } + catch(RedisException& e) + { + fail(e.message()); + } + catch(Poco::BadCastException& e) + { + fail(e.message()); + } +} + + +CppUnit::Test* RedisTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("RedisTest"); + + CppUnit_addTest(pSuite, RedisTest, testAPPEND); + CppUnit_addTest(pSuite, RedisTest, testBLPOP); + CppUnit_addTest(pSuite, RedisTest, testBRPOP); + CppUnit_addTest(pSuite, RedisTest, testDECR); + CppUnit_addTest(pSuite, RedisTest, testDECR); + CppUnit_addTest(pSuite, RedisTest, testECHO); + CppUnit_addTest(pSuite, RedisTest, testError); + CppUnit_addTest(pSuite, RedisTest, testEVAL); + CppUnit_addTest(pSuite, RedisTest, testHDEL); + CppUnit_addTest(pSuite, RedisTest, testHEXISTS); + CppUnit_addTest(pSuite, RedisTest, testHGETALL); + CppUnit_addTest(pSuite, RedisTest, testHINCRBY); + CppUnit_addTest(pSuite, RedisTest, testHKEYS); + CppUnit_addTest(pSuite, RedisTest, testHMGET); + CppUnit_addTest(pSuite, RedisTest, testHMSET); + CppUnit_addTest(pSuite, RedisTest, testHSET); + //CppUnit_addTest(pSuite, RedisTest, testHSTRLEN); + CppUnit_addTest(pSuite, RedisTest, testHVALS); + CppUnit_addTest(pSuite, RedisTest, testINCR); + CppUnit_addTest(pSuite, RedisTest, testINCRBY); + CppUnit_addTest(pSuite, RedisTest, testLINDEX); + CppUnit_addTest(pSuite, RedisTest, testLINSERT); + CppUnit_addTest(pSuite, RedisTest, testLPOP); + CppUnit_addTest(pSuite, RedisTest, testLREM); + CppUnit_addTest(pSuite, RedisTest, testLSET); + CppUnit_addTest(pSuite, RedisTest, testLTRIM); + CppUnit_addTest(pSuite, RedisTest, testMSET); + CppUnit_addTest(pSuite, RedisTest, testMSETWithMap); + CppUnit_addTest(pSuite, RedisTest, testMULTI); + CppUnit_addTest(pSuite, RedisTest, testPING); + CppUnit_addTest(pSuite, RedisTest, testPipeliningWithSendCommands); + CppUnit_addTest(pSuite, RedisTest, testPipeliningWithWriteCommand); + CppUnit_addTest(pSuite, RedisTest, testPubSub); + CppUnit_addTest(pSuite, RedisTest, testSADD); + CppUnit_addTest(pSuite, RedisTest, testSCARD); + CppUnit_addTest(pSuite, RedisTest, testSDIFF); + CppUnit_addTest(pSuite, RedisTest, testSDIFFSTORE); + CppUnit_addTest(pSuite, RedisTest, testSET); + CppUnit_addTest(pSuite, RedisTest, testSINTER); + CppUnit_addTest(pSuite, RedisTest, testSINTERSTORE); + CppUnit_addTest(pSuite, RedisTest, testSISMEMBER); + CppUnit_addTest(pSuite, RedisTest, testSMEMBERS); + CppUnit_addTest(pSuite, RedisTest, testSMOVE); + CppUnit_addTest(pSuite, RedisTest, testSPOP); + CppUnit_addTest(pSuite, RedisTest, testSRANDMEMBER); + CppUnit_addTest(pSuite, RedisTest, testSREM); + CppUnit_addTest(pSuite, RedisTest, testSTRLEN); + CppUnit_addTest(pSuite, RedisTest, testSUNION); + CppUnit_addTest(pSuite, RedisTest, testSUNIONSTORE); + CppUnit_addTest(pSuite, RedisTest, testRENAME); + CppUnit_addTest(pSuite, RedisTest, testRENAMENX); + CppUnit_addTest(pSuite, RedisTest, testRPOP); + CppUnit_addTest(pSuite, RedisTest, testRPOPLPUSH); + CppUnit_addTest(pSuite, RedisTest, testRPUSH); + + CppUnit_addTest(pSuite, RedisTest, testPool); + + return pSuite; +} diff --git a/Redis/testsuite/src/RedisTest.h b/Redis/testsuite/src/RedisTest.h new file mode 100644 index 000000000..9f55ed389 --- /dev/null +++ b/Redis/testsuite/src/RedisTest.h @@ -0,0 +1,106 @@ +// +// RedisTest.h +// +// $Id$ +// +// Definition of the RedisTest class. +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef RedisTest_INCLUDED +#define RedisTest_INCLUDED + + +#include "Poco/Redis/Redis.h" +#include "Poco/Redis/Client.h" + +#include "CppUnit/TestCase.h" + + +class RedisTest: public CppUnit::TestCase +{ +public: + RedisTest(const std::string& name); + + + virtual ~RedisTest(); + + void testAPPEND(); + void testBLPOP(); + void testBRPOP(); + void testDECR(); + void testECHO(); + void testError(); + void testEVAL(); + void testHDEL(); + void testHEXISTS(); + void testHGETALL(); + void testHINCRBY(); + void testHKEYS(); + void testHMGET(); + void testHMSET(); + void testHSET(); + void testHSTRLEN(); + void testHVALS(); + void testINCR(); + void testINCRBY(); + void testLINDEX(); + void testLINSERT(); + void testLPOP(); + void testLREM(); + void testLSET(); + void testLTRIM(); + void testMULTI(); + void testMSET(); + void testMSETWithMap(); + void testPING(); + void testPipeliningWithSendCommands(); + void testPipeliningWithWriteCommand(); + void testPubSub(); + void testSADD(); + void testSCARD(); + void testSDIFF(); + void testSDIFFSTORE(); + void testSET(); + void testSINTER(); + void testSINTERSTORE(); + void testSISMEMBER(); + void testSMEMBERS(); + void testSMOVE(); + void testSPOP(); + void testSRANDMEMBER(); + void testSREM(); + void testSUNION(); + void testSUNIONSTORE(); + void testSTRLEN(); + void testRENAME(); + void testRENAMENX(); + void testRPOP(); + void testRPOPLPUSH(); + void testRPUSH(); + + void testPool(); + + void setUp(); + void tearDown(); + + static CppUnit::Test* suite(); + +private: + + void delKey(const std::string& key); + + std::string _host; + unsigned _port; + static bool _connected; + static Poco::Redis::Client _redis; + +}; + + +#endif // RedisTest_INCLUDED diff --git a/Redis/testsuite/src/RedisTestSuite.cpp b/Redis/testsuite/src/RedisTestSuite.cpp new file mode 100644 index 000000000..f2e806a73 --- /dev/null +++ b/Redis/testsuite/src/RedisTestSuite.cpp @@ -0,0 +1,24 @@ +// +// RedisTestSuite.cpp +// +// $Id$ +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "RedisTestSuite.h" +#include "RedisTest.h" + + +CppUnit::Test* RedisTestSuite::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("RedisTestSuite"); + + pSuite->addTest(RedisTest::suite()); + + return pSuite; +} diff --git a/Redis/testsuite/src/RedisTestSuite.h b/Redis/testsuite/src/RedisTestSuite.h new file mode 100644 index 000000000..4f7664e0b --- /dev/null +++ b/Redis/testsuite/src/RedisTestSuite.h @@ -0,0 +1,29 @@ +// +// RedisTestSuite.h +// +// $Id$ +// +// Definition of the RedisTestSuite class. +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef RedisTestSuite_INCLUDED +#define RedisTestSuite_INCLUDED + + +#include "CppUnit/TestSuite.h" + + +class RedisTestSuite +{ +public: + static CppUnit::Test* suite(); +}; + + +#endif // RedisTestSuite_INCLUDED diff --git a/Redis/testsuite/src/WinCEDriver.cpp b/Redis/testsuite/src/WinCEDriver.cpp new file mode 100644 index 000000000..58f7abd62 --- /dev/null +++ b/Redis/testsuite/src/WinCEDriver.cpp @@ -0,0 +1,32 @@ +// +// WinCEDriver.cpp +// +// $Id$ +// +// Console-based test driver for Windows CE. +// +// Copyright (c) 2004-2010, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "CppUnit/TestRunner.h" +#include "RedisTestSuite.h" +#include + + +int _tmain(int argc, wchar_t* argv[]) +{ + std::vector args; + for (int i = 0; i < argc; ++i) + { + char buffer[1024]; + std::wcstombs(buffer, argv[i], sizeof(buffer)); + args.push_back(std::string(buffer)); + } + CppUnit::TestRunner runner; + runner.addTest("RedisTestSuite", RedisTestSuite::suite()); + return runner.run(args) ? 0 : 1; +} diff --git a/Redis/testsuite/src/WinDriver.cpp b/Redis/testsuite/src/WinDriver.cpp new file mode 100644 index 000000000..f1d37cd3a --- /dev/null +++ b/Redis/testsuite/src/WinDriver.cpp @@ -0,0 +1,30 @@ +// +// WinDriver.cpp +// +// $Id$ +// +// Windows test driver for Poco MongoDB. +// +// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "WinTestRunner/WinTestRunner.h" +#include "RedisTestSuite.h" + + +class TestDriver: public CppUnit::WinTestRunnerApp +{ + void TestMain() + { + CppUnit::WinTestRunner runner; + runner.addTest(RedisTestSuite::suite()); + runner.run(); + } +}; + + +TestDriver theDriver; diff --git a/Zip/testsuite/src/CompressTest.cpp b/Zip/testsuite/src/CompressTest.cpp index 4af1598f4..41c61f0d5 100644 --- a/Zip/testsuite/src/CompressTest.cpp +++ b/Zip/testsuite/src/CompressTest.cpp @@ -19,6 +19,7 @@ #include "Poco/FileStream.h" #include "CppUnit/TestCaller.h" #include "CppUnit/TestSuite.h" +#include #include #undef min #include @@ -40,7 +41,7 @@ CompressTest::~CompressTest() void CompressTest::testSingleFile() { std::ofstream out("appinf.zip", std::ios::binary); - Poco::Path theFile(ZipTest::getTestFile("test.zip")); + Poco::Path theFile(ZipTest::getTestFile("data", "test.zip")); Compress c(out, true); c.addFile(theFile, theFile.getFileName()); ZipArchive a(c.close()); @@ -75,14 +76,14 @@ void CompressTest::testManipulator() { { std::ofstream out("appinf.zip", std::ios::binary); - Poco::Path theFile(ZipTest::getTestFile("test.zip")); + Poco::Path theFile(ZipTest::getTestFile("data", "test.zip")); Compress c(out, true); c.addFile(theFile, theFile.getFileName()); ZipArchive a(c.close()); } ZipManipulator zm("appinf.zip", true); zm.renameFile("test.zip", "renamedtest.zip"); - zm.addFile("doc/othertest.zip", ZipTest::getTestFile("test.zip")); + zm.addFile("doc/othertest.zip", ZipTest::getTestFile("data", "test.zip")); ZipArchive archive=zm.commit(); assert (archive.findHeader("doc/othertest.zip") != archive.headerEnd()); } @@ -92,14 +93,14 @@ void CompressTest::testManipulatorDel() { { std::ofstream out("appinf.zip", std::ios::binary); - Poco::Path theFile(ZipTest::getTestFile("test.zip")); + Poco::Path theFile(ZipTest::getTestFile("data", "test.zip")); Compress c(out, true); c.addFile(theFile, theFile.getFileName()); ZipArchive a(c.close()); } ZipManipulator zm("appinf.zip", true); zm.deleteFile("test.zip"); - zm.addFile("doc/data.zip", ZipTest::getTestFile("data.zip")); + zm.addFile("doc/data.zip", ZipTest::getTestFile("data", "data.zip")); ZipArchive archive=zm.commit(); assert (archive.findHeader("test.zip") == archive.headerEnd()); assert (archive.findHeader("doc/data.zip") != archive.headerEnd()); @@ -110,13 +111,13 @@ void CompressTest::testManipulatorReplace() { { std::ofstream out("appinf.zip", std::ios::binary); - Poco::Path theFile(ZipTest::getTestFile("test.zip")); + Poco::Path theFile(ZipTest::getTestFile("data", "test.zip")); Compress c(out, true); c.addFile(theFile, theFile.getFileName()); ZipArchive a(c.close()); } ZipManipulator zm("appinf.zip", true); - zm.replaceFile("test.zip", ZipTest::getTestFile("doc.zip")); + zm.replaceFile("test.zip", ZipTest::getTestFile("data", "doc.zip")); ZipArchive archive=zm.commit(); assert (archive.findHeader("test.zip") != archive.headerEnd()); @@ -128,7 +129,7 @@ void CompressTest::testSetZipComment() { std::string comment("Testing...123..."); std::ofstream out("comment.zip", std::ios::binary); - Poco::Path theFile(ZipTest::getTestFile("test.zip")); + Poco::Path theFile(ZipTest::getTestFile("data", "test.zip")); Compress c(out, true); c.addFile(theFile, theFile.getFileName()); c.setZipComment(comment); diff --git a/Zip/testsuite/src/ZipTest.cpp b/Zip/testsuite/src/ZipTest.cpp index 41e96a3a5..e56c61bc6 100644 --- a/Zip/testsuite/src/ZipTest.cpp +++ b/Zip/testsuite/src/ZipTest.cpp @@ -27,6 +27,7 @@ #include "CppUnit/TestSuite.h" #undef min #include +#include #include #include @@ -46,7 +47,7 @@ ZipTest::~ZipTest() void ZipTest::testSkipSingleFile() { - std::string testFile = getTestFile("test.zip"); + std::string testFile = getTestFile("data", "test.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); SkipCallback skip; @@ -68,7 +69,7 @@ void ZipTest::testSkipSingleFile() void ZipTest::testDecompressSingleFile() { - std::string testFile = getTestFile("test.zip"); + std::string testFile = getTestFile("data", "test.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); ZipArchive arch(inp); @@ -83,7 +84,7 @@ void ZipTest::testDecompressSingleFile() void ZipTest::testDecompressSingleFileInDir() { - std::string testFile = getTestFile("test.zip"); + std::string testFile = getTestFile("data","test.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); ZipArchive arch(inp); @@ -98,7 +99,7 @@ void ZipTest::testDecompressSingleFileInDir() void ZipTest::testCrcAndSizeAfterData() { - std::string testFile = getTestFile("data.zip"); + std::string testFile = getTestFile("data", "data.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); Decompress dec(inp, Poco::Path()); @@ -112,7 +113,7 @@ void ZipTest::testCrcAndSizeAfterData() void ZipTest::testCrcAndSizeAfterDataWithArchive() { - std::string testFile = getTestFile("data.zip"); + std::string testFile = getTestFile("data", "data.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); Poco::Zip::ZipArchive zip(inp); @@ -132,30 +133,34 @@ void ZipTest::testCrcAndSizeAfterDataWithArchive() } -std::string ZipTest::getTestFile(const std::string& testFile) +std::string ZipTest::getTestFile(const std::string& directory, const std::string& file) { - Poco::Path root; - root.makeAbsolute(); - Poco::Path result; - while (!Poco::Path::find(root.toString(), "data", result)) + std::ostringstream ostr; + ostr << directory << '/' << file; + std::string validDir(ostr.str()); + Poco::Path pathPattern(validDir); + if (Poco::File(pathPattern).exists()) { - root.makeParent(); - if (root.toString().empty() || root.toString() == "/") - throw Poco::FileNotFoundException("Didn't find data subdir"); + return validDir; } - result.makeDirectory(); - result.setFileName(testFile); - Poco::File aFile(result.toString()); - if (!aFile.exists() || (aFile.exists() && !aFile.isFile())) - throw Poco::FileNotFoundException("Didn't find " + testFile); - - return result.toString(); + + ostr.str(""); + ostr << "/Zip/testsuite/" << directory << '/' << file; + validDir = Poco::Environment::get("POCO_BASE") + ostr.str(); + pathPattern = validDir; + + if (!Poco::File(pathPattern).exists()) + { + std::cout << "Can't find " << validDir << std::endl; + throw Poco::NotFoundException("cannot locate directory containing valid Zip test files"); + } + return validDir; } void ZipTest::testDecompress() { - std::string testFile = getTestFile("test.zip"); + std::string testFile = getTestFile("data", "test.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); Decompress dec(inp, Poco::Path()); @@ -169,7 +174,7 @@ void ZipTest::testDecompress() void ZipTest::testDecompressFlat() { - std::string testFile = getTestFile("test.zip"); + std::string testFile = getTestFile("data", "test.zip"); std::ifstream inp(testFile.c_str(), std::ios::binary); assert (inp.good()); Decompress dec(inp, Poco::Path(), true); diff --git a/Zip/testsuite/src/ZipTest.h b/Zip/testsuite/src/ZipTest.h index cfd9b81ae..180b8ed53 100644 --- a/Zip/testsuite/src/ZipTest.h +++ b/Zip/testsuite/src/ZipTest.h @@ -45,7 +45,7 @@ public: static CppUnit::Test* suite(); - static std::string getTestFile(const std::string& testFile); + static std::string getTestFile(const std::string& directory, const std::string& type); private: void onDecompressError(const void* pSender, std::pair& info); diff --git a/build/config/AppleTV b/build/config/AppleTV index dff58750c..4f68d8bab 100644 --- a/build/config/AppleTV +++ b/build/config/AppleTV @@ -31,7 +31,7 @@ TVOS_SDK_VERSION_MIN ?= $(patsubst %.sdk,%,$(patsubst $(TVOS_SDK_ROOT_DIR)%,%,$( POCO_TARGET_OSNAME ?= $(TVOS_SDK) POCO_TARGET_OSARCH ?= arm64 TOOL_PREFIX ?= $(shell xcode-select -print-path)/Platforms/$(TVOS_SDK).platform/Developer/usr/bin -OSFLAGS ?= -arch $(POCO_TARGET_OSARCH) -isysroot $(TVOS_SDK_BASE) -mtvos-version-min=$(TVOS_SDK_VERSION_MIN) +OSFLAGS ?= -arch $(POCO_TARGET_OSARCH) -isysroot $(TVOS_SDK_BASE) -mtvos-version-min=$(TVOS_SDK_VERSION_MIN) -fembed-bitcode # # Tools diff --git a/build/config/AppleTVSimulator b/build/config/AppleTVSimulator index 211b34c31..a8659a2ca 100644 --- a/build/config/AppleTVSimulator +++ b/build/config/AppleTVSimulator @@ -8,6 +8,6 @@ TVOS_SDK = AppleTVSimulator POCO_TARGET_OSARCH = x86_64 -OSFLAGS = -arch $(POCO_TARGET_OSARCH) -isysroot $(TVOS_SDK_BASE) -mtvos-simulator-version-min=$(TVOS_SDK_VERSION_MIN) +OSFLAGS = -arch $(POCO_TARGET_OSARCH) -isysroot $(TVOS_SDK_BASE) -mtvos-simulator-version-min=$(TVOS_SDK_VERSION_MIN) -fembed-bitcode include $(POCO_BASE)/build/config/AppleTV diff --git a/build/config/Cygwin b/build/config/Cygwin index 4538cd69f..fe14e1032 100644 --- a/build/config/Cygwin +++ b/build/config/Cygwin @@ -33,9 +33,13 @@ MKDIR = mkdir -p # Extension for Shared Libraries # LIBPREFIX = cyg -SHAREDLIBEXT = .$(target_version).dll +SHAREDLIBEXT = -$(target_version).dll SHAREDLIBLINKEXT = .dll +IMPPREFIX = lib +IMPLIBEXT = -$(target_version).dll.a +IMPLIBLINKEXT = .dll.a + # # Compiler and Linker Flags # diff --git a/build/config/Linux b/build/config/Linux index 9d1c09731..c1e56d699 100644 --- a/build/config/Linux +++ b/build/config/Linux @@ -39,14 +39,20 @@ SHAREDLIBLINKEXT = .so # Compiler and Linker Flags # CFLAGS = -CFLAGS32 = -CFLAGS64 = +CFLAGS32 = -m32 +CFLAGS64 = -m64 CXXFLAGS = -Wall -Wno-sign-compare -CXXFLAGS32 = -CXXFLAGS64 = +CXXFLAGS32 = -m32 +CXXFLAGS64 = -m64 +SHLIBFLAGS = +SHLIBFLAGS32 = -m32 +SHLIBFLAGS64 = -m64 +LIBFLAGS = +LIBFLAGS32 = -m32 +LIBFLAGS64 = -m64 LINKFLAGS = -LINKFLAGS32 = -LINKFLAGS64 = +LINKFLAGS32 = -m32 +LINKFLAGS64 = -m64 STATICOPT_CC = STATICOPT_CXX = STATICOPT_LINK = -static @@ -69,3 +75,15 @@ SYSFLAGS = -D_XOPEN_SOURCE=500 -D_REENTRANT -D_THREAD_SAFE -D_FILE_OFFSET_BITS=6 # System Specific Libraries # SYSLIBS = -lpthread -ldl -lrt + +# +# Auto-detect architecture if not specified +# +ifndef OSARCH_64BITS + LBITS := $(shell getconf LONG_BIT) + ifeq ($(LBITS),64) + OSARCH_64BITS = 1 + else + OSARCH_64BITS = 0 + endif +endif diff --git a/build/config/WatchOS b/build/config/WatchOS index 15721fe22..d576e0235 100644 --- a/build/config/WatchOS +++ b/build/config/WatchOS @@ -31,7 +31,7 @@ WATCHOS_SDK_VERSION_MIN ?= $(patsubst %.sdk,%,$(patsubst $(WATCHOS_SDK_ROOT_DIR) POCO_TARGET_OSNAME ?= $(WATCHOS_SDK) POCO_TARGET_OSARCH ?= armv7k TOOL_PREFIX ?= $(shell xcode-select -print-path)/Platforms/$(WATCHOS_SDK).platform/Developer/usr/bin -OSFLAGS ?= -arch $(POCO_TARGET_OSARCH) -isysroot $(WATCHOS_SDK_BASE) -mwatchos-version-min=$(WATCHOS_SDK_VERSION_MIN) +OSFLAGS ?= -arch $(POCO_TARGET_OSARCH) -isysroot $(WATCHOS_SDK_BASE) -mwatchos-version-min=$(WATCHOS_SDK_VERSION_MIN) -fembed-bitcode # # Tools diff --git a/build/config/WatchSimulator b/build/config/WatchSimulator index 3e296ac20..b264e4c9a 100644 --- a/build/config/WatchSimulator +++ b/build/config/WatchSimulator @@ -8,6 +8,6 @@ WATCHOS_SDK = WatchSimulator POCO_TARGET_OSARCH = i386 -OSFLAGS = -arch $(POCO_TARGET_OSARCH) -isysroot $(WATCHOS_SDK_BASE) -mwatchos-simulator-version-min=$(WATCHOS_SDK_VERSION_MIN) +OSFLAGS = -arch $(POCO_TARGET_OSARCH) -isysroot $(WATCHOS_SDK_BASE) -mwatchos-simulator-version-min=$(WATCHOS_SDK_VERSION_MIN) -fembed-bitcode include $(POCO_BASE)/build/config/WatchOS diff --git a/build/rules/dylib b/build/rules/dylib index b4fa9b951..dd2025291 100644 --- a/build/rules/dylib +++ b/build/rules/dylib @@ -22,13 +22,13 @@ DYLIB = $(SHLIB) DYLIBFLAGS = $(SHLIBFLAGS) endif -DYLIB_DEBUG = $(BINPATH)/$(target)d$(OSARCH_POSTFIX)$(SHL_EXT) -DYLIB_RELEASE = $(BINPATH)/$(target)$(OSARCH_POSTFIX)$(SHL_EXT) -DYLIB_S_DEBUG = $(BINPATH)/static/$(target)d$(OSARCH_POSTFIX)$(SHL_EXT) -DYLIB_S_RELEASE = $(BINPATH)/static/$(target)$(OSARCH_POSTFIX)$(SHL_EXT) +DYLIB_DEBUG = $(BINPATH)/$(target)d$(SHL_EXT) +DYLIB_RELEASE = $(BINPATH)/$(target)$(SHL_EXT) +DYLIB_S_DEBUG = $(BINPATH)/static/$(target)d$(SHL_EXT) +DYLIB_S_RELEASE = $(BINPATH)/static/$(target)$(SHL_EXT) -TARGET_LIBS_DEBUG = $(foreach l,$(target_libs),-l$(l)d$(OSARCH_POSTFIX)) -TARGET_LIBS_RELEASE = $(foreach l,$(target_libs),-l$(l)$(OSARCH_POSTFIX)) +TARGET_LIBS_DEBUG = $(foreach l,$(target_libs),-l$(l)d) +TARGET_LIBS_RELEASE = $(foreach l,$(target_libs),-l$(l)) # # Include the compile rules diff --git a/build/rules/exec b/build/rules/exec index d65d09b6d..73753a29c 100644 --- a/build/rules/exec +++ b/build/rules/exec @@ -9,13 +9,13 @@ # # Target names # -EXEC_RELEASE_STATIC = $(BINPATH)/static/$(target)$(OSARCH_POSTFIX)$(BINEXT) -EXEC_DEBUG_STATIC = $(BINPATH)/static/$(target)d$(OSARCH_POSTFIX)$(BINEXT) -EXEC_RELEASE_SHARED = $(BINPATH)/$(target)$(OSARCH_POSTFIX)$(BINEXT) -EXEC_DEBUG_SHARED = $(BINPATH)/$(target)d$(OSARCH_POSTFIX)$(BINEXT) +EXEC_RELEASE_STATIC = $(BINPATH)/static/$(target)$(BINEXT) +EXEC_DEBUG_STATIC = $(BINPATH)/static/$(target)d$(BINEXT) +EXEC_RELEASE_SHARED = $(BINPATH)/$(target)$(BINEXT) +EXEC_DEBUG_SHARED = $(BINPATH)/$(target)d$(BINEXT) -TARGET_LIBS_DEBUG = $(foreach l,$(target_libs),-l$(l)d$(OSARCH_POSTFIX)) -TARGET_LIBS_RELEASE = $(foreach l,$(target_libs),-l$(l)$(OSARCH_POSTFIX)) +TARGET_LIBS_DEBUG = $(foreach l,$(target_libs),-l$(l)d) +TARGET_LIBS_RELEASE = $(foreach l,$(target_libs),-l$(l)) # # Include the compile rules diff --git a/build/rules/global b/build/rules/global index 0f4e08968..ea1ef2cec 100644 --- a/build/rules/global +++ b/build/rules/global @@ -249,12 +249,14 @@ LINKFLAGS += $(COMMONFLAGS) $(SYSFLAGS) ifeq ($(OSARCH_64BITS),1) CFLAGS += $(CFLAGS64) CXXFLAGS += $(CXXFLAGS64) +LIBFLAGS += $(LIBFLAGS64) SHLIBFLAGS += $(SHLIBFLAGS64) DYLIBFLAGS += $(DYLIBFLAGS64) LINKFLAGS += $(LINKFLAGS64) else CFLAGS += $(CFLAGS32) CXXFLAGS += $(CXXFLAGS32) +LIBFLAGS += $(LIBFLAGS32) SHLIBFLAGS += $(SHLIBFLAGS32) DYLIBFLAGS += $(DYLIBFLAGS32) LINKFLAGS += $(LINKFLAGS32) @@ -263,10 +265,10 @@ endif # # Compose object file path # -OBJPATH_RELEASE_STATIC = $(OBJPATH)/release_static$(OSARCH_POSTFIX) -OBJPATH_DEBUG_STATIC = $(OBJPATH)/debug_static$(OSARCH_POSTFIX) -OBJPATH_RELEASE_SHARED = $(OBJPATH)/release_shared$(OSARCH_POSTFIX) -OBJPATH_DEBUG_SHARED = $(OBJPATH)/debug_shared$(OSARCH_POSTFIX) +OBJPATH_RELEASE_STATIC = $(OBJPATH)/release_static +OBJPATH_DEBUG_STATIC = $(OBJPATH)/debug_static +OBJPATH_RELEASE_SHARED = $(OBJPATH)/release_shared +OBJPATH_DEBUG_SHARED = $(OBJPATH)/debug_shared # # Build Include directory List diff --git a/build/rules/lib b/build/rules/lib index 8126f4af3..c2ae1334b 100644 --- a/build/rules/lib +++ b/build/rules/lib @@ -11,19 +11,28 @@ # ifdef target_version SHL_EXT = $(SHAREDLIBEXT) +IMP_EXT = $(IMPLIBEXT) else SHL_EXT = $(SHAREDLIBLINKEXT) +IMP_EXT = $(IMPLIBLINKEXT) endif -LIB_RELEASE_STATIC = $(LIBPATH)/$(LIBPREFIX)$(target)$(OSARCH_POSTFIX).a -LIB_DEBUG_STATIC = $(LIBPATH)/$(LIBPREFIX)$(target)d$(OSARCH_POSTFIX).a -LIB_RELEASE_SHARED = $(LIBPATH)/$(LIBPREFIX)$(target)$(OSARCH_POSTFIX)$(SHL_EXT) -LIB_DEBUG_SHARED = $(LIBPATH)/$(LIBPREFIX)$(target)d$(OSARCH_POSTFIX)$(SHL_EXT) -LIB_RELEASE_SHARED_LINK = $(LIBPATH)/$(LIBPREFIX)$(target)$(OSARCH_POSTFIX)$(SHAREDLIBLINKEXT) -LIB_DEBUG_SHARED_LINK = $(LIBPATH)/$(LIBPREFIX)$(target)d$(OSARCH_POSTFIX)$(SHAREDLIBLINKEXT) +LIB_RELEASE_STATIC = $(LIBPATH)/$(LIBPREFIX)$(target).a +LIB_DEBUG_STATIC = $(LIBPATH)/$(LIBPREFIX)$(target)d.a +LIB_RELEASE_SHARED = $(LIBPATH)/$(LIBPREFIX)$(target)$(SHL_EXT) +LIB_DEBUG_SHARED = $(LIBPATH)/$(LIBPREFIX)$(target)d$(SHL_EXT) +LIB_RELEASE_SHARED_LINK = $(LIBPATH)/$(LIBPREFIX)$(target)$(SHAREDLIBLINKEXT) +LIB_DEBUG_SHARED_LINK = $(LIBPATH)/$(LIBPREFIX)$(target)d$(SHAREDLIBLINKEXT) -TARGET_LIBS_DEBUG = $(foreach l,$(target_libs),-l$(l)d$(OSARCH_POSTFIX)) -TARGET_LIBS_RELEASE = $(foreach l,$(target_libs),-l$(l)$(OSARCH_POSTFIX)) +ifeq ($(OSNAME), Cygwin) +IMP_RELEASE_SHARED = $(LIBPATH)/$(IMPPREFIX)$(target)$(OSARCH_POSTFIX)$(IMP_EXT) +IMP_DEBUG_SHARED = $(LIBPATH)/$(IMPPREFIX)$(target)d$(OSARCH_POSTFIX)$(IMP_EXT) +IMP_RELEASE_SHARED_LINK = $(LIBPATH)/$(IMPPREFIX)$(target)$(OSARCH_POSTFIX)$(IMPLIBLINKEXT) +IMP_DEBUG_SHARED_LINK = $(LIBPATH)/$(IMPPREFIX)$(target)d$(OSARCH_POSTFIX)$(IMPLIBLINKEXT) +endif + +TARGET_LIBS_DEBUG = $(foreach l,$(target_libs),-l$(l)d) +TARGET_LIBS_RELEASE = $(foreach l,$(target_libs),-l$(l)) # # Include the compile rules @@ -49,25 +58,31 @@ shared_release: libdirs $(LIB_RELEASE_SHARED) $(LIB_DEBUG_STATIC): $(foreach o,$(objects),$(OBJPATH_DEBUG_STATIC)/$(o).o) @echo "** Building library (debug)" $@ - $(LIB) $@ $^ + $(LIB) $(LIBFLAGS) $@ $^ $(RANLIB) $@ $(postbuild) $(LIB_RELEASE_STATIC): $(foreach o,$(objects),$(OBJPATH_RELEASE_STATIC)/$(o).o) @echo "** Building library (release)" $@ - $(LIB) $@ $^ + $(LIB) $(LIBFLAGS) $@ $^ $(RANLIB) $@ $(postbuild) $(LIB_DEBUG_SHARED): $(foreach o,$(objects),$(OBJPATH_DEBUG_SHARED)/$(o).o) @echo "** Building shared library (debug)" $@ $(SHLIB) $(SHLIBFLAGS) $^ $(LIBRARY) $(TARGET_LIBS_DEBUG) $(SYSLIBS) +ifeq ($(OSNAME), Cygwin) + $(SHLIBLN) $(IMP_DEBUG_SHARED) $(IMP_DEBUG_SHARED_LINK) +endif $(SHLIBLN) $(LIB_DEBUG_SHARED) $(LIB_DEBUG_SHARED_LINK) $(postbuild) $(LIB_RELEASE_SHARED): $(foreach o,$(objects),$(OBJPATH_RELEASE_SHARED)/$(o).o) @echo "** Building shared library (release)" $@ $(SHLIB) $(SHLIBFLAGS) $^ $(LIBRARY) $(TARGET_LIBS_RELEASE) $(SYSLIBS) +ifeq ($(OSNAME), Cygwin) + $(SHLIBLN) $(IMP_RELEASE_SHARED) $(IMP_RELEASE_SHARED_LINK) +endif $(SHLIBLN) $(LIB_RELEASE_SHARED) $(LIB_RELEASE_SHARED_LINK) $(STRIPCMD) $(postbuild) diff --git a/build/rules/sample b/build/rules/sample index b0724afb0..92ab738b1 100644 --- a/build/rules/sample +++ b/build/rules/sample @@ -78,10 +78,10 @@ LINKFLAGS += $(COMMONFLAGS) $(SYSFLAGS) # # Compose object file path # -OBJPATH_RELEASE_STATIC = $(OBJPATH)/release_static$(OSARCH_POSTFIX) -OBJPATH_DEBUG_STATIC = $(OBJPATH)/debug_static$(OSARCH_POSTFIX) -OBJPATH_RELEASE_SHARED = $(OBJPATH)/release_shared$(OSARCH_POSTFIX) -OBJPATH_DEBUG_SHARED = $(OBJPATH)/debug_shared$(OSARCH_POSTFIX) +OBJPATH_RELEASE_STATIC = $(OBJPATH)/release_static +OBJPATH_DEBUG_STATIC = $(OBJPATH)/debug_static +OBJPATH_RELEASE_SHARED = $(OBJPATH)/release_shared +OBJPATH_DEBUG_SHARED = $(OBJPATH)/debug_shared # # Build Include directory List diff --git a/build/script/runtests.sh b/build/script/runtests.sh index 728c99bb5..3a93dd9fc 100755 --- a/build/script/runtests.sh +++ b/build/script/runtests.sh @@ -22,11 +22,15 @@ # 5/ run the Foundation tests: build/script/runtests.sh Foundation # +TESTRUNNER=./testrunner + if [ "$POCO_BASE" = "" ] ; then POCO_BASE=`pwd` fi -TESTRUNNER=./testrunner +if [ "$POCO_BUILD" = "" ] ; then + POCO_BUILD=$POCO_BASE +fi if [ "$1" = "" ] ; then components=`cat $POCO_BASE/components` @@ -40,21 +44,22 @@ else TESTRUNNERARGS=$2 fi +if [ "$OSARCH" = "" ] ; then + OSARCH=`uname -m | tr ' /' _-` +fi if [ "$OSNAME" = "" ] ; then OSNAME=`uname` case $OSNAME in CYGWIN*) - OSNAME=CYGWIN + OSNAME=Cygwin TESTRUNNER=$TESTRUNNER.exe + PATH=$POCO_BUILD/lib/$OSNAME/$OSARCH:$PATH ;; MINGW*) OSNAME=MinGW ;; esac fi -if [ "$OSARCH" = "" ] ; then - OSARCH=`uname -m | tr ' /' _-` -fi BINDIR="bin/$OSNAME/$OSARCH/" runs=0 @@ -72,8 +77,8 @@ do fi done if [ $excluded -eq 0 ] ; then - if [ -d "$POCO_BASE/$comp/testsuite/$BINDIR" ] ; then - if [ -x "$POCO_BASE/$comp/testsuite/$BINDIR/$TESTRUNNER" ] ; then + if [ -d "$POCO_BUILD/$comp/testsuite/$BINDIR" ] ; then + if [ -x "$POCO_BUILD/$comp/testsuite/$BINDIR/$TESTRUNNER" ] ; then echo "" echo "" echo "****************************************" @@ -82,7 +87,7 @@ do echo "" runs=`expr $runs + 1` - sh -c "cd $POCO_BASE/$comp/testsuite/$BINDIR && $TESTRUNNER $TESTRUNNERARGS" + sh -c "cd $POCO_BUILD/$comp/testsuite/$BINDIR && $TESTRUNNER $TESTRUNNERARGS" if [ $? -ne 0 ] ; then failures=`expr $failures + 1` failedTests="$failedTests $comp" diff --git a/components b/components index 1ad691031..a415f7da3 100644 --- a/components +++ b/components @@ -18,5 +18,6 @@ PageCompiler/File2Page PDF CppParser MongoDB +Redis PocoDoc ProGen diff --git a/doc/99150-GMakeBuildNotes.page b/doc/99150-GMakeBuildNotes.page index 92bb511fe..7cd7eadc0 100644 --- a/doc/99150-GMakeBuildNotes.page +++ b/doc/99150-GMakeBuildNotes.page @@ -706,21 +706,76 @@ compiling in 32 bit mode. compiling in 64 bit mode. +!LIBFLAGS + +<*LIBFLAGS*> specifies additional flags passed to the linker when building +a static library. + + +!LIBFLAGS32 + +<*LIBFLAGS32*> specifies additional flags passed to the linker when building +a static library if compiling in 32 bit mode. + + +!LIBFLAGS64 + +<*LIBFLAGS64*> specifies additional flags passed to the linker when building +a static library if compiling in 64 bit mode. + + +!SHLIBFLAGS + +<*SHLIBFLAGS*> specifies additional flags passed to the linker when building +a shared library. + + +!SHLIBFLAGS32 + +<*SHLIBFLAGS32*> specifies additional flags passed to the linker when building +a shared library if compiling in 32 bit mode. + + +!SHLIBFLAGS64 + +<*SHLIBFLAGS64*> specifies additional flags passed to the linker when building +a shared library if compiling in 64 bit mode. + + +!DYLIBFLAGS + +<*DYLIBFLAGS*> specifies additional flags passed to the linker when building +a dynamically loadable shared library. + + +!DYLIBFLAGS32 + +<*DYLIBFLAGS32*> specifies additional flags passed to the linker when building +a dynamically loadable shared library if compiling in 32 bit mode. + + +!DYLIBFLAGS64 + +<*DYLIBFLAGS64*> specifies additional flags passed to the linker when building +a dynamically loadable shared library if compiling in 64 bit mode. + + !LINKFLAGS -<*LINKFLAGS*> specifies additional flags passed to the linker. +<*LINKFLAGS*> specifies additional flags passed to the linker when building +an executable. !LINKFLAGS32 -<*LINKFLAGS32*> specifies additional flags passed to the linker if -compiling in 32 bit mode. +<*LINKFLAGS32*> specifies additional flags passed to the linker when building +an executable if compiling in 32 bit mode. !LINKFLAGS64 -<*LINKFLAGS64*> specifies additional flags passed to the linker if -compiling in 64 bit mode. +<*LINKFLAGS64*> specifies additional flags passed to the linker when building +an executable if compiling in 64 bit mode. !STATICOPT_CC @@ -743,8 +798,8 @@ static linking. !SHAREDOPT_CC -<*SHAREDOPT_CC*> specifies additonal flags passed to the C compiler if -compiling for dynamic linking. +<*SHAREDOPT_CC*> specifies additonal flags passed to the C compiler +for dynamic linking. !SHAREDOPT_CXX