From f74473bf07f9a0e0758e222ab4ddb9b2484a081b Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 2 Dec 2016 14:16:32 +0000 Subject: [PATCH] Import catch and write a first integration test. --- .gitmodules | 3 + CMakeLists.txt | 6 ++ lib/catch | 1 + test/integration/CMakeLists.txt | 52 ++++++++++ test/integration/main.cpp | 95 +++++++++++++++++++ test/integration/redis_connection_fixture.cpp | 44 +++++++++ test/integration/redis_connection_fixture.hpp | 28 ++++++ test/integration/test_insert_retrieve.cpp | 81 ++++++++++++++++ 8 files changed, 310 insertions(+) create mode 160000 lib/catch create mode 100644 test/integration/CMakeLists.txt create mode 100644 test/integration/main.cpp create mode 100644 test/integration/redis_connection_fixture.cpp create mode 100644 test/integration/redis_connection_fixture.hpp create mode 100644 test/integration/test_insert_retrieve.cpp diff --git a/.gitmodules b/.gitmodules index 5264eb7..6c1f147 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/better-enums"] path = lib/better-enums url = https://github.com/aantron/better-enums.git +[submodule "lib/catch"] + path = lib/catch + url = https://github.com/philsquared/Catch.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 272892a..8276c57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ project(incredis CXX) list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) include(shared_git_project) +include(CTest) find_package(hiredis 0.11.0 REQUIRED) find_package(CryptoPP 5.6) @@ -59,6 +60,7 @@ if (CryptoPP_FOUND) else() set (has_cryptopp_lib OFF) endif() +set(INCREDIS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION lib @@ -79,3 +81,7 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_noexcept PUBLIC cxx_rvalue_references ) + +if (BUILD_TESTING) + add_subdirectory(test/integration) +endif() diff --git a/lib/catch b/lib/catch new file mode 160000 index 0000000..30cebd6 --- /dev/null +++ b/lib/catch @@ -0,0 +1 @@ +Subproject commit 30cebd617788f6b399297725f97317f9bc462114 diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt new file mode 100644 index 0000000..95bd8f6 --- /dev/null +++ b/test/integration/CMakeLists.txt @@ -0,0 +1,52 @@ +project(integration CXX) + +find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options) + +set(INCREDIS_TEST_HOSTNAME "127.0.0.1" CACHE STRING "Hostname for the integration test to connect to") +set(INCREDIS_TEST_PORT "6379" CACHE STRING "Port on the host") +set(INCREDIS_TEST_SOCKET "" CACHE STRING "Socket name, leave empty to use hostname:port") +set(INCREDIS_TEST_DB "0" CACHE STRING "Number of the database to run the tests in - all data in it will be destroyed") + +add_executable(${PROJECT_NAME} + main.cpp + redis_connection_fixture.cpp + test_insert_retrieve.cpp +) + +target_include_directories(${PROJECT_NAME} + PRIVATE ${INCREDIS_SOURCE_DIR}/lib/catch/single_include +) +target_include_directories(${PROJECT_NAME} SYSTEM + PRIVATE ${Boost_INCLUDE_DIRS} +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE ${Boost_LIBRARIES} + PRIVATE incredis +) + +if ("${INCREDIS_TEST_HOSTNAME}" STREQUAL "") + set(hostname_param "") +else() + set(hostname_param --hostname ${INCREDIS_TEST_HOSTNAME}) +endif() +if ("${INCREDIS_TEST_PORT}" STREQUAL "") + set(port_param "") +else() + set(port_param --port ${INCREDIS_TEST_PORT}) +endif() +if ("${INCREDIS_TEST_SOCKET}" STREQUAL "") + set(socket_param "") +else() + set(socket_param --socket ${INCREDIS_TEST_SOCKET}) +endif() +if ("${INCREDIS_TEST_DB}" STREQUAL "") + set(db_param "") +else() + set(db_param --db ${INCREDIS_TEST_DB}) +endif() +add_test( + NAME redis_integration + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${PROJECT_NAME} ${hostname_param} ${port_param} ${socket_param} ${db_param} +) diff --git a/test/integration/main.cpp b/test/integration/main.cpp new file mode 100644 index 0000000..7fa14d2 --- /dev/null +++ b/test/integration/main.cpp @@ -0,0 +1,95 @@ +#define CATCH_CONFIG_RUNNER + +#include "catch.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace po = boost::program_options; + +namespace incredis { + namespace test { + std::string g_hostname = "127.0.0.1"; + uint16_t g_port = 6379; + std::string g_socket = ""; + uint32_t g_db = 0; + } //namespace test +} //namespace incredis + +namespace { + const char* const unknown_options_key = "unknown_options"; + + void parse_commandline (int parArgc, const char* const* parArgv, po::variables_map& parVM) { + po::options_description connection_options("Redis connection options"); + connection_options.add_options() + ("hostname,h", po::value(), "Server hostname") + ("port,p", po::value(), "Server port") + ("socket,s", po::value(), "Server socket (overrides hostname and port)") + ("db,n", po::value(), "Database number") + ; + po::options_description positional_options("Catch test suite options"); + positional_options.add_options() + (unknown_options_key, po::value>(), "List of options that will be passed to Catch") + ; + + po::options_description all("Available options"); + all.add(connection_options).add(positional_options); + po::positional_options_description pd; + pd.add(unknown_options_key, -1); + + po::store(po::command_line_parser(parArgc, parArgv).options(all).positional(pd).run(), parVM); + po::notify(parVM); + } + + std::vector stringlist_to_charlist (const std::vector& parList) { + std::vector retval; + retval.reserve(parList.size()); + std::transform(parList.begin(), parList.end(), back_inserter(retval), std::bind(&std::string::c_str, std::placeholders::_1)); + assert(parList.size() == retval.size()); + return retval; + } + + void set_global_connection_params (const po::variables_map& parVM) { + using namespace incredis::test; + + if (parVM.count("hostname")) + g_hostname = parVM["hostname"].as(); + if (parVM.count("port")) + g_port = parVM["port"].as(); + if (parVM.count("socket")) + g_socket = parVM["socket"].as(); + if (parVM.count("db")) + g_db = parVM["db"].as(); + } +} //unnamed namespace + +int main (int parArgc, char* const parArgv[]) { + po::variables_map vm; + parse_commandline(parArgc, parArgv, vm); + + const std::vector unparsed_params_str( + vm.count(unknown_options_key) ? + vm[unknown_options_key].as>() : + std::vector() + ); + + std::vector unparsed_params = + stringlist_to_charlist(unparsed_params_str); + assert(parArgc); + unparsed_params.insert(unparsed_params.begin(), parArgv[0]); + + //bleah + set_global_connection_params(vm); + + Catch::Session session; + + const int retcode = session.applyCommandLine(unparsed_params.size(), unparsed_params.data()); + if (0 != retcode) + return retcode; + + return session.run(); +} diff --git a/test/integration/redis_connection_fixture.cpp b/test/integration/redis_connection_fixture.cpp new file mode 100644 index 0000000..eac31c9 --- /dev/null +++ b/test/integration/redis_connection_fixture.cpp @@ -0,0 +1,44 @@ +#include "redis_connection_fixture.hpp" +#include "catch.hpp" +#include "incredis/incredis.hpp" +#include + +namespace incredis { + namespace test { + extern std::string g_hostname; + extern uint16_t g_port; + extern std::string g_socket; + extern uint32_t g_db; + + RedisConnectionFixture::RedisConnectionFixture() : + m_hostname(g_hostname), + m_socket(g_socket), + m_db(g_db), + m_port(g_port) + { + if (use_socket_connection()) + m_incredis.reset(new redis::IncRedis(std::string(m_socket))); + else + m_incredis.reset(new redis::IncRedis(std::string(m_hostname), m_port)); + m_incredis->connect(); + m_incredis->wait_for_connect(); + REQUIRE_FALSE(not m_incredis->is_connected()); + + auto batch = m_incredis->make_batch(); + batch.select(m_db); + batch.client_setname("IncredisIntegrationTest"); + batch.script_flush(); + REQUIRE_NOTHROW(batch.throw_if_failed()); + } + + RedisConnectionFixture::~RedisConnectionFixture() noexcept = default; + + bool RedisConnectionFixture::use_socket_connection() const { + return not m_socket.empty(); + } + + redis::IncRedis& RedisConnectionFixture::incredis() { + return *m_incredis; + } + } //namespace test +} //namespace incredis diff --git a/test/integration/redis_connection_fixture.hpp b/test/integration/redis_connection_fixture.hpp new file mode 100644 index 0000000..8aa9d18 --- /dev/null +++ b/test/integration/redis_connection_fixture.hpp @@ -0,0 +1,28 @@ +#include +#include +#include + +namespace redis { + class IncRedis; +} //namespace redis + +namespace incredis { + namespace test { + class RedisConnectionFixture { + public: + RedisConnectionFixture(); + ~RedisConnectionFixture() noexcept; + + redis::IncRedis& incredis(); + + private: + bool use_socket_connection() const; + + std::unique_ptr m_incredis; + std::string m_hostname; + std::string m_socket; + uint32_t m_db; + uint16_t m_port; + }; + } //namespace test +} //namespac incredis diff --git a/test/integration/test_insert_retrieve.cpp b/test/integration/test_insert_retrieve.cpp new file mode 100644 index 0000000..0fd968f --- /dev/null +++ b/test/integration/test_insert_retrieve.cpp @@ -0,0 +1,81 @@ +#include "redis_connection_fixture.hpp" +#include "catch.hpp" +#include "incredis/incredis.hpp" +#include +#include + +using incredis::test::RedisConnectionFixture; + +TEST_CASE_METHOD(RedisConnectionFixture, "Batch insert elements in the db and expect to be able to read the same values back", "[set][get]") { + using redis::IncRedisBatch; + + REQUIRE_FALSE(not incredis().flushdb()); + REQUIRE(incredis().dbsize() == 0); + + auto batch = incredis().make_batch(); + const std::unordered_map sample_values { + {"個人と対話", "プロセスやツール"}, + {"動くソフトウェア", "包括的なドキュメント"}, + {"顧客との協調", "契約交渉"}, + {"変化への対応", "計画に従うこと"}, + {"erase", "erases elements"}, + {"swap", "swaps the contents"}, + {"extract", "extracts nodes from the container"}, + {"merge", "splices nodes from another container"} + }; + + for (auto& value : sample_values) { + batch.set(value.first, value.second, IncRedisBatch::ADD_NX); + } + REQUIRE_NOTHROW(batch.throw_if_failed()); + + REQUIRE(incredis().dbsize() == static_cast(sample_values.size())); + + { + redis::RedisInt scanned_items = 0; + for (auto& key : incredis().scan()) { + REQUIRE(sample_values.count(key) == 1); + redis::IncRedis::opt_string value = incredis().get(key); + REQUIRE_FALSE(not value); + REQUIRE(*value == sample_values.at(key)); + ++scanned_items; + } + REQUIRE(static_cast(sample_values.size()) == scanned_items); + } +} + +TEST_CASE_METHOD(RedisConnectionFixture, "Insert elements in the db and expect to be able to read the same values back", "[set][get]") { + using redis::IncRedisBatch; + + REQUIRE_FALSE(not incredis().flushdb()); + REQUIRE(incredis().dbsize() == 0); + + incredis().set("個人と対話", "プロセスやツール"); + incredis().set("動くソフトウェア", "包括的なドキュメント"); + incredis().set("顧客との協調", "契約交渉"); + incredis().set("変化への対応", "計画に従うこと"); + incredis().set("erase", "erases elements"); + incredis().set("swap", "swaps the contents"); + incredis().set("extract", "extracts nodes from the container"); + incredis().set("merge", "splices nodes from another container"); + + REQUIRE(incredis().dbsize() == 8); + + REQUIRE_FALSE(not incredis().get("個人と対話")); + REQUIRE_FALSE(not incredis().get("動くソフトウェア")); + REQUIRE_FALSE(not incredis().get("顧客との協調")); + REQUIRE_FALSE(not incredis().get("変化への対応")); + REQUIRE_FALSE(not incredis().get("erase")); + REQUIRE_FALSE(not incredis().get("swap")); + REQUIRE_FALSE(not incredis().get("extract")); + REQUIRE_FALSE(not incredis().get("merge")); + + REQUIRE(*incredis().get("個人と対話") == "プロセスやツール"); + REQUIRE(*incredis().get("動くソフトウェア") == "包括的なドキュメント"); + REQUIRE(*incredis().get("顧客との協調") == "契約交渉"); + REQUIRE(*incredis().get("変化への対応") == "計画に従うこと"); + REQUIRE(*incredis().get("erase") == "erases elements"); + REQUIRE(*incredis().get("swap") == "swaps the contents"); + REQUIRE(*incredis().get("extract") == "extracts nodes from the container"); + REQUIRE(*incredis().get("merge") == "splices nodes from another container"); +}