From e7f51c073a098b790b442c680cbee712a5dc2fef Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 11 Sep 2020 13:30:32 +0200 Subject: [PATCH] flush --- src/bin/memdump.cc | 2 +- testing/CMakeLists.txt | 4 +- testing/conf.h.in | 3 ++ testing/lib/MemcachedCluster.cpp | 27 ------------ testing/lib/MemcachedCluster.hpp | 32 +------------- testing/lib/ReturnMatcher.cpp | 28 ++++++++++++ testing/lib/ReturnMatcher.hpp | 35 +++++++++++++++ testing/lib/Shell.cpp | 71 ++++++++++++++++++++++++++++++ testing/lib/Shell.hpp | 16 +++++++ testing/lib/common.hpp | 28 ++++++++++++ testing/lib/random.cpp | 5 ++- testing/lib/random.hpp | 1 + testing/tests/bin/memcat.cpp | 59 +++++++++++++++++++++++++ testing/tests/bin/memcp.cpp | 75 ++++++++++++++++++++++++++++++++ testing/tests/bin/memdump.cpp | 66 ++++++++++++++++++++++++++++ testing/tests/bin/memerror.cpp | 37 ++++++++++++++++ testing/tests/bin/memexist.cpp | 58 ++++++++++++++++++++++++ 17 files changed, 486 insertions(+), 61 deletions(-) create mode 100644 testing/conf.h.in create mode 100644 testing/lib/ReturnMatcher.cpp create mode 100644 testing/lib/ReturnMatcher.hpp create mode 100644 testing/lib/Shell.cpp create mode 100644 testing/lib/Shell.hpp create mode 100644 testing/tests/bin/memcat.cpp create mode 100644 testing/tests/bin/memcp.cpp create mode 100644 testing/tests/bin/memdump.cpp create mode 100644 testing/tests/bin/memerror.cpp create mode 100644 testing/tests/bin/memexist.cpp diff --git a/src/bin/memdump.cc b/src/bin/memdump.cc index 8e12066b..cf99027b 100644 --- a/src/bin/memdump.cc +++ b/src/bin/memdump.cc @@ -70,7 +70,7 @@ int main(int argc, char *argv[]) { opt_servers= strdup(temp); } - else if (argc >= 1 and argv[--argc]) + else if (argc > 1 and argv[--argc]) { opt_servers= strdup(argv[argc]); } diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt index 145e6ada..9f2239a7 100644 --- a/testing/CMakeLists.txt +++ b/testing/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB_RECURSE TESTING_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) - +set(TESTING_ROOT ${CMAKE_CURRENT_BINARY_DIR}) +configure_file(conf.h.in conf.h @ONLY) add_executable(catch_main ${TESTING_SRC}) set_target_properties(catch_main PROPERTIES CXX_STANDARD 17) target_link_libraries(catch_main libhashkit libmemcached libmemcachedutil) +add_dependencies(catch_main ${CLIENTS}) diff --git a/testing/conf.h.in b/testing/conf.h.in new file mode 100644 index 00000000..e06af919 --- /dev/null +++ b/testing/conf.h.in @@ -0,0 +1,3 @@ +#pragma once + +#cmakedefine TESTING_ROOT "@TESTING_ROOT@" diff --git a/testing/lib/MemcachedCluster.cpp b/testing/lib/MemcachedCluster.cpp index 6b9af3e3..64736149 100644 --- a/testing/lib/MemcachedCluster.cpp +++ b/testing/lib/MemcachedCluster.cpp @@ -82,30 +82,3 @@ MemcachedCluster &MemcachedCluster::operator=(MemcachedCluster &&mc) { returns = ReturnMatcher{&memc}; return *this; } - -ReturnMatcher &ReturnMatcher::operator=(ReturnMatcher &&rm) { - memc = exchange(rm.memc, nullptr); - expected = rm.expected; - return *this; -} - -bool ReturnMatcher::match(const memcached_return_t &arg) const { - return arg == expected; -} - -ReturnMatcher ReturnMatcher::success() { - return ReturnMatcher{memc}; -} - -ReturnMatcher ReturnMatcher::operator()(memcached_return_t expected_) { - return ReturnMatcher{memc, expected_}; -} - -string ReturnMatcher::describe() const { - return "is " + to_string(expected) - + "\n actual: " + memcached_last_error_message(memc); -} - -ReturnMatcher::ReturnMatcher(ReturnMatcher &&rm) { - *this = move(rm); -} diff --git a/testing/lib/MemcachedCluster.hpp b/testing/lib/MemcachedCluster.hpp index 7286e69f..ea6aa346 100644 --- a/testing/lib/MemcachedCluster.hpp +++ b/testing/lib/MemcachedCluster.hpp @@ -2,38 +2,8 @@ #include "common.hpp" #include "Cluster.hpp" +#include "ReturnMatcher.hpp" -class ReturnMatcher : public Catch::MatcherBase { -public: - explicit ReturnMatcher(const memcached_st *memc_, memcached_return_t expected_ = MEMCACHED_SUCCESS) - : memc{memc_} - , expected{expected_} - {} - - ReturnMatcher(const ReturnMatcher &) = default; - ReturnMatcher &operator = (const ReturnMatcher &) = default; - - ReturnMatcher(ReturnMatcher &&rm); - ReturnMatcher &operator = (ReturnMatcher &&rm); - - bool match(const memcached_return_t &arg) const override; - ReturnMatcher success(); - ReturnMatcher operator () (memcached_return_t expected_); - -protected: - string describe() const override; - -private: - const memcached_st *memc; - memcached_return_t expected{MEMCACHED_SUCCESS}; -}; - -class LoneReturnMatcher { -public: - ReturnMatcher returns; - explicit LoneReturnMatcher(const memcached_st *memc) : returns{memc} - {} -}; class MemcachedCluster { public: diff --git a/testing/lib/ReturnMatcher.cpp b/testing/lib/ReturnMatcher.cpp new file mode 100644 index 00000000..863945e8 --- /dev/null +++ b/testing/lib/ReturnMatcher.cpp @@ -0,0 +1,28 @@ +#include "ReturnMatcher.hpp" + +ReturnMatcher &ReturnMatcher::operator=(ReturnMatcher &&rm) { + memc = exchange(rm.memc, nullptr); + expected = rm.expected; + return *this; +} + +bool ReturnMatcher::match(const memcached_return_t &arg) const { + return arg == expected; +} + +ReturnMatcher ReturnMatcher::success() { + return ReturnMatcher{memc}; +} + +ReturnMatcher ReturnMatcher::operator()(memcached_return_t expected_) { + return ReturnMatcher{memc, expected_}; +} + +string ReturnMatcher::describe() const { + return "is " + to_string(expected) + + "\n actual: " + memcached_last_error_message(memc); +} + +ReturnMatcher::ReturnMatcher(ReturnMatcher &&rm) { + *this = move(rm); +} diff --git a/testing/lib/ReturnMatcher.hpp b/testing/lib/ReturnMatcher.hpp new file mode 100644 index 00000000..286bc670 --- /dev/null +++ b/testing/lib/ReturnMatcher.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "testing/lib/common.hpp" + +class ReturnMatcher : public Catch::MatcherBase { +public: + explicit ReturnMatcher(const memcached_st *memc_, memcached_return_t expected_ = MEMCACHED_SUCCESS) + : memc{memc_} + , expected{expected_} + {} + + ReturnMatcher(const ReturnMatcher &) = default; + ReturnMatcher &operator = (const ReturnMatcher &) = default; + + ReturnMatcher(ReturnMatcher &&rm); + ReturnMatcher &operator = (ReturnMatcher &&rm); + + bool match(const memcached_return_t &arg) const override; + ReturnMatcher success(); + ReturnMatcher operator () (memcached_return_t expected_); + +protected: + string describe() const override; + +private: + const memcached_st *memc; + memcached_return_t expected{MEMCACHED_SUCCESS}; +}; + +class LoneReturnMatcher { +public: + ReturnMatcher returns; + explicit LoneReturnMatcher(const memcached_st *memc) : returns{memc} + {} +}; diff --git a/testing/lib/Shell.cpp b/testing/lib/Shell.cpp new file mode 100644 index 00000000..cd3017ac --- /dev/null +++ b/testing/lib/Shell.cpp @@ -0,0 +1,71 @@ +#include "Shell.hpp" + +#include +#include + +bool Shell::run(const string &command_, string &output) { + auto command = prepareCommand(command_); + auto *file = popen(command.c_str(), "r"); + + if (!file) { + perror("Shell::run popen()"); + return false; + } + + do { + char data[1U<<12U]; + auto read = fread(data, 1, sizeof(data), file); + + if (read) { + output.append(data, read); + } + if (ferror(file)) { + cerr << "Shell::run read(): " << strerror(ferror(file)); + break; + } + } while (!feof(file)); + + auto error = ferror(file); + auto status = pclose(file); + return !error && !status; +} + +bool Shell::run(const string &command) { + auto error = system(prepareCommand(command).c_str()); + if (error == -1) { + perror("Shell::run system()"); + return false; + } + return !error; +} + +Shell::Shell(bool redirect_stderr) +: redirect{redirect_stderr} +{ + if (!system(nullptr)) { + throw runtime_error("no shell available"); + } +} + +Shell::Shell(const string &prefix_, bool redirect_stderr) +: prefix{prefix_} +, redirect{redirect_stderr} +{ + if (!system(nullptr)) { + throw runtime_error("no shell available"); + } +} + +string Shell::prepareCommand(const string &command_) { + string command; + if (prefix.length()) { + command.append(prefix); + command.append("/"); + } + command.append(command_); + if (redirect) { + command.append(" 2>&1"); + } + INFO("Prepared command: " << command); + return command; +} diff --git a/testing/lib/Shell.hpp b/testing/lib/Shell.hpp new file mode 100644 index 00000000..03b60d72 --- /dev/null +++ b/testing/lib/Shell.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "testing/lib/common.hpp" + +class Shell { +public: + explicit Shell(bool redirect_stderr = true); + explicit Shell(const string &prefix, bool redirect_stderr = true); + bool run(const string &command, string &output); + bool run(const string &command); +private: + string prefix; + bool redirect; + + string prepareCommand(const string &command_); +}; diff --git a/testing/lib/common.hpp b/testing/lib/common.hpp index 9f957822..17199839 100644 --- a/testing/lib/common.hpp +++ b/testing/lib/common.hpp @@ -11,6 +11,7 @@ #include #include +#include "testing/conf.h" #include "testing/lib/catch.hpp" #include "testing/lib/random.hpp" @@ -55,6 +56,33 @@ inline memcached_return_t fetch_all_results(memcached_st *memc, unsigned int &ke return rc; } +#include +#include + +class Tempfile { +public: + explicit Tempfile(const char templ_[] = "memc.test.XXXXXX") { + *copy(S(templ_)+templ_, fn) = '\0'; + fd = mkstemp(fn); + } + ~Tempfile() { + close(fd); + unlink(fn); + } + int getFd() const { + return fd; + } + const char *getFn() const { + return fn; + } + bool put(const char *buf, size_t len) const { + return static_cast(len) == write(fd, buf, len); + } +private: + char fn[80]; + int fd; +}; + class MemcachedPtr { public: memcached_st *memc; diff --git a/testing/lib/random.cpp b/testing/lib/random.cpp index a238c743..f312d8bd 100644 --- a/testing/lib/random.cpp +++ b/testing/lib/random.cpp @@ -33,6 +33,10 @@ unsigned random_port() { goto retry; } +string random_port_string(const string &) { + return to_string(random_port()); +} + string random_socket(const string &prefix) { return prefix + to_string(random_num(1U, UINT32_MAX)) + "@" + to_string(getpid()) + ".sock"; } @@ -74,4 +78,3 @@ pair random_ascii_pair(size_t minlen, size_t maxlen) { random_ascii_string(random_num(minlen, maxlen)) }; } - diff --git a/testing/lib/random.hpp b/testing/lib/random.hpp index 208b09f4..06fc5ef6 100644 --- a/testing/lib/random.hpp +++ b/testing/lib/random.hpp @@ -13,6 +13,7 @@ template enable_if_t, T> random_num(T min, T max); unsigned random_port(); +string random_port_string(const string &); char random_ascii(char min = '!', char max = '~'); string random_ascii_string(size_t len, char min = '!', char max = '~'); diff --git a/testing/tests/bin/memcat.cpp b/testing/tests/bin/memcat.cpp new file mode 100644 index 00000000..e09bf28e --- /dev/null +++ b/testing/tests/bin/memcat.cpp @@ -0,0 +1,59 @@ +#include "testing/lib/common.hpp" +#include "testing/lib/Shell.hpp" +#include "testing/lib/Server.hpp" +#include "testing/lib/Retry.hpp" +#include "testing/lib/ReturnMatcher.hpp" + +using Catch::Matchers::Contains; + +TEST_CASE("memcat") { + Shell sh{string{TESTING_ROOT "/../src/bin"}}; + + SECTION("no servers provided") { + string output; + REQUIRE_FALSE(sh.run("memcat", output)); + REQUIRE(output == "No servers provided\n"); + } + + SECTION("--help") { + string output; + REQUIRE(sh.run("memcat --help", output)); + REQUIRE_THAT(output, Contains("memcat")); + REQUIRE_THAT(output, Contains("v1")); + REQUIRE_THAT(output, Contains("help")); + REQUIRE_THAT(output, Contains("version")); + REQUIRE_THAT(output, Contains("option")); + REQUIRE_THAT(output, Contains("--")); + REQUIRE_THAT(output, Contains("=")); + } + + SECTION("with server") { + Server server{"memcached"}; + MemcachedPtr memc; + LoneReturnMatcher test{*memc}; + + server.start(); + Retry{[&server] { return server.isListening(); }}(); + auto port = get(server.getSocketOrPort()); + auto comm = "memcat --servers=localhost:" + to_string(port) + " "; + + REQUIRE_SUCCESS(memcached_server_add(*memc, "localhost", port)); + + SECTION("found") { + REQUIRE_SUCCESS(memcached_set(*memc, S("memcat"), S("MEMCAT-SET"), 0, 0)); + + string output; + REQUIRE(sh.run(comm + "memcat", output)); + REQUIRE(output == "MEMCAT-SET\n"); + } + + SECTION("not found") { + memcached_delete(*memc, S("memcat"), 0); + + string output; + REQUIRE_FALSE(sh.run(comm + "memcat", output)); + REQUIRE_THAT(output, !Contains("MEMCAT-SET")); + REQUIRE_THAT(output, Contains("NOT FOUND")); + } + } +} diff --git a/testing/tests/bin/memcp.cpp b/testing/tests/bin/memcp.cpp new file mode 100644 index 00000000..6c69a502 --- /dev/null +++ b/testing/tests/bin/memcp.cpp @@ -0,0 +1,75 @@ +#include "testing/lib/common.hpp" +#include "testing/lib/Shell.hpp" +#include "testing/lib/Server.hpp" +#include "testing/lib/Retry.hpp" +#include "testing/lib/ReturnMatcher.hpp" + +using Catch::Matchers::Contains; + +TEST_CASE("memcp") { + Shell sh{string{TESTING_ROOT "/../src/bin"}}; + + SECTION("no servers provided") { + string output; + REQUIRE_FALSE(sh.run("memcp nonexistent", output)); + REQUIRE(output == "No Servers provided\n"); + } + + SECTION("--help") { + string output; + REQUIRE(sh.run("memcp --help", output)); + REQUIRE_THAT(output, Contains("memcp")); + REQUIRE_THAT(output, Contains("v1")); + REQUIRE_THAT(output, Contains("help")); + REQUIRE_THAT(output, Contains("version")); + REQUIRE_THAT(output, Contains("option")); + REQUIRE_THAT(output, Contains("--")); + REQUIRE_THAT(output, Contains("=")); + } + + SECTION("with server") { + Server server{"memcached"}; + MemcachedPtr memc; + LoneReturnMatcher test{*memc}; + + server.start(); + Retry{[&server] { return server.isListening(); }}(); + auto port = get(server.getSocketOrPort()); + auto comm = "memcp --servers=localhost:" + to_string(port) + " "; + + REQUIRE_SUCCESS(memcached_server_add(*memc, "localhost", port)); + + SECTION("okay") { + Tempfile temp; + temp.put(S("123")); + + string output; + REQUIRE(sh.run(comm + temp.getFn(), output)); + REQUIRE(output == ""); + + size_t len; + memcached_return_t rc; + Malloced val(memcached_get(*memc, S(temp.getFn()), &len, nullptr, &rc)); + + REQUIRE(*val); + REQUIRE_SUCCESS(rc); + REQUIRE(string(*val, len) == "123"); + } + + SECTION("connection failure") { + server.signal(SIGKILL); + + Tempfile temp; + + string output; + REQUIRE_FALSE(sh.run(comm + temp.getFn(), output)); + REQUIRE_THAT(output, Contains("CONNECTION FAILURE")); + } + + SECTION("file not found") { + string output; + REQUIRE_FALSE(sh.run(comm + "nonexistent", output)); + REQUIRE_THAT(output, Contains("No such file or directory")); + } + } +} diff --git a/testing/tests/bin/memdump.cpp b/testing/tests/bin/memdump.cpp new file mode 100644 index 00000000..fe302335 --- /dev/null +++ b/testing/tests/bin/memdump.cpp @@ -0,0 +1,66 @@ +#include "testing/lib/common.hpp" +#include "testing/lib/Shell.hpp" +#include "testing/lib/Server.hpp" +#include "testing/lib/Retry.hpp" +#include "testing/lib/ReturnMatcher.hpp" + +using Catch::Matchers::Contains; + +TEST_CASE("memdump") { + Shell sh{string{TESTING_ROOT "/../src/bin"}}; + + SECTION("no servers provided") { + string output; + REQUIRE_FALSE(sh.run("memdump", output)); + REQUIRE(output == "No Servers provided\n"); + } + + SECTION("--help") { + string output; + REQUIRE(sh.run("memdump --help", output)); + REQUIRE_THAT(output, Contains("memdump")); + REQUIRE_THAT(output, Contains("v1")); + REQUIRE_THAT(output, Contains("help")); + REQUIRE_THAT(output, Contains("version")); + REQUIRE_THAT(output, Contains("option")); + REQUIRE_THAT(output, Contains("--")); + REQUIRE_THAT(output, Contains("=")); + } + + SECTION("with server") { + Server server{"memcached"}; + MemcachedPtr memc; + LoneReturnMatcher test{*memc}; + + server.start(); + Retry{[&server] { return server.isListening(); }}(); + auto port = get(server.getSocketOrPort()); + auto comm = "memdump --servers=localhost:" + to_string(port) + " "; + + REQUIRE_SUCCESS(memcached_server_add(*memc, "localhost", port)); + + SECTION("okay") { + + REQUIRE_SUCCESS(memcached_set(*memc, S("key1"), S("val1"), 0, 0)); + REQUIRE_SUCCESS(memcached_set(*memc, S("key2"), S("val2"), 0, 0)); + + string output; + REQUIRE(sh.run(comm, output)); + REQUIRE_THAT(output, Contains("key1") && Contains("key2")); + } + + SECTION("connection failure") { + server.signal(SIGKILL); + + string output; + REQUIRE_FALSE(sh.run(comm + "-v", output)); + REQUIRE_THAT(output, Contains("CONNECTION FAILURE") || Contains("SERVER HAS FAILED")); + } + + SECTION("empty") { + string output; + REQUIRE(sh.run(comm, output)); + REQUIRE(output == ""); + } + } +} diff --git a/testing/tests/bin/memerror.cpp b/testing/tests/bin/memerror.cpp new file mode 100644 index 00000000..26bb6cf0 --- /dev/null +++ b/testing/tests/bin/memerror.cpp @@ -0,0 +1,37 @@ +#include "testing/lib/common.hpp" +#include "testing/lib/Shell.hpp" +#include "testing/lib/Server.hpp" +#include "testing/lib/Retry.hpp" +#include "testing/lib/ReturnMatcher.hpp" + +using Catch::Matchers::Contains; + +TEST_CASE("memerror") { + Shell sh{string{TESTING_ROOT "/../src/bin"}}; + + SECTION("--help") { + string output; + REQUIRE(sh.run("memerror --help", output)); + REQUIRE_THAT(output, Contains("memerror")); + REQUIRE_THAT(output, Contains("v1")); + REQUIRE_THAT(output, Contains("help")); + REQUIRE_THAT(output, Contains("version")); + REQUIRE_THAT(output, Contains("option")); + REQUIRE_THAT(output, Contains("--")); + REQUIRE_THAT(output, Contains("=")); + } + + SECTION("valid error codes") { + for (underlying_type_t rc = MEMCACHED_SUCCESS; rc < MEMCACHED_MAXIMUM_RETURN; ++rc) { + string output; + REQUIRE(sh.run("memerror " + to_string(rc), output)); + CHECK(output == memcached_strerror(nullptr, static_cast(rc)) + string{"\n"}); + } + } + + SECTION("unknown error code") { + string output; + REQUIRE_FALSE(sh.run("memerror " + to_string(MEMCACHED_MAXIMUM_RETURN), output)); + REQUIRE_THAT(output, Contains("INVALID memcached_return_t")); + } +} diff --git a/testing/tests/bin/memexist.cpp b/testing/tests/bin/memexist.cpp new file mode 100644 index 00000000..d8877909 --- /dev/null +++ b/testing/tests/bin/memexist.cpp @@ -0,0 +1,58 @@ +#include "testing/lib/common.hpp" +#include "testing/lib/Shell.hpp" +#include "testing/lib/Server.hpp" +#include "testing/lib/Retry.hpp" +#include "testing/lib/ReturnMatcher.hpp" + +using Catch::Matchers::Contains; + +TEST_CASE("memexist") { + Shell sh{string{TESTING_ROOT "/../src/bin"}}; + + SECTION("no servers provided") { + string output; + REQUIRE_FALSE(sh.run("memexist", output)); + REQUIRE(output == "No Servers provided\n"); + } + + SECTION("--help") { + string output; + REQUIRE(sh.run("memexist --help", output)); + REQUIRE_THAT(output, Contains("memexist")); + REQUIRE_THAT(output, Contains("v1")); + REQUIRE_THAT(output, Contains("help")); + REQUIRE_THAT(output, Contains("version")); + REQUIRE_THAT(output, Contains("option")); + REQUIRE_THAT(output, Contains("--")); + REQUIRE_THAT(output, Contains("=")); + } + + SECTION("with server") { + Server server{"memcached", {"-p", random_port_string}}; + MemcachedPtr memc; + LoneReturnMatcher test{*memc}; + + server.start(); + Retry{[&server] { return server.isListening(); }}(); + auto port = get(server.getSocketOrPort()); + auto comm = "memexist --servers=localhost:" + to_string(port) + " "; + + REQUIRE_SUCCESS(memcached_server_add(*memc, "localhost", port)); + + SECTION("found") { + REQUIRE_SUCCESS(memcached_set(*memc, S("memexist"), S("MEMEXIST-SET"), 0, 0)); + + string output; + REQUIRE(sh.run(comm + "memexist", output)); + REQUIRE(output.empty()); + } + + SECTION("not found") { + memcached_delete(*memc, S("memexist"), 0); + + string output; + REQUIRE_FALSE(sh.run(comm + "memexist", output)); + REQUIRE(output.empty()); + } + } +} -- 2.30.2