-add_executable(catch_main
- main.cpp
-
- lib/common.cpp
-
- lib/Cluster.cpp
- lib/Connection.cpp
- lib/ForkAndExec.cpp
- lib/MemcachedCluster.cpp
- lib/Server.cpp
- lib/Retry.cpp
-
- lib.cpp
- hashkit/basic.cpp
- memcached/basic.cpp
- memcached/callbacks.cpp
- memcached/servers.cpp
- memcached/dump.cpp
- memcached/encoding_key.cpp
- memcached/exist.cpp
- memcached/haldenbrand.cpp
- )
-
-set_target_properties(catch_main PROPERTIES
- CXX_STANDARD 17)
+file(GLOB_RECURSE TESTING_SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
+add_executable(catch_main ${TESTING_SRC})
+set_target_properties(catch_main PROPERTIES CXX_STANDARD 17)
target_link_libraries(catch_main libhashkit libmemcached)
+++ /dev/null
-#include "../lib/common.hpp"
-
-#include "libhashkit-1.0/hashkit.hpp"
-
-static const char *input[] = {
- "apple",
- "beat",
- "carrot",
- "daikon",
- "eggplant",
- "flower",
- "green",
- "hide",
- "ick",
- "jack",
- "kick",
- "lime",
- "mushrooms",
- "nectarine",
- "orange",
- "peach",
- "quant",
- "ripen",
- "strawberry",
- "tang",
- "up",
- "volumne",
- "when",
- "yellow",
- "zip",
-};
-
-static const uint32_t output[][sizeof(input)/sizeof(*input)] = {
- // one_at_a_time
- { 2297466611U, 3902465932U, 469785835U, 1937308741U,
- 261917617U, 3785641677U, 1439605128U, 1649152283U,
- 1493851484U, 1246520657U, 2221159044U, 1973511823U,
- 384136800U, 214358653U, 2379473940U, 4269788650U,
- 2864377005U, 2638630052U, 427683330U, 990491717U,
- 1747111141U, 792127364U, 2599214128U, 2553037199U,
- 2509838425U },
-
- // md5
- { 3195025439U, 2556848621U, 3724893440U, 3332385401U,
- 245758794U, 2550894432U, 121710495U, 3053817768U,
- 1250994555U, 1862072655U, 2631955953U, 2951528551U,
- 1451250070U, 2820856945U, 2060845566U, 3646985608U,
- 2138080750U, 217675895U, 2230934345U, 1234361223U,
- 3968582726U, 2455685270U, 1293568479U, 199067604U,
- 2042482093U },
-
- // crc
- { 10542U, 22009U, 14526U, 19510U, 19432U, 10199U, 20634U,
- 9369U, 11511U, 10362U, 7893U, 31289U, 11313U, 9354U,
- 7621U, 30628U, 15218U, 25967U, 2695U, 9380U,
- 17300U, 28156U, 9192U, 20484U, 16925U },
-
- // fnv1_64
- { 473199127U, 4148981457U, 3971873300U, 3257986707U,
- 1722477987U, 2991193800U, 4147007314U, 3633179701U,
- 1805162104U, 3503289120U, 3395702895U, 3325073042U,
- 2345265314U, 3340346032U, 2722964135U, 1173398992U,
- 2815549194U, 2562818319U, 224996066U, 2680194749U,
- 3035305390U, 246890365U, 2395624193U, 4145193337U,
- 1801941682U },
-
- // fnv1a_64
- { 1488911807U, 2500855813U, 1510099634U, 1390325195U,
- 3647689787U, 3241528582U, 1669328060U, 2604311949U,
- 734810122U, 1516407546U, 560948863U, 1767346780U,
- 561034892U, 4156330026U, 3716417003U, 3475297030U,
- 1518272172U, 227211583U, 3938128828U, 126112909U,
- 3043416448U, 3131561933U, 1328739897U, 2455664041U,
- 2272238452U },
-
- // fnv1_32
- { 67176023U, 1190179409U, 2043204404U, 3221866419U,
- 2567703427U, 3787535528U, 4147287986U, 3500475733U,
- 344481048U, 3865235296U, 2181839183U, 119581266U,
- 510234242U, 4248244304U, 1362796839U, 103389328U,
- 1449620010U, 182962511U, 3554262370U, 3206747549U,
- 1551306158U, 4127558461U, 1889140833U, 2774173721U,
- 1180552018U },
-
- // fnv1a_32
- { 280767167U, 2421315013U, 3072375666U, 855001899U,
- 459261019U, 3521085446U, 18738364U, 1625305005U,
- 2162232970U, 777243802U, 3323728671U, 132336572U,
- 3654473228U, 260679466U, 1169454059U, 2698319462U,
- 1062177260U, 235516991U, 2218399068U, 405302637U,
- 1128467232U, 3579622413U, 2138539289U, 96429129U,
- 2877453236U },
-
- // hsieh
-#ifdef HAVE_HSIEH_HASH
- { 3738850110U, 3636226060U, 3821074029U, 3489929160U, 3485772682U, 80540287U,
- 1805464076U, 1895033657U, 409795758U, 979934958U, 3634096985U, 1284445480U,
- 2265380744U, 707972988U, 353823508U, 1549198350U, 1327930172U, 9304163U,
- 4220749037U, 2493964934U, 2777873870U, 2057831732U, 1510213931U, 2027828987U,
- 3395453351U },
-#else
- { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
-#endif
-
- // murmur
-#ifdef HAVE_MURMUR_HASH
- // murmur
- { 4142305122U, 734504955U, 3802834688U, 4076891445U,
- 387802650U, 560515427U, 3274673488U, 3150339524U,
- 1527441970U, 2728642900U, 3613992239U, 2938419259U,
- 2321988328U, 1145154116U, 4038540960U, 2224541613U,
- 264013145U, 3995512858U, 2400956718U, 2346666219U,
- 926327338U, 442757446U, 1770805201U, 560483147U,
- 3902279934U },
- // murmur3
- { 1120212521U, 1448785489U, 4186307405U, 2686268514U,
- 444808887U, 221750260U, 3074673162U, 1946933257U,
- 2826416675U, 2430719166U, 3200429559U, 297894347U,
- 732888124U, 4050076964U, 3298336176U, 1336207361U,
- 810553576U, 3748182674U, 3860119212U, 3439537197U,
- 3044240981U, 1464271804U, 3896193724U, 2915115798U,
- 1702843840U },
-#else
- { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
- { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
-#endif
-
- // jenkins
- { 1442444624U, 4253821186U, 1885058256U, 2120131735U,
- 3261968576U, 3515188778U, 4232909173U, 4288625128U,
- 1812047395U, 3689182164U, 2502979932U, 1214050606U,
- 2415988847U, 1494268927U, 1025545760U, 3920481083U,
- 4153263658U, 3824871822U, 3072759809U, 798622255U,
- 3065432577U, 1453328165U, 2691550971U, 3408888387U,
- 2629893356U }
-};
-
-TEST_CASE("hashkit") {
- hashkit_st st, *hp = hashkit_create(nullptr);
- Hashkit stack;
- Hashkit *heap = new Hashkit;
-
- REQUIRE(hashkit_create(&st));
- REQUIRE(hp);
-
- SECTION("can copy") {
- Hashkit stack_copy(stack);
- Hashkit *heap_copy(heap);
- hashkit_st st_copy, *st_ptr;
-
- (void) stack_copy;
- (void) heap_copy;
-
- st_ptr = hashkit_clone(&st_copy, &st);
- REQUIRE(st_ptr == &st_copy);
- REQUIRE(hashkit_compare(st_ptr, &st_copy));
-
- SUCCEED("OK");
- }
-
- SECTION("can assign") {
- Hashkit stack_copy;
-
- stack_copy = stack;
- (void) stack_copy;
-
- SUCCEED("OK");
- }
-
- SECTION("can digest default") {
- REQUIRE(2297466611U == stack.digest(S("apple")));
- REQUIRE(2297466611U == hashkit_digest(&st, S("apple")));
- }
-
- SECTION("can set hash function") {
- for (int f = HASHKIT_HASH_DEFAULT; f < HASHKIT_HASH_MAX; ++f) {
- auto h = static_cast<hashkit_hash_algorithm_t>(f);
-
- if (h == HASHKIT_HASH_CUSTOM) {
- continue;
- }
- if (!libhashkit_has_algorithm(h)) {
- WARN("hashkit algorithm not enabled: " << libhashkit_string_hash(h) << " (" << f << ")");
- continue;
- }
-
- REQUIRE(HASHKIT_SUCCESS == stack.set_function(h));
- REQUIRE(HASHKIT_SUCCESS == hashkit_set_function(&st, h));
-
- SECTION("can digest set hash function") {
- auto n = 0;
-
- for (auto i : input) {
- CHECK(output[f][n] == stack.digest(S(i)));
- CHECK(output[f][n] == hashkit_digest(&st, S(i)));
- CHECK(output[f][n] == libhashkit_digest(S(i), h));
- ++n;
- }
- }
- }
- }
-
- SECTION("is comparable") {
- REQUIRE(*heap == stack);
- REQUIRE(hashkit_compare(&st, hp));
-
- stack.set_function(HASHKIT_HASH_MD5);
- hashkit_set_function(&st, HASHKIT_HASH_MD5);
-
- REQUIRE_FALSE(*heap == stack);
- REQUIRE_FALSE(hashkit_compare(&st, hp));
- }
-
- delete heap;
- hashkit_free(&st);
- hashkit_free(hp);
-}
+++ /dev/null
-#include "lib/common.hpp"
-#include "lib/Cluster.hpp"
-#include "lib/Retry.hpp"
-#include "lib/Server.hpp"
-
-TEST_CASE("lib/Server") {
- Server server{"memcached"};
-
- SECTION("starts and listens") {
-
- REQUIRE(server.start().has_value());
-
- Retry server_is_listening{[&server] {
- return server.isListening();
- }};
- REQUIRE(server_is_listening());
-
- SECTION("stops") {
-
- REQUIRE(server.stop());
-
- SECTION("is waitable") {
-
- REQUIRE(server.wait());
-
- SECTION("stopped") {
-
- REQUIRE_FALSE(server.check());
- }
- }
- }
- }
-}
-
-TEST_CASE("lib/Cluster") {
- Cluster cluster{Server{"memcached", {
- random_socket_or_port_arg(),
- }}};
-
- SECTION("starts and listens") {
-
- REQUIRE(cluster.start());
-
- Retry cluster_is_listening{[&cluster] {
- return cluster.isListening();
- }};
- REQUIRE(cluster_is_listening());
-
- SECTION("stops") {
-
- cluster.stop();
- cluster.wait();
-
- SECTION("stopped") {
-
- REQUIRE(cluster.isStopped());
- }
- }
- }
-}
#include "Cluster.hpp"
+#include "Retry.hpp"
#include <sys/wait.h>
-Cluster::Cluster(Server &&serv, uint16_t cnt)
+Cluster::Cluster(Server serv, uint16_t cnt)
: count{cnt}
-, proto{forward<Server>(serv)}
+, proto{move(serv)}
{
if (!cnt) {
count = thread::hardware_concurrency()/2 ?: 4;
bool started = true;
for (auto &server : cluster) {
- auto pid = server.start();
- if (pid.has_value()) {
- pids[*pid] = &server;
- } else {
+ if (!startServer(server)) {
started = false;
}
}
bool Cluster::isListening() {
for (auto &server : cluster) {
- if (!server.isListening()) {
- // zombie?
- auto old_pid = server.getPid();
- if (server.tryWait()) {
- pids.erase(old_pid);
- auto pid = server.start();
- if (pid.has_value()) {
- pids[*pid] = &server;
+ Retry server_is_listening{[&] {
+ if (!server.isListening()) {
+ // zombie?
+ auto old_pid = server.getPid();
+ if (server.tryWait()) {
+ cerr << "zombie collected (old pid=" << old_pid << "): " << server << "\n";
+ pids.erase(old_pid);
+ // restart
+ startServer(server);
+ }
+ if (!server.isListening()) {
+ return false;
}
}
- return server.isListening();
+ return true;
+ }};
+ if (!server_is_listening()) {
+ return false;
}
}
return true;
}
+bool Cluster::startServer(Server &server) {
+ auto pid = server.start();
+ if (pid.has_value()) {
+ pids[*pid] = &server;
+ return true;
+ }
+ return false;
+}
+
void Cluster::wait() {
siginfo_t inf;
}
}
}
-
class Cluster {
public:
explicit
- Cluster(Server &&serv, uint16_t cnt = 0);
+ Cluster(Server serv, uint16_t cnt = 0);
~Cluster();
Server proto;
vector<Server> cluster;
map<pid_t, Server *> pids;
+
+ bool startServer(Server &server);
};
REQUIRE(MEMCACHED_SUCCESS == memcached_behavior_set(&memc,
MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, enable));
}
+
+MemcachedCluster::MemcachedCluster(MemcachedCluster &&mc)
+ : cluster{Server{}}
+{
+ *this = move(mc);
+}
+
+MemcachedCluster &MemcachedCluster::operator=(MemcachedCluster &&mc) {
+ cluster = move(mc.cluster);
+ memcached_clone(&memc, &mc.memc);
+ 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);
+}
ReturnMatcher(const ReturnMatcher &) = default;
ReturnMatcher &operator = (const ReturnMatcher &) = default;
- ReturnMatcher(ReturnMatcher &&rm) {
- *this = move(rm);
- }
- ReturnMatcher &operator = (ReturnMatcher &&rm) {
- memc = exchange(rm.memc, nullptr);
- expected = rm.expected;
- return *this;
- }
-
- bool match(const memcached_return_t &arg) const override {
- return arg == expected;
- }
-
- ReturnMatcher success() {
- return ReturnMatcher{memc};
- }
-
- ReturnMatcher operator () (memcached_return_t expected_) {
- return ReturnMatcher{memc, expected_};
- }
+ 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 {
- return string{"is "}
- + to_string(expected)
- + " (" + memcached_strerror(memc, expected) + ") "
- + "\n\tlast error: "
- + memcached_last_error_message(memc);
- }
+ string describe() const override;
private:
const memcached_st *memc;
- memcached_return_t expected;
+ memcached_return_t expected{MEMCACHED_SUCCESS};
+};
+
+class LoneReturnMatcher {
+public:
+ ReturnMatcher returns;
+ explicit LoneReturnMatcher(const memcached_st *memc) : returns{memc}
+ {}
};
class MemcachedCluster {
MemcachedCluster(const MemcachedCluster &) = delete;
MemcachedCluster &operator=(const MemcachedCluster &) = delete;
- MemcachedCluster(MemcachedCluster &&mc)
- : cluster{Server{}}
- {
- *this = move(mc);
- };
- MemcachedCluster &operator=(MemcachedCluster &&mc)
- {
- cluster = move(mc.cluster);
- memcached_clone(&memc, &mc.memc);
- returns = ReturnMatcher{&memc};
- return *this;
- }
+ MemcachedCluster(MemcachedCluster &&mc);;
+ MemcachedCluster &operator=(MemcachedCluster &&mc);
void enableBinaryProto(bool enable = true);
void flush();
#include "Retry.hpp"
-Retry::Retry(predicate &&pred_, unsigned int max_, chrono::milliseconds sleep_for_)
+Retry::Retry(predicate pred_, unsigned int max_, chrono::milliseconds sleep_for_)
: max{max_}
, sleep_for{sleep_for_}
-, pred{forward<predicate>(pred_)}
+, pred{move(pred_)}
{}
bool Retry::operator()() {
using predicate = function<bool()>;
- explicit Retry(predicate &&pred_, unsigned max_ = 10, chrono::milliseconds sleep_for_ = 20ms);
+ explicit Retry(predicate pred_, unsigned max_ = 10, chrono::milliseconds sleep_for_ = 100ms);
bool operator () ();
#include <sys/wait.h>
#include <unistd.h>
-Server::Server(string &&binary_, Server::argv_t &&args_)
- : binary{forward<string>(binary_)}
- , args{forward<argv_t>(args_)}
+Server::Server(string binary_, Server::argv_t args_)
+ : binary{move(binary_)}
+ , args{move(args_)}
{}
Server::~Server() {
using argv_t = vector<variant<arg_t, arg_pair_t>>;
explicit
- Server(string &&binary_ = "false", argv_t && args_ = {});
+ Server(string binary_ = "false", argv_t args_ = {});
~Server();
vector<char *> createArgv();
optional<string> handleArg(vector<char *> &arr, const string &arg, const arg_func_t &next_arg);
};
+
+inline ostream &operator << (ostream &out, const socket_or_port_t sop) {
+ if (holds_alternative<string>(sop)) {
+ out << get<string>(sop);
+ } else {
+ out << get<int>(sop);
+ }
+ return out;
+}
+
+inline ostream &operator << (ostream &out, const Server &server) {
+ out << "Server{binary=" << server.getBinary() << ",pid=" << server.getPid() << ",conn=" << server.getSocketOrPort() << "}";
+ return out;
+}
#include "Connection.hpp"
#include <cstdlib>
-#include <sys/random.h>
-#include <unistd.h>
-
-unsigned random_num(unsigned min, unsigned max) {
- unsigned p;
- getrandom(&p, sizeof(p), 0);
- return (p % (max - min + 1)) + min;
-}
-
-unsigned random_port() {
- retry:
- int port = random_num(2<<9, 2<<15);
- Connection conn(port);
-
- if (!conn.open()) {
- return port;
- }
- if (!conn.isOpen()) {
- return port;
- }
- goto retry;
-}
-
-string random_socket() {
- return "/tmp/libmc." + to_string(random_num(1, UINT32_MAX)) + "@" + to_string(getpid()) + ".sock";
-}
-
-string random_socket_or_port_string(const string &what) {
- if (what == "-s") {
- return random_socket();
- }
-
- return to_string(random_port());
-}
-
-string random_socket_or_port_flag(const string &binary) {
- (void) binary;
- return random_num(0, 1) ? "-p" : "-s";
-}
const char *getenv_else(const char *var, const char *defval) {
auto val = getenv(var);
#include <variant>
#include <vector>
-#include "../lib/catch.hpp"
+#include "testing/lib/catch.hpp"
+#include "testing/lib/random.hpp"
#include "libmemcached/memcached.h"
* Useful macros for testing
*/
#define S(s) (s),strlen(s)
+#define DECLARE_STREQUAL static auto strequal = equal_to<string>();
#define LOOPED_SECTION(tests) \
for (auto &[name, test] : tests) DYNAMIC_SECTION("test " << name)
-#define REQUIRE_SUCCESS(rc) REQUIRE_THAT(rc, test.returns.success())
-#define REQUIRE_RC(rc, call) REQUIRE_THAT(call, test.returns(rc))
-
+#define REQUIRE_SUCCESS(rc) do { \
+ INFO("expected: SUCCESS"); \
+ REQUIRE_THAT(rc, test.returns.success()); \
+ } while(0)
+#define REQUIRE_RC(rc, call) do { \
+ INFO("expected: " << memcached_strerror(nullptr, rc)); \
+ REQUIRE_THAT(call, test.returns(rc)); \
+ } while(0)
const char *getenv_else(const char *var, const char *defval);
-unsigned random_num(unsigned min, unsigned max);
-unsigned random_port();
-string random_socket();
-string random_socket_or_port_string(const string &what);
-string random_socket_or_port_flag(const string &binary);
-
-inline auto random_socket_or_port_arg() {
- return make_pair(&random_socket_or_port_flag, &random_socket_or_port_string);
-}
inline memcached_return_t fetch_all_results(memcached_st *memc, unsigned int &keys_returned, memcached_return_t &rc) {
keys_returned = 0;
memcached_result_st *result = nullptr;
- while ((result = memcached_fetch_result(memc, result, &rc)))
- {
+ while ((result = memcached_fetch_result(memc, result, &rc))) {
REQUIRE(MEMCACHED_SUCCESS == rc);
keys_returned += 1;
}
}
};
+template<class T>
class Malloced {
- void *ptr;
+ T *ptr;
public:
- Malloced(void *ptr_)
+ Malloced(T *ptr_)
: ptr{ptr_}
{}
~Malloced() {
- free(ptr);
+ if(ptr)
+ free(ptr);
}
- void *operator *() {
+ auto operator *() {
return ptr;
}
};
--- /dev/null
+#include "testing/lib/random.hpp"
+#include "testing/lib/Connection.hpp"
+
+#include <chrono>
+#include <random>
+
+#include <unistd.h> // getpid()
+
+
+template<typename T>
+enable_if_t<is_integral_v<T>, T> random_num(T min, T max) {
+ using namespace chrono;
+ using rnd = mt19937;
+ using dst = uniform_int_distribution<T>;
+
+ auto time = duration_cast<microseconds>(system_clock::now().time_since_epoch());
+ auto seed = static_cast<rnd::result_type>(time.count() % numeric_limits<T>::max());
+ auto rgen = rnd{seed};
+ return dst(min, max)(rgen);
+}
+
+unsigned random_port() {
+ retry:
+ auto port = random_num(2<<9, 2<<15);
+ Connection conn(port);
+
+ if (!conn.open()) {
+ return port;
+ }
+ if (!conn.isOpen()) {
+ return port;
+ }
+ goto retry;
+}
+
+string random_socket(const string &prefix) {
+ return prefix + to_string(random_num(1U, UINT32_MAX)) + "@" + to_string(getpid()) + ".sock";
+}
+
+string random_socket_or_port_string(const string &what) {
+ if (what == "-s") {
+ return random_socket();
+ }
+
+ return to_string(random_port());
+}
+
+string random_socket_or_port_flag(const string &binary) {
+ (void) binary;
+ return random_num(0, 1) ? "-p" : "-s";
+}
+
+char random_ascii(char min, char max) {
+ return static_cast<char>(random_num(min, max));
+}
+
+string random_ascii_string(size_t len, char min, char max) {
+ string s;
+ s.reserve(len + 1);
+
+ for (size_t rem = 0; rem < len; ++rem) {
+ s += random_ascii(min, max);
+ }
+
+ s[len] = '\0';
+ assert(strlen(s.c_str()) == s.size());
+
+ return s;
+}
+
+pair<string, string> random_ascii_pair(size_t minlen, size_t maxlen) {
+ return {
+ random_ascii_string(random_num(minlen, maxlen)),
+ random_ascii_string(random_num(minlen, maxlen))
+ };
+}
+
--- /dev/null
+#pragma once
+
+#include <cstddef>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+using namespace std;
+
+using kv_pair = pair<string, string>;
+
+template<typename T>
+enable_if_t<is_integral_v<T>, T> random_num(T min, T max);
+
+unsigned random_port();
+
+char random_ascii(char min = '!', char max = '~');
+string random_ascii_string(size_t len, char min = '!', char max = '~');
+kv_pair random_ascii_pair(size_t minlen = 1<<2, size_t maxlen = 1<<10);
+
+template<template <typename> class Container>
+auto random_ascii_pairs(size_t count, size_t minlen = 1<<2, size_t maxlen = 1<<10) {
+ Container<kv_pair> v;
+
+ v.reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ v.emplace_back(random_ascii_pair(minlen, maxlen));
+ }
+
+ return v;
+}
+
+string random_socket(const string &prefix = "/tmp/libmc.");
+string random_socket_or_port_string(const string &what);
+string random_socket_or_port_flag(const string &binary);
+
+inline auto random_socket_or_port_arg() {
+ return make_pair(&random_socket_or_port_flag, &random_socket_or_port_string);
+}
+++ /dev/null
-#include "../lib/common.hpp"
-
-#include "libmemcached/is.h"
-
-TEST_CASE("memcached basic") {
- memcached_st memc, *memc_ptr;
-
- memc_ptr = memcached_create(&memc);
-
- REQUIRE(memc_ptr);
- REQUIRE(memc_ptr == &memc);
-
- SECTION("can be cloned") {
- memc_ptr = memcached_clone(nullptr, &memc);
- REQUIRE(memc_ptr);
- REQUIRE(memcached_is_allocated(memc_ptr));
- memcached_free(memc_ptr);
- }
-
- SECTION("can be reset") {
- memc_ptr = memcached_clone(nullptr, &memc);
- REQUIRE(MEMCACHED_SUCCESS == memcached_reset(&memc));
- REQUIRE_FALSE(memcached_is_allocated(&memc));
- REQUIRE(MEMCACHED_SUCCESS == memcached_reset(memc_ptr));
- REQUIRE(memcached_is_allocated(memc_ptr));
- memcached_free(memc_ptr);
- }
-}
+++ /dev/null
-#include "../lib/common.hpp"
-
-static memcached_return_t delete_trigger(memcached_st *, const char *, size_t) {
- return MEMCACHED_SUCCESS;
-}
-
-TEST_CASE("memcached callbacks") {
- void *fptr = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(&delete_trigger));
- MemcachedPtr memc;
-
- SECTION("set delete trigger") {
- REQUIRE(MEMCACHED_SUCCESS == memcached_callback_set(*memc, MEMCACHED_CALLBACK_DELETE_TRIGGER, fptr));
- }
-
- SECTION("set delete trigger fails w/ NOREPLY") {
- REQUIRE(MEMCACHED_SUCCESS == memcached_behavior_set(*memc, MEMCACHED_BEHAVIOR_NOREPLY, true));
- REQUIRE_FALSE(MEMCACHED_SUCCESS == memcached_callback_set(*memc, MEMCACHED_CALLBACK_DELETE_TRIGGER, fptr));
- }
-}
+++ /dev/null
-#include "../lib/common.hpp"
-#include "../lib/MemcachedCluster.hpp"
-
-memcached_return_t dump_cb(const memcached_st *, const char *, size_t, void *ctx) {
- auto *c = reinterpret_cast<size_t *>(ctx);
- ++(*c);
- return MEMCACHED_SUCCESS;
-}
-
-TEST_CASE("memcached dump") {
- pair<string, MemcachedCluster> tests[]{
- {"mixed", MemcachedCluster::mixed()},
- {"network", MemcachedCluster::network()},
- {"socket", MemcachedCluster::socket()}
- };
-
- LOOPED_SECTION(tests) {
- auto memc = &test.memc;
-
- SECTION("prepared with 64 KVs") {
- for (int i = 0; i < 64; ++i) {
- char key[8];
- int len = snprintf(key, sizeof(key) - 1, "k_%d", i);
-
- CHECKED_IF(len) {
- REQUIRE_SUCCESS(memcached_set(memc, key, len, key, len, 0, 0));
- }
- }
-
- memcached_quit(memc);
-
- // let memcached sort itself
- using namespace chrono_literals;
- this_thread::sleep_for(3s);
-
- SECTION("dumps 64 KVs") {
- size_t counter = 0;
- memcached_dump_fn fn[] = {dump_cb};
-
- REQUIRE_SUCCESS(memcached_dump(memc, fn, &counter, 1));
- REQUIRE(counter == 64);
- }
- }
- }
-}
+++ /dev/null
-#include "../lib/common.hpp"
-#include "../lib/MemcachedCluster.hpp"
-
-#define TEST_KEY S("test")
-#define INITIAL_VAL S("initial")
-#define REPLACED_VAL S("replaced")
-
-static inline void check(memcached_st *enc, memcached_st *raw, const char *val, size_t len) {
- memcached_return_t enc_rc, raw_rc;
- size_t enc_length, raw_length;
- Malloced enc_value(memcached_get(enc, TEST_KEY, &enc_length, nullptr, &enc_rc));
- Malloced raw_value(memcached_get(raw, TEST_KEY, &raw_length, nullptr, &raw_rc));
-
- REQUIRE(enc_rc == MEMCACHED_SUCCESS);
- REQUIRE(raw_rc == MEMCACHED_SUCCESS);
- REQUIRE_FALSE(enc_length == raw_length);
- REQUIRE(memcmp(*raw_value, *enc_value, raw_length));
- REQUIRE(enc_length == len);
- REQUIRE_FALSE(memcmp(val, *enc_value, enc_length));
-}
-
-TEST_CASE("memcached encoding_key") {
- pair<string, MemcachedCluster> tests[]{
- {"network", MemcachedCluster::network()},
- {"socket", MemcachedCluster::socket()}
- };
-
- LOOPED_SECTION(tests) {
- auto memc = &test.memc;
-
- SECTION("accepts encoding key") {
- MemcachedPtr copy(memc);
-
- REQUIRE_SUCCESS(memcached_set_encoding_key(memc, S(__func__)));
-
- SECTION("sets encoded value") {
- REQUIRE_SUCCESS(memcached_set(memc, TEST_KEY, INITIAL_VAL, 0, 0));
-
- SECTION("gets encoded value") {
- check(memc, ©.memc, INITIAL_VAL);
- }
-
- SECTION("cloned gets encoded value") {
- MemcachedPtr dupe(memc);
-
- check(&dupe.memc, ©.memc, INITIAL_VAL);
- }
- }
-
- SECTION("adds encoded value") {
-
- REQUIRE_SUCCESS(memcached_set(memc, TEST_KEY, INITIAL_VAL, 0, 0));
- REQUIRE_RC(MEMCACHED_NOTSTORED, memcached_add(memc, TEST_KEY, REPLACED_VAL, 0, 0));
-
- check(memc, ©.memc, INITIAL_VAL);
-
- test.flush();
-
- REQUIRE_SUCCESS(memcached_add(memc, TEST_KEY, REPLACED_VAL, 0, 0));
-
- SECTION("gets encoded value") {
- check(memc, ©.memc, REPLACED_VAL);
- }
- }
-
- SECTION("replaces encoded value") {
- REQUIRE_SUCCESS(memcached_set(memc, TEST_KEY, INITIAL_VAL, 0, 0));
-
- check(memc, ©.memc, INITIAL_VAL);
-
- REQUIRE_SUCCESS(memcached_replace(memc, TEST_KEY, REPLACED_VAL, 0, 0));
-
- SECTION("gets encoded value") {
- check(memc, ©.memc, REPLACED_VAL);
- }
- }
-
- SECTION("unsupported") {
- REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_increment(memc, TEST_KEY, 0, nullptr));
- REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_decrement(memc, TEST_KEY, 0, nullptr));
- REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_increment_with_initial(memc, TEST_KEY, 0, 0, 0, nullptr));
- REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_decrement_with_initial(memc, TEST_KEY, 0, 0, 0, nullptr));
- REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_append(memc, TEST_KEY, REPLACED_VAL, 0, 0));
- REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_prepend(memc, TEST_KEY, REPLACED_VAL, 0, 0));
- }
- }
- }
-}
+++ /dev/null
-#include "../lib/common.hpp"
-#include "../lib/MemcachedCluster.hpp"
-
-TEST_CASE("memcached exist") {
- pair<string, MemcachedCluster> tests[]{
- {"bin_mixed", MemcachedCluster::mixed()},
- {"network", MemcachedCluster::network()},
- {"socket", MemcachedCluster::socket()}
- };
-
- tests[0].second.enableBinaryProto();
-
- LOOPED_SECTION(tests) {
- auto memc = &test.memc;
-
- SECTION("initial not found") {
- REQUIRE_RC(MEMCACHED_NOTFOUND,memcached_exist(memc, S("frog")));
- }
-
- SECTION("set found") {
- REQUIRE_SUCCESS(memcached_set(memc, S("frog"), S("frog"), 0, 0));
- REQUIRE_SUCCESS(memcached_exist(memc, S("frog")));
-
- SECTION("deleted not found") {
- REQUIRE_SUCCESS(memcached_delete(memc, S("frog"), 0));
- REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_exist(memc, S("frog")));
- }
- }
-
- SECTION("by key") {
- SECTION("initial not found") {
- REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_exist_by_key(memc, S("master"), S("frog")));
- }
-
- SECTION("set found") {
- REQUIRE_SUCCESS(memcached_set_by_key(memc, S("master"), S("frog"), S("frog"), 0, 0));
- REQUIRE_SUCCESS(memcached_exist_by_key(memc, S("master"), S("frog")));
-
- SECTION("deleted not found") {
- REQUIRE_SUCCESS(memcached_delete_by_key(memc, S("master"), S("frog"), 0));
- REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_exist_by_key(memc, S("master"), S("frog")));
- }
- }
- }
- }
-
-}
+++ /dev/null
-#include "../lib/common.hpp"
-#include "../lib/MemcachedCluster.hpp"
-
-/* Test case provided by Cal Haldenbrand */
-
-#define HALDENBRAND_KEY_COUNT 3000U // * 1024576
-#define HALDENBRAND_FLAG_KEY 99 // * 1024576
-
-#include <cstdlib>
-
-TEST_CASE("memcached haldenbrand nblock_tcp_ndelay") {
- pair<string, MemcachedCluster> tests[] = {
- {"network", MemcachedCluster::network()},
- };
-
- for (auto &[name, test] : tests) {
- REQUIRE_SUCCESS(memcached_behavior_set(&test.memc, MEMCACHED_BEHAVIOR_NO_BLOCK, true));
- REQUIRE_SUCCESS(memcached_behavior_set(&test.memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, true));
- }
-
- LOOPED_SECTION(tests) {
- auto memc = &test.memc;
-
- /* We just keep looking at the same values over and over */
- srandom(10);
-
- /* add key */
- unsigned long long total = 0;
- for (uint32_t x = 0; total < 20 * 1024576; x++) {
- uint32_t size = (uint32_t) (rand() % (5 * 1024)) + 400;
- char randomstuff[6 * 1024];
- memset(randomstuff, 0, 6 * 1024);
- REQUIRE(size < 6 * 1024); /* Being safe here */
-
- for (uint32_t j = 0; j < size; j++) {
- randomstuff[j] = (signed char) ((rand() % 26) + 97);
- }
-
- total += size;
- char key[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1];
- int key_length = snprintf(key, sizeof(key), "%u", x);
- REQUIRE_SUCCESS(memcached_set(memc, key, key_length,
- randomstuff, strlen(randomstuff),
- time_t(0), HALDENBRAND_FLAG_KEY
- ));
- }
- REQUIRE(total > HALDENBRAND_KEY_COUNT);
-
- size_t total_value_length = 0;
- for (uint32_t x = 0, errors = 0; total_value_length < 24576; x++) {
- uint32_t flags = 0;
- size_t val_len = 0;
-
- char key[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1];
- int key_length = snprintf(key, sizeof(key), "%u", x);
-
- memcached_return_t rc;
- char *getval = memcached_get(memc, key, key_length, &val_len, &flags, &rc);
- if (memcached_failed(rc)) {
- if (rc == MEMCACHED_NOTFOUND) {
- errors++;
- } else {
- REQUIRE(rc);
- }
-
- continue;
- }
- REQUIRE(uint32_t(HALDENBRAND_FLAG_KEY) == flags);
- REQUIRE(getval);
-
- total_value_length += val_len;
- errors = 0;
- ::free(getval);
- }
-
-
- std::vector<size_t> key_lengths;
- key_lengths.resize(HALDENBRAND_KEY_COUNT);
- std::vector<char *> keys;
- keys.resize(key_lengths.size());
- for (uint32_t x = 0; x < key_lengths.size(); x++) {
- char key[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1];
- int key_length = snprintf(key, sizeof(key), "%u", x);
- REQUIRE(key_length > 0);
- REQUIRE(key_length < MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1);
- keys[x] = strdup(key);
- key_lengths[x] = key_length;
- }
-
- REQUIRE_SUCCESS(memcached_mget(memc, &keys[0], &key_lengths[0], key_lengths.size()));
-
- unsigned int keys_returned;
- REQUIRE(memcached_success(fetch_all_results(memc, keys_returned)));
- REQUIRE(HALDENBRAND_KEY_COUNT == keys_returned);
-
- for (auto key : keys) {
- free(key);
- }
- }
-}
+++ /dev/null
-#include "../lib/common.hpp"
-
-TEST_CASE("memcached servers") {
- SECTION("memcached_servers_parse") {
- SECTION("does not leak memory") {
- memcached_server_st *s = memcached_servers_parse("1.2.3.4:1234");
- REQUIRE(s);
- memcached_server_free(s);
- }
- }
-
- SECTION("memcached_server_list") {
- SECTION("append with weight - all zeros") {
- memcached_server_st *sl = memcached_server_list_append_with_weight(
- nullptr, nullptr, 0, 0, 0);
- REQUIRE(sl);
- memcached_server_list_free(sl);
- }
- SECTION("append with weight - host set only") {
- memcached_server_st *sl = memcached_server_list_append_with_weight(
- nullptr, "localhost", 0, 0, 0);
- REQUIRE(sl);
- memcached_server_list_free(sl);
- }
- SECTION("append with weight - error set only") {
- memcached_return_t rc;
- memcached_server_st *sl = memcached_server_list_append_with_weight(
- nullptr, nullptr, 0, 0, &rc);
- REQUIRE(sl);
- REQUIRE(MEMCACHED_SUCCESS == rc);
- memcached_server_list_free(sl);
- }
- }
-
- SECTION("no configured servers") {
- MemcachedPtr memc;
-
- REQUIRE(MEMCACHED_NO_SERVERS == memcached_increment(*memc, S("key"), 1, nullptr));
- }
-}
--- /dev/null
+#include "testing/lib/common.hpp"
+
+#include "libhashkit-1.0/hashkit.hpp"
+
+static const char *input[] = {
+ "apple",
+ "beat",
+ "carrot",
+ "daikon",
+ "eggplant",
+ "flower",
+ "green",
+ "hide",
+ "ick",
+ "jack",
+ "kick",
+ "lime",
+ "mushrooms",
+ "nectarine",
+ "orange",
+ "peach",
+ "quant",
+ "ripen",
+ "strawberry",
+ "tang",
+ "up",
+ "volumne",
+ "when",
+ "yellow",
+ "zip",
+};
+
+static const uint32_t output[][sizeof(input)/sizeof(*input)] = {
+ // one_at_a_time
+ { 2297466611U, 3902465932U, 469785835U, 1937308741U,
+ 261917617U, 3785641677U, 1439605128U, 1649152283U,
+ 1493851484U, 1246520657U, 2221159044U, 1973511823U,
+ 384136800U, 214358653U, 2379473940U, 4269788650U,
+ 2864377005U, 2638630052U, 427683330U, 990491717U,
+ 1747111141U, 792127364U, 2599214128U, 2553037199U,
+ 2509838425U },
+
+ // md5
+ { 3195025439U, 2556848621U, 3724893440U, 3332385401U,
+ 245758794U, 2550894432U, 121710495U, 3053817768U,
+ 1250994555U, 1862072655U, 2631955953U, 2951528551U,
+ 1451250070U, 2820856945U, 2060845566U, 3646985608U,
+ 2138080750U, 217675895U, 2230934345U, 1234361223U,
+ 3968582726U, 2455685270U, 1293568479U, 199067604U,
+ 2042482093U },
+
+ // crc
+ { 10542U, 22009U, 14526U, 19510U, 19432U, 10199U, 20634U,
+ 9369U, 11511U, 10362U, 7893U, 31289U, 11313U, 9354U,
+ 7621U, 30628U, 15218U, 25967U, 2695U, 9380U,
+ 17300U, 28156U, 9192U, 20484U, 16925U },
+
+ // fnv1_64
+ { 473199127U, 4148981457U, 3971873300U, 3257986707U,
+ 1722477987U, 2991193800U, 4147007314U, 3633179701U,
+ 1805162104U, 3503289120U, 3395702895U, 3325073042U,
+ 2345265314U, 3340346032U, 2722964135U, 1173398992U,
+ 2815549194U, 2562818319U, 224996066U, 2680194749U,
+ 3035305390U, 246890365U, 2395624193U, 4145193337U,
+ 1801941682U },
+
+ // fnv1a_64
+ { 1488911807U, 2500855813U, 1510099634U, 1390325195U,
+ 3647689787U, 3241528582U, 1669328060U, 2604311949U,
+ 734810122U, 1516407546U, 560948863U, 1767346780U,
+ 561034892U, 4156330026U, 3716417003U, 3475297030U,
+ 1518272172U, 227211583U, 3938128828U, 126112909U,
+ 3043416448U, 3131561933U, 1328739897U, 2455664041U,
+ 2272238452U },
+
+ // fnv1_32
+ { 67176023U, 1190179409U, 2043204404U, 3221866419U,
+ 2567703427U, 3787535528U, 4147287986U, 3500475733U,
+ 344481048U, 3865235296U, 2181839183U, 119581266U,
+ 510234242U, 4248244304U, 1362796839U, 103389328U,
+ 1449620010U, 182962511U, 3554262370U, 3206747549U,
+ 1551306158U, 4127558461U, 1889140833U, 2774173721U,
+ 1180552018U },
+
+ // fnv1a_32
+ { 280767167U, 2421315013U, 3072375666U, 855001899U,
+ 459261019U, 3521085446U, 18738364U, 1625305005U,
+ 2162232970U, 777243802U, 3323728671U, 132336572U,
+ 3654473228U, 260679466U, 1169454059U, 2698319462U,
+ 1062177260U, 235516991U, 2218399068U, 405302637U,
+ 1128467232U, 3579622413U, 2138539289U, 96429129U,
+ 2877453236U },
+
+ // hsieh
+#ifdef HAVE_HSIEH_HASH
+ { 3738850110U, 3636226060U, 3821074029U, 3489929160U, 3485772682U, 80540287U,
+ 1805464076U, 1895033657U, 409795758U, 979934958U, 3634096985U, 1284445480U,
+ 2265380744U, 707972988U, 353823508U, 1549198350U, 1327930172U, 9304163U,
+ 4220749037U, 2493964934U, 2777873870U, 2057831732U, 1510213931U, 2027828987U,
+ 3395453351U },
+#else
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+#endif
+
+ // murmur
+#ifdef HAVE_MURMUR_HASH
+ // murmur
+ { 4142305122U, 734504955U, 3802834688U, 4076891445U,
+ 387802650U, 560515427U, 3274673488U, 3150339524U,
+ 1527441970U, 2728642900U, 3613992239U, 2938419259U,
+ 2321988328U, 1145154116U, 4038540960U, 2224541613U,
+ 264013145U, 3995512858U, 2400956718U, 2346666219U,
+ 926327338U, 442757446U, 1770805201U, 560483147U,
+ 3902279934U },
+ // murmur3
+ { 1120212521U, 1448785489U, 4186307405U, 2686268514U,
+ 444808887U, 221750260U, 3074673162U, 1946933257U,
+ 2826416675U, 2430719166U, 3200429559U, 297894347U,
+ 732888124U, 4050076964U, 3298336176U, 1336207361U,
+ 810553576U, 3748182674U, 3860119212U, 3439537197U,
+ 3044240981U, 1464271804U, 3896193724U, 2915115798U,
+ 1702843840U },
+#else
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+#endif
+
+ // jenkins
+ { 1442444624U, 4253821186U, 1885058256U, 2120131735U,
+ 3261968576U, 3515188778U, 4232909173U, 4288625128U,
+ 1812047395U, 3689182164U, 2502979932U, 1214050606U,
+ 2415988847U, 1494268927U, 1025545760U, 3920481083U,
+ 4153263658U, 3824871822U, 3072759809U, 798622255U,
+ 3065432577U, 1453328165U, 2691550971U, 3408888387U,
+ 2629893356U }
+};
+
+TEST_CASE("hashkit") {
+ hashkit_st st, *hp = hashkit_create(nullptr);
+ Hashkit stack;
+ Hashkit *heap = new Hashkit;
+
+ REQUIRE(hashkit_create(&st));
+ REQUIRE(hp);
+
+ SECTION("can copy") {
+ Hashkit stack_copy(stack);
+ Hashkit *heap_copy(heap);
+ hashkit_st st_copy, *st_ptr;
+
+ (void) stack_copy;
+ (void) heap_copy;
+
+ st_ptr = hashkit_clone(&st_copy, &st);
+ REQUIRE(st_ptr == &st_copy);
+ REQUIRE(hashkit_compare(st_ptr, &st_copy));
+
+ SUCCEED("OK");
+ }
+
+ SECTION("can assign") {
+ Hashkit stack_copy;
+
+ stack_copy = stack;
+ (void) stack_copy;
+
+ SUCCEED("OK");
+ }
+
+ SECTION("can digest default") {
+ REQUIRE(2297466611U == stack.digest(S("apple")));
+ REQUIRE(2297466611U == hashkit_digest(&st, S("apple")));
+ }
+
+ SECTION("can set hash function") {
+ for (int f = HASHKIT_HASH_DEFAULT; f < HASHKIT_HASH_MAX; ++f) {
+ auto h = static_cast<hashkit_hash_algorithm_t>(f);
+
+ if (h == HASHKIT_HASH_CUSTOM) {
+ continue;
+ }
+ if (!libhashkit_has_algorithm(h)) {
+ WARN("hashkit algorithm not enabled: " << libhashkit_string_hash(h) << " (" << f << ")");
+ continue;
+ }
+
+ REQUIRE(HASHKIT_SUCCESS == stack.set_function(h));
+ REQUIRE(HASHKIT_SUCCESS == hashkit_set_function(&st, h));
+
+ SECTION("can digest set hash function") {
+ auto n = 0;
+
+ for (auto i : input) {
+ CHECK(output[f][n] == stack.digest(S(i)));
+ CHECK(output[f][n] == hashkit_digest(&st, S(i)));
+ CHECK(output[f][n] == libhashkit_digest(S(i), h));
+ ++n;
+ }
+ }
+ }
+ }
+
+ SECTION("is comparable") {
+ REQUIRE(*heap == stack);
+ REQUIRE(hashkit_compare(&st, hp));
+
+ stack.set_function(HASHKIT_HASH_MD5);
+ hashkit_set_function(&st, HASHKIT_HASH_MD5);
+
+ REQUIRE_FALSE(*heap == stack);
+ REQUIRE_FALSE(hashkit_compare(&st, hp));
+ }
+
+ delete heap;
+ hashkit_free(&st);
+ hashkit_free(hp);
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/Cluster.hpp"
+#include "testing/lib/Retry.hpp"
+#include "testing/lib/Server.hpp"
+
+TEST_CASE("lib/Server") {
+ Server server{"memcached"};
+
+ SECTION("starts and listens") {
+
+ REQUIRE(server.start().has_value());
+
+ Retry server_is_listening{[&server] {
+ return server.isListening();
+ }};
+ REQUIRE(server_is_listening());
+
+ SECTION("stops") {
+
+ REQUIRE(server.stop());
+
+ SECTION("is waitable") {
+
+ REQUIRE(server.wait());
+
+ SECTION("stopped") {
+
+ REQUIRE_FALSE(server.check());
+ }
+ }
+ }
+ }
+}
+
+TEST_CASE("lib/Cluster") {
+ Cluster cluster{Server{"memcached", {
+ random_socket_or_port_arg(),
+ }}};
+
+ SECTION("starts and listens") {
+
+ REQUIRE(cluster.start());
+
+ Retry cluster_is_listening{[&cluster] {
+ return cluster.isListening();
+ }};
+ REQUIRE(cluster_is_listening());
+
+ SECTION("stops") {
+
+ cluster.stop();
+ cluster.wait();
+
+ SECTION("stopped") {
+
+ REQUIRE(cluster.isStopped());
+ }
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+DECLARE_STREQUAL;
+
+TEST_CASE("memcached append") {
+ pair<string, MemcachedCluster> tests[] = {
+ {"bin_mixed", MemcachedCluster::mixed()},
+ {"mixed", MemcachedCluster::mixed()}
+ };
+
+ tests[0].second.enableBinaryProto();
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ SECTION("text") {
+ const char *values[] = {
+ "one", "two", "three", "four"
+ };
+
+ for (auto key : values) {
+ string cmp{key};
+
+ REQUIRE_SUCCESS(memcached_set(memc, S(key), S(key), 0, 0));
+ for (auto value : values) {
+ REQUIRE_SUCCESS(memcached_append(memc, S(key), S(value), 0, 0));
+ cmp += value;
+ }
+
+ memcached_return_t rc;
+ size_t len;
+ uint32_t flags;
+ Malloced got(memcached_get(memc, S(key), &len, &flags, &rc));
+
+ REQUIRE(strequal(cmp, *got));
+ REQUIRE_SUCCESS(rc);
+ }
+ }
+
+ SECTION("bytes") {
+ vector<uint32_t> store_list{ 23, 56, 499, 98, 32847, 0 };
+ const size_t raw_len = sizeof(decltype(store_list)::value_type) * store_list.size();
+ const char *raw_ptr = reinterpret_cast<char *>(store_list.data());
+
+ REQUIRE_SUCCESS(memcached_set(memc, S(__func__), nullptr, 0, 0, 0));
+
+ for (auto item : store_list) {
+ auto val = reinterpret_cast<char *>(&item);
+ REQUIRE_SUCCESS(memcached_append(memc, S(__func__), val, sizeof(item), 0, 0));
+ }
+
+ memcached_return_t rc;
+ size_t len;
+ uint32_t flags;
+ Malloced got(memcached_get(memc, S(__func__), &len, &flags, &rc));
+
+ REQUIRE(len == raw_len);
+ REQUIRE(strequal({raw_ptr, raw_len}, string{*got, len}));
+ REQUIRE_SUCCESS(rc);
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+
+#include "libmemcached/is.h"
+
+TEST_CASE("memcached basic") {
+ SECTION("library") {
+ REQUIRE(string(LIBMEMCACHED_VERSION_STRING) == memcached_lib_version());
+ }
+ SECTION("memcached_st") {
+ memcached_st memc, *memc_ptr;
+
+ memc_ptr = memcached_create(&memc);
+
+ REQUIRE(memc_ptr);
+ REQUIRE(memc_ptr == &memc);
+
+ SECTION("can be cloned") {
+ memc_ptr = memcached_clone(nullptr, &memc);
+ REQUIRE(memc_ptr);
+ REQUIRE(memcached_is_allocated(memc_ptr));
+ memcached_free(memc_ptr);
+ }
+
+ SECTION("can be reset") {
+ memc_ptr = memcached_clone(nullptr, &memc);
+ REQUIRE(MEMCACHED_SUCCESS == memcached_reset(&memc));
+ REQUIRE_FALSE(memcached_is_allocated(&memc));
+ REQUIRE(MEMCACHED_SUCCESS == memcached_reset(memc_ptr));
+ REQUIRE(memcached_is_allocated(memc_ptr));
+ memcached_free(memc_ptr);
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+static memcached_return_t delete_trigger(memcached_st *, const char *, size_t) {
+ return MEMCACHED_SUCCESS;
+}
+
+static memcached_return_t read_through_trigger(memcached_st *, char *, size_t, memcached_result_st *result) {
+ return memcached_result_set_value(result, S("updated by read through trigger"));
+}
+
+static memcached_return_t clone_callback(memcached_st *, memcached_st *) {
+ return MEMCACHED_SUCCESS;
+}
+
+static memcached_return_t cleanup_callback(memcached_st *) {
+ return MEMCACHED_SUCCESS;
+}
+
+TEST_CASE("memcached callbacks") {
+ auto test{MemcachedCluster::mixed()};
+ auto memc = &test.memc;
+ memcached_return_t rc;
+
+ SECTION("user data") {
+ int temp = 123;
+ void *data = &temp;
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_USER_DATA, data));
+ REQUIRE(data == memcached_callback_get(memc, MEMCACHED_CALLBACK_USER_DATA, &rc));
+ REQUIRE_SUCCESS(rc);
+ }
+
+ SECTION("delete callback") {
+ void *dptr = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(&delete_trigger));
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_DELETE_TRIGGER, dptr));
+ REQUIRE(memcached_callback_get(memc, MEMCACHED_CALLBACK_DELETE_TRIGGER, &rc) == dptr);
+ REQUIRE_SUCCESS(rc);
+
+ SECTION("fails w/ NOREPLY") {
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, true));
+ REQUIRE_RC(MEMCACHED_INVALID_ARGUMENTS, memcached_callback_set(memc, MEMCACHED_CALLBACK_DELETE_TRIGGER, dptr));
+ }
+ }
+
+ SECTION("get_failure callback") {
+ void *gptr = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(&read_through_trigger));
+ Malloced empty(memcached_get(memc, S(__func__), nullptr, nullptr, &rc));
+ REQUIRE_FALSE(*empty);
+ REQUIRE_RC(MEMCACHED_NOTFOUND, rc);
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_GET_FAILURE, gptr));
+ REQUIRE(gptr == memcached_callback_get(memc, MEMCACHED_CALLBACK_GET_FAILURE, &rc));
+ REQUIRE_SUCCESS(rc);
+
+ for (int twice = 0; twice < 2; ++twice) {
+ uint32_t flags;
+ size_t len;
+ Malloced val(memcached_get(memc, S(__func__), &len, &flags, &rc));
+
+ REQUIRE_SUCCESS(rc);
+ REQUIRE(string("updated by read through trigger") == string(*val, len));
+ REQUIRE_FALSE((*val)[len]);
+ }
+
+ SECTION("clone callback") {
+ void *cptr = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(&clone_callback));
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_CLONE_FUNCTION, cptr));
+ REQUIRE(cptr == memcached_callback_get(memc, MEMCACHED_CALLBACK_CLONE_FUNCTION, &rc));
+ REQUIRE_SUCCESS(rc);
+ }
+
+ SECTION("cleanup callback") {
+ void *cptr = reinterpret_cast<void *>(reinterpret_cast<intptr_t>(cleanup_callback));
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_CLEANUP_FUNCTION, cptr));
+ REQUIRE(cptr == memcached_callback_get(memc, MEMCACHED_CALLBACK_CLEANUP_FUNCTION, &rc));
+ REQUIRE_SUCCESS(rc);
+ }
+
+ SECTION("namespace") {
+ void *ns;
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, "ns"));
+ ns = memcached_callback_get(memc, MEMCACHED_CALLBACK_NAMESPACE, &rc);
+ REQUIRE_SUCCESS(rc);
+ REQUIRE("ns"s == static_cast<char *>(ns));
+
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, nullptr));
+ ns = memcached_callback_get(memc, MEMCACHED_CALLBACK_NAMESPACE, &rc);
+ REQUIRE_SUCCESS(rc);
+ REQUIRE(nullptr == ns);
+
+
+ uint64_t binary = GENERATE(0, 1);
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, binary));
+
+ DYNAMIC_SECTION("too long (binary=" << binary << ")") {
+ string blob;
+
+ blob = random_ascii_string(MEMCACHED_MAX_NAMESPACE-1, '@', 'Z');
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, blob.c_str()));
+
+ blob = random_ascii_string(MEMCACHED_MAX_NAMESPACE, '@', 'Z');
+ REQUIRE_RC(MEMCACHED_KEY_TOO_BIG,memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, blob.c_str()));
+ }
+
+ DYNAMIC_SECTION("verify key (binary=" << binary << ")") {
+ REQUIRE_RC(binary ? MEMCACHED_INVALID_ARGUMENTS : MEMCACHED_SUCCESS,
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_VERIFY_KEY, 1));
+ REQUIRE_RC(binary ? MEMCACHED_SUCCESS : MEMCACHED_BAD_KEY_PROVIDED,
+ memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, "with spaces"));
+ }
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached cas") {
+ pair<string, MemcachedCluster> tests[] = {
+ {"network", MemcachedCluster::network()},
+ {"socket", MemcachedCluster::socket()}
+ };
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+ const char *keys[2] = {__func__, NULL};
+ size_t keylengths[2] = {strlen(__func__), 0};
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, true));
+ REQUIRE_SUCCESS(memcached_set(memc, S(__func__), S("we the people"), (time_t) 0, (uint32_t) 0));
+ REQUIRE_SUCCESS(memcached_mget(memc, keys, keylengths, 1));
+
+ memcached_result_st *results = memcached_result_create(memc, nullptr);
+ REQUIRE(results);
+
+ memcached_return_t rc;
+ results = memcached_fetch_result(memc, results, &rc);
+ REQUIRE(results);
+ REQUIRE_SUCCESS(rc);
+
+ REQUIRE(memcached_result_cas(results));
+ REQUIRE("we the people"s == string(memcached_result_value(results), memcached_result_length(results)));
+
+ uint64_t cas = memcached_result_cas(results);
+ REQUIRE(memcached_success(memcached_cas(memc, S(__func__), S("change the value"), 0, 0, cas)));
+
+ /*
+ * The item will have a new cas value, so try to set it again with the old
+ * value. This should fail!
+ */
+ REQUIRE_RC(MEMCACHED_DATA_EXISTS, memcached_cas(memc, S(__func__), S("change the value"), 0, 0, cas));
+
+ memcached_result_free(results);
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+memcached_return_t dump_cb(const memcached_st *, const char *, size_t, void *ctx) {
+ auto *c = reinterpret_cast<size_t *>(ctx);
+ ++(*c);
+ return MEMCACHED_SUCCESS;
+}
+
+TEST_CASE("memcached dump") {
+ pair<string, MemcachedCluster> tests[]{
+ {"mixed", MemcachedCluster::mixed()},
+ {"network", MemcachedCluster::network()},
+ {"socket", MemcachedCluster::socket()}
+ };
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ SECTION("prepared with 64 KVs") {
+ for (int i = 0; i < 64; ++i) {
+ char key[8];
+ int len = snprintf(key, sizeof(key) - 1, "k_%d", i);
+
+ CHECKED_IF(len) {
+ REQUIRE_SUCCESS(memcached_set(memc, key, len, key, len, 0, 0));
+ }
+ }
+
+ memcached_quit(memc);
+
+ // let memcached sort itself
+ using namespace chrono_literals;
+ this_thread::sleep_for(3s);
+
+ SECTION("dumps 64 KVs") {
+ size_t counter = 0;
+ memcached_dump_fn fn[] = {dump_cb};
+
+ REQUIRE_SUCCESS(memcached_dump(memc, fn, &counter, 1));
+ REQUIRE(counter == 64);
+ }
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+#define TEST_KEY S("test")
+#define INITIAL_VAL S("initial")
+#define REPLACED_VAL S("replaced")
+
+static inline void check(memcached_st *enc, memcached_st *raw, const char *val, size_t len) {
+ memcached_return_t enc_rc, raw_rc;
+ size_t enc_length, raw_length;
+ Malloced enc_value(memcached_get(enc, TEST_KEY, &enc_length, nullptr, &enc_rc));
+ Malloced raw_value(memcached_get(raw, TEST_KEY, &raw_length, nullptr, &raw_rc));
+
+ REQUIRE(enc_rc == MEMCACHED_SUCCESS);
+ REQUIRE(raw_rc == MEMCACHED_SUCCESS);
+ REQUIRE_FALSE(enc_length == raw_length);
+ REQUIRE(memcmp(*raw_value, *enc_value, raw_length));
+ REQUIRE(enc_length == len);
+ REQUIRE_FALSE(memcmp(val, *enc_value, enc_length));
+}
+
+TEST_CASE("memcached encoding_key") {
+ pair<string, MemcachedCluster> tests[]{
+ {"network", MemcachedCluster::network()},
+ {"socket", MemcachedCluster::socket()}
+ };
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ SECTION("accepts encoding key") {
+ MemcachedPtr copy(memc);
+
+ REQUIRE_SUCCESS(memcached_set_encoding_key(memc, S(__func__)));
+
+ SECTION("sets encoded value") {
+ REQUIRE_SUCCESS(memcached_set(memc, TEST_KEY, INITIAL_VAL, 0, 0));
+
+ SECTION("gets encoded value") {
+ check(memc, ©.memc, INITIAL_VAL);
+ }
+
+ SECTION("cloned gets encoded value") {
+ MemcachedPtr dupe(memc);
+
+ check(&dupe.memc, ©.memc, INITIAL_VAL);
+ }
+ }
+
+ SECTION("adds encoded value") {
+
+ REQUIRE_SUCCESS(memcached_set(memc, TEST_KEY, INITIAL_VAL, 0, 0));
+ REQUIRE_RC(MEMCACHED_NOTSTORED, memcached_add(memc, TEST_KEY, REPLACED_VAL, 0, 0));
+
+ check(memc, ©.memc, INITIAL_VAL);
+
+ test.flush();
+
+ REQUIRE_SUCCESS(memcached_add(memc, TEST_KEY, REPLACED_VAL, 0, 0));
+
+ SECTION("gets encoded value") {
+ check(memc, ©.memc, REPLACED_VAL);
+ }
+ }
+
+ SECTION("replaces encoded value") {
+ REQUIRE_SUCCESS(memcached_set(memc, TEST_KEY, INITIAL_VAL, 0, 0));
+
+ check(memc, ©.memc, INITIAL_VAL);
+
+ REQUIRE_SUCCESS(memcached_replace(memc, TEST_KEY, REPLACED_VAL, 0, 0));
+
+ SECTION("gets encoded value") {
+ check(memc, ©.memc, REPLACED_VAL);
+ }
+ }
+
+ SECTION("unsupported") {
+ REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_increment(memc, TEST_KEY, 0, nullptr));
+ REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_decrement(memc, TEST_KEY, 0, nullptr));
+ REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_increment_with_initial(memc, TEST_KEY, 0, 0, 0, nullptr));
+ REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_decrement_with_initial(memc, TEST_KEY, 0, 0, 0, nullptr));
+ REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_append(memc, TEST_KEY, REPLACED_VAL, 0, 0));
+ REQUIRE_RC(MEMCACHED_NOT_SUPPORTED, memcached_prepend(memc, TEST_KEY, REPLACED_VAL, 0, 0));
+ }
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached exist") {
+ pair<string, MemcachedCluster> tests[]{
+ {"bin_mixed", MemcachedCluster::mixed()},
+ {"network", MemcachedCluster::network()},
+ {"socket", MemcachedCluster::socket()}
+ };
+
+ tests[0].second.enableBinaryProto();
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ SECTION("initial not found") {
+ REQUIRE_RC(MEMCACHED_NOTFOUND,memcached_exist(memc, S("frog")));
+ }
+
+ SECTION("set found") {
+ REQUIRE_SUCCESS(memcached_set(memc, S("frog"), S("frog"), 0, 0));
+ REQUIRE_SUCCESS(memcached_exist(memc, S("frog")));
+
+ SECTION("deleted not found") {
+ REQUIRE_SUCCESS(memcached_delete(memc, S("frog"), 0));
+ REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_exist(memc, S("frog")));
+ }
+ }
+
+ SECTION("by key") {
+ SECTION("initial not found") {
+ REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_exist_by_key(memc, S("master"), S("frog")));
+ }
+
+ SECTION("set found") {
+ REQUIRE_SUCCESS(memcached_set_by_key(memc, S("master"), S("frog"), S("frog"), 0, 0));
+ REQUIRE_SUCCESS(memcached_exist_by_key(memc, S("master"), S("frog")));
+
+ SECTION("deleted not found") {
+ REQUIRE_SUCCESS(memcached_delete_by_key(memc, S("master"), S("frog"), 0));
+ REQUIRE_RC(MEMCACHED_NOTFOUND, memcached_exist_by_key(memc, S("master"), S("frog")));
+ }
+ }
+ }
+ }
+
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+/* Test case provided by Cal Haldenbrand */
+
+#define HALDENBRAND_KEY_COUNT 3000U // * 1024576
+#define HALDENBRAND_FLAG_KEY 99 // * 1024576
+
+#include <cstdlib>
+
+TEST_CASE("memcached haldenbrand nblock_tcp_ndelay") {
+ pair<string, MemcachedCluster> tests[] = {
+ {"network", MemcachedCluster::network()},
+ };
+
+ for (auto &[name, test] : tests) {
+ REQUIRE_SUCCESS(memcached_behavior_set(&test.memc, MEMCACHED_BEHAVIOR_NO_BLOCK, true));
+ REQUIRE_SUCCESS(memcached_behavior_set(&test.memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, true));
+ }
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ /* We just keep looking at the same values over and over */
+ srandom(10);
+
+ /* add key */
+ unsigned long long total = 0;
+ for (uint32_t x = 0; total < 20 * 1024576; x++) {
+ uint32_t size = (uint32_t) (rand() % (5 * 1024)) + 400;
+ char randomstuff[6 * 1024];
+ memset(randomstuff, 0, 6 * 1024);
+ REQUIRE(size < 6 * 1024); /* Being safe here */
+
+ for (uint32_t j = 0; j < size; j++) {
+ randomstuff[j] = (signed char) ((rand() % 26) + 97);
+ }
+
+ total += size;
+ char key[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1];
+ int key_length = snprintf(key, sizeof(key), "%u", x);
+ REQUIRE_SUCCESS(memcached_set(memc, key, key_length,
+ randomstuff, strlen(randomstuff),
+ time_t(0), HALDENBRAND_FLAG_KEY
+ ));
+ }
+ REQUIRE(total > HALDENBRAND_KEY_COUNT);
+
+ size_t total_value_length = 0;
+ for (uint32_t x = 0, errors = 0; total_value_length < 24576; x++) {
+ uint32_t flags = 0;
+ size_t val_len = 0;
+
+ char key[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1];
+ int key_length = snprintf(key, sizeof(key), "%u", x);
+
+ memcached_return_t rc;
+ char *getval = memcached_get(memc, key, key_length, &val_len, &flags, &rc);
+ if (memcached_failed(rc)) {
+ if (rc == MEMCACHED_NOTFOUND) {
+ errors++;
+ } else {
+ REQUIRE(rc);
+ }
+
+ continue;
+ }
+ REQUIRE(uint32_t(HALDENBRAND_FLAG_KEY) == flags);
+ REQUIRE(getval);
+
+ total_value_length += val_len;
+ errors = 0;
+ ::free(getval);
+ }
+
+
+ std::vector<size_t> key_lengths;
+ key_lengths.resize(HALDENBRAND_KEY_COUNT);
+ std::vector<char *> keys;
+ keys.resize(key_lengths.size());
+ for (uint32_t x = 0; x < key_lengths.size(); x++) {
+ char key[MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1];
+ int key_length = snprintf(key, sizeof(key), "%u", x);
+ REQUIRE(key_length > 0);
+ REQUIRE(key_length < MEMCACHED_MAXIMUM_INTEGER_DISPLAY_LENGTH + 1);
+ keys[x] = strdup(key);
+ key_lengths[x] = key_length;
+ }
+
+ REQUIRE_SUCCESS(memcached_mget(memc, &keys[0], &key_lengths[0], key_lengths.size()));
+
+ unsigned int keys_returned;
+ REQUIRE(memcached_success(fetch_all_results(memc, keys_returned)));
+ REQUIRE(HALDENBRAND_KEY_COUNT == keys_returned);
+
+ for (auto key : keys) {
+ free(key);
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached inc_dec") {
+ pair<string, MemcachedCluster> tests[] = {
+ {"mixed", MemcachedCluster::mixed()},
+ };
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+ uint64_t binary = GENERATE(0, 1);
+ void *prefix = GENERATE(as<void *>{}, nullptr, "namespace:");
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, binary));
+ if (prefix) {
+ REQUIRE_SUCCESS(memcached_callback_set(memc, MEMCACHED_CALLBACK_NAMESPACE, prefix));
+ }
+
+ DYNAMIC_SECTION("increment (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+
+ REQUIRE_SUCCESS(memcached_set(memc, S("number"), S("0"), 0, 0));
+
+ for (auto i = 1; i <= 10; ++i) {
+ REQUIRE_SUCCESS(memcached_increment(memc, S("number"), 1, &number));
+ REQUIRE(number == static_cast<uint64_t>(i));
+ }
+ }
+
+ DYNAMIC_SECTION("increment by_key (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+
+ REQUIRE_SUCCESS(memcached_set_by_key(memc, S("key"), S("number"), S("0"), 0, 0));
+
+ for (auto i = 1; i <= 10; ++i) {
+ REQUIRE_SUCCESS(memcached_increment_by_key(memc, S("key"), S("number"), 1, &number));
+ REQUIRE(number == static_cast<uint64_t>(i));
+ }
+ }
+
+ DYNAMIC_SECTION("increment with initial (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+ uint64_t initial = GENERATE(0, 456);
+
+ if (!binary) {
+ REQUIRE_RC(MEMCACHED_INVALID_ARGUMENTS,
+ memcached_increment_with_initial(memc, S("number"), 1, initial, 0, &number));
+ } else {
+ REQUIRE_SUCCESS(memcached_increment_with_initial(memc, S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial);
+ REQUIRE_SUCCESS(memcached_increment_with_initial(memc, S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial + 123);
+ }
+ }
+
+ DYNAMIC_SECTION("increment with initial by_key (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+ uint64_t initial = GENERATE(0, 456);
+
+ if (!binary) {
+ REQUIRE_RC(MEMCACHED_INVALID_ARGUMENTS,
+ memcached_increment_with_initial_by_key(memc, S("key"), S("number"), 1, initial, 0, &number));
+ } else {
+ REQUIRE_SUCCESS(memcached_increment_with_initial_by_key(memc, S("key"), S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial);
+ REQUIRE_SUCCESS(memcached_increment_with_initial_by_key(memc, S("key"), S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial + 123);
+ }
+ }
+
+ DYNAMIC_SECTION("decrement (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+
+ REQUIRE_SUCCESS(memcached_set(memc, S("number"), S("10"), 0, 0));
+
+ for (auto i = 9; i >= 0; --i) {
+ REQUIRE_SUCCESS(memcached_decrement(memc, S("number"), 1, &number));
+ REQUIRE(number == static_cast<uint64_t>(i));
+ }
+ }
+ DYNAMIC_SECTION("decrement by_key (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+
+ REQUIRE_SUCCESS(memcached_set_by_key(memc, S("key"), S("number"), S("10"), 0, 0));
+
+ for (auto i = 9; i >= 0; --i) {
+ REQUIRE_SUCCESS(memcached_decrement_by_key(memc, S("key"), S("number"), 1, &number));
+ REQUIRE(number == static_cast<uint64_t>(i));
+ }
+ }
+ DYNAMIC_SECTION("decrement with initial (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+ uint64_t initial = GENERATE(987, 456);
+
+ if (!binary) {
+ REQUIRE_RC(MEMCACHED_INVALID_ARGUMENTS,
+ memcached_decrement_with_initial(memc, S("number"), 1, initial, 0, &number));
+ } else {
+ REQUIRE_SUCCESS(memcached_decrement_with_initial(memc, S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial);
+ REQUIRE_SUCCESS(memcached_decrement_with_initial(memc, S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial - 123);
+ }
+ }
+ DYNAMIC_SECTION("decrement with initial by_key (binary=" << binary << ", prefix=" << prefix << ")") {
+ uint64_t number;
+ uint64_t initial = GENERATE(987, 456);
+
+ if (!binary) {
+ REQUIRE_RC(MEMCACHED_INVALID_ARGUMENTS,
+ memcached_decrement_with_initial_by_key(memc, S("key"), S("number"), 1, initial, 0, &number));
+ } else {
+ REQUIRE_SUCCESS(memcached_decrement_with_initial_by_key(memc, S("key"), S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial);
+ REQUIRE_SUCCESS(memcached_decrement_with_initial_by_key(memc, S("key"), S("number"), 123, initial, 0, &number));
+ REQUIRE(number == initial - 123);
+ }
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+#include "ketama_test_cases.h"
+
+#include "libmemcached/continuum.hpp"
+#include "libmemcached/instance.hpp"
+
+DECLARE_STREQUAL;
+
+TEST_CASE("memcached ketama_compat") {
+ auto test = MemcachedCluster::network();
+ auto memc = &test.memc;
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED, 1));
+ REQUIRE(uint64_t(1) == memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED));
+
+ SECTION("generate hash") {
+ REQUIRE_SUCCESS(memcached_behavior_set_distribution(memc, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA));
+ REQUIRE(MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA == memcached_behavior_get_distribution(memc));
+
+ memcached_server_st *server_pool = memcached_servers_parse(
+ "10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100"
+ );
+ memcached_servers_reset(memc);
+ memcached_server_push(memc, server_pool);
+
+ /* verify that the server list was parsed okay. */
+ REQUIRE(8U == memcached_server_count(memc));
+ REQUIRE(strequal(server_pool[0].hostname, "10.0.1.1"));
+ REQUIRE(in_port_t(11211) == server_pool[0].port);
+ REQUIRE(600U == server_pool[0].weight);
+ REQUIRE(strequal(server_pool[2].hostname, "10.0.1.3"));
+ REQUIRE(in_port_t(11211) == server_pool[2].port);
+ REQUIRE(200U == server_pool[2].weight);
+ REQUIRE(strequal(server_pool[7].hostname, "10.0.1.8"));
+ REQUIRE(in_port_t(11211) == server_pool[7].port);
+ REQUIRE(100U == server_pool[7].weight);
+
+ /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+ * us test the boundary wraparound.
+ */
+ REQUIRE(memcached_generate_hash(memc, (char *) "VDEAAAAA", 8) == memc->ketama.continuum[0].index);
+
+ /* verify the standard ketama set. */
+ for (uint32_t x = 0; x < 99; x++) {
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+ const memcached_instance_st *instance =
+ memcached_server_instance_by_position(memc, server_idx);
+ const char *hostname = memcached_server_name(instance);
+
+ REQUIRE(strequal(hostname, ketama_test_cases[x].server));
+ }
+
+ memcached_server_list_free(server_pool);
+ }
+
+ SECTION("user bug 18") {
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5));
+ REQUIRE(MEMCACHED_HASH_MD5 == memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH));
+
+ memcached_server_st *server_pool = memcached_servers_parse(
+ "10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100"
+ );
+ memcached_servers_reset(memc);
+ memcached_server_push(memc, server_pool);
+
+ /* verify that the server list was parsed okay. */
+ REQUIRE(memcached_server_count(memc) == 8);
+ REQUIRE(strequal(server_pool[0].hostname, "10.0.1.1"));
+ REQUIRE(server_pool[0].port == 11211);
+ REQUIRE(server_pool[0].weight == 600);
+ REQUIRE(strequal(server_pool[2].hostname, "10.0.1.3"));
+ REQUIRE(server_pool[2].port == 11211);
+ REQUIRE(server_pool[2].weight == 200);
+ REQUIRE(strequal(server_pool[7].hostname, "10.0.1.8"));
+ REQUIRE(server_pool[7].port == 11211);
+ REQUIRE(server_pool[7].weight == 100);
+
+ /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+ * us test the boundary wraparound.
+ */
+ REQUIRE(memcached_generate_hash(memc, (char *) "VDEAAAAA", 8) == memc->ketama.continuum[0].index);
+
+ /* verify the standard ketama set. */
+ for (auto x = 0; x < 99; x++) {
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+
+ const memcached_instance_st *instance =
+ memcached_server_instance_by_position(memc, server_idx);
+
+ const char *hostname = memcached_server_name(instance);
+ REQUIRE(strequal(hostname, ketama_test_cases[x].server));
+ }
+
+ memcached_server_list_free(server_pool);
+ }
+
+ SECTION("auto_eject_hosts") {
+ memcached_servers_reset(memc);
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH, MEMCACHED_HASH_MD5));
+ REQUIRE(MEMCACHED_HASH_MD5 == memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_KETAMA_HASH));
+
+ /* server should be removed when in delay */
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, 1));
+ REQUIRE(memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS));
+
+ memcached_server_st *server_pool;
+ server_pool = memcached_servers_parse(
+ "10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100"
+ );
+ memcached_servers_reset(memc);
+ memcached_server_push(memc, server_pool);
+
+ /* verify that the server list was parsed okay. */
+ REQUIRE(memcached_server_count(memc) == 8);
+ REQUIRE(strequal(server_pool[0].hostname, "10.0.1.1"));
+ REQUIRE(server_pool[0].port == 11211);
+ REQUIRE(server_pool[0].weight == 600);
+ REQUIRE(strequal(server_pool[2].hostname, "10.0.1.3"));
+ REQUIRE(server_pool[2].port == 11211);
+ REQUIRE(server_pool[2].weight == 200);
+ REQUIRE(strequal(server_pool[7].hostname, "10.0.1.8"));
+ REQUIRE(server_pool[7].port == 11211);
+ REQUIRE(server_pool[7].weight == 100);
+
+ const memcached_instance_st *instance = memcached_server_instance_by_position(memc, 2);
+ memcached_instance_next_retry(instance, time(nullptr) + 15);
+ memc->ketama.next_distribution_rebuild = time(nullptr) - 1;
+
+ /*
+ This would not work if there were only two hosts.
+ */
+ for (auto x = 0; x < 99; x++) {
+ memcached_autoeject(memc);
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+ REQUIRE(server_idx != 2);
+ }
+
+ /* and re-added when it's back. */
+ time_t absolute_time = time(nullptr) - 1;
+ memcached_instance_next_retry(instance, absolute_time);
+ memc->ketama.next_distribution_rebuild = absolute_time;
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, memc->distribution);
+ for (auto x = 0; x < 99; x++) {
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases[x].key, strlen(ketama_test_cases[x].key));
+ // We re-use instance from above.
+ instance = memcached_server_instance_by_position(memc, server_idx);
+ const char *hostname = memcached_server_name(instance);
+ REQUIRE(strequal(hostname, ketama_test_cases[x].server));
+ }
+
+ memcached_server_list_free(server_pool);
+ }
+
+ SECTION("spymemcached") {
+ memcached_servers_reset(memc);
+ REQUIRE_SUCCESS(memcached_behavior_set_distribution(memc, MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY));
+ REQUIRE(MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY == memcached_behavior_get_distribution(memc));
+
+ memcached_server_st *server_pool = memcached_servers_parse(
+ "10.0.1.1:11211 600,10.0.1.2:11211 300,10.0.1.3:11211 200,10.0.1.4:11211 350,10.0.1.5:11211 1000,10.0.1.6:11211 800,10.0.1.7:11211 950,10.0.1.8:11211 100"
+ );
+ REQUIRE(server_pool);
+ memcached_server_push(memc, server_pool);
+
+ /* verify that the server list was parsed okay. */
+ REQUIRE(8U == memcached_server_count(memc));
+ REQUIRE(strequal(server_pool[0].hostname, "10.0.1.1"));
+ REQUIRE(in_port_t(11211) == server_pool[0].port);
+ REQUIRE(600U == server_pool[0].weight);
+ REQUIRE(strequal(server_pool[2].hostname, "10.0.1.3"));
+ REQUIRE(in_port_t(11211) == server_pool[2].port);
+ REQUIRE(200U == server_pool[2].weight);
+ REQUIRE(strequal(server_pool[7].hostname, "10.0.1.8"));
+ REQUIRE(in_port_t(11211) == server_pool[7].port);
+ REQUIRE(100U == server_pool[7].weight);
+
+ /* VDEAAAAA hashes to fffcd1b5, after the last continuum point, and lets
+ * us test the boundary wraparound.
+ */
+ REQUIRE(memcached_generate_hash(memc, (char *) "VDEAAAAA", 8) == memc->ketama.continuum[0].index);
+
+ /* verify the standard ketama set. */
+ for (uint32_t x = 0; x < 99; x++) {
+ uint32_t server_idx = memcached_generate_hash(memc, ketama_test_cases_spy[x].key,
+ strlen(ketama_test_cases_spy[x].key));
+
+ const memcached_instance_st *instance =
+ memcached_server_instance_by_position(memc, server_idx);
+
+ const char *hostname = memcached_server_name(instance);
+ REQUIRE(strequal(hostname, ketama_test_cases_spy[x].server));
+ }
+
+ memcached_server_list_free(server_pool);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2006-2009 Brian Aker
+ * All rights reserved.
+ *
+ * Use and distribution licensed under the BSD license. See
+ * the COPYING file in the parent directory for full text.
+ */
+
+#pragma once
+
+static struct {
+ const char *key;
+ unsigned long hash1;
+ unsigned long hash2;
+ const char *server;
+} ketama_test_cases[99]= {
+ { "SVa_]_V41)", 443691461UL, 445379617UL, "10.0.1.7" },
+ { "*/Z;?V(.\\8", 1422915503UL, 1428303028UL, "10.0.1.1" },
+ { "30C1*Z*S/_", 1473165754UL, 1480075959UL, "10.0.1.2" },
+ { "ERR:EC58G>", 2148406511UL, 2168579133UL, "10.0.1.7" },
+ { "1I=cTMNTKF", 2882686667UL, 2885206587UL, "10.0.1.5" },
+ { "]VG<`I*Z8)", 1103544263UL, 1104827657UL, "10.0.1.5" },
+ { "UUTC`-V159", 3716288206UL, 3727224240UL, "10.0.1.5" },
+ { "@7RU6C6T+Z", 3862737685UL, 3871917949UL, "10.0.1.5" },
+ { "/XLN0@+36;", 1623269830UL, 1627683651UL, "10.0.1.1" },
+ { "4(`X;\\V.^c", 373546328UL, 383925769UL, "10.0.1.1" },
+ { "726bW=9*a4", 4213440020UL, 4213950705UL, "10.0.1.7" },
+ { "\\`)<B)UE,c", 951096736UL, 955226069UL, "10.0.1.1" },
+ { "P1[Ma3=K1/", 1989324036UL, 1994028240UL, "10.0.1.8" },
+ { "C89I.-V?cT", 1604239957UL, 1606398093UL, "10.0.1.8" },
+ { "D[HE+cFXDK", 2117036136UL, 2117124014UL, "10.0.1.3" },
+ { "P1L?NAB[)K", 2129972569UL, 2132542634UL, "10.0.1.1" },
+ { "cDT0)Z5P6,", 176485284UL, 178675413UL, "10.0.1.5" },
+ { "@JW`+[WAO8", 2720940826UL, 2743975456UL, "10.0.1.5" },
+ { "\\39DKW^)N_", 3548879868UL, 3550704865UL, "10.0.1.3" },
+ { "EM75N0+[X1", 1558531507UL, 1559308507UL, "10.0.1.4" },
+ { "`,SS]NBP,b", 1883545960UL, 1884847278UL, "10.0.1.1" },
+ { "XX1a9LT+F?", 653487707UL, 656410408UL, "10.0.1.5" },
+ { "Zc\\-,F-c6V", 1160802451UL, 1171575728UL, "10.0.1.5" },
+ { "1*RTMC7,03", 1602398012UL, 1606398093UL, "10.0.1.8" },
+ { "*Xc+V0P>32", 536016577UL, 539988520UL, "10.0.1.7" },
+ { "U))Fb-(`,.", 4128682289UL, 4136854163UL, "10.0.1.7" },
+ { "R-08RNTaRT", 3718170086UL, 3727224240UL, "10.0.1.5" },
+ { "(LHcO203I3", 1007779411UL, 1014643570UL, "10.0.1.5" },
+ { "=256P+;Qc8", 3976201210UL, 3976304873UL, "10.0.1.5" },
+ { "OI5XZ_BBT(", 2155922164UL, 2168579133UL, "10.0.1.7" },
+ { "2TLRL/UL;:", 1086800909UL, 1095659802UL, "10.0.1.7" },
+ { "WHD\\O1`ZRW", 3087923411UL, 3095471560UL, "10.0.1.5" },
+ { ".=54)_c;=T", 2497691631UL, 2502731301UL, "10.0.1.1" },
+ { ";G<W-XWZ@b", 2888169733UL, 2888728739UL, "10.0.1.5" },
+ { "(,>E`)FT\\4", 580747448UL, 581063326UL, "10.0.1.2" },
+ { "HZAU*;P*N]", 2564670474UL, 2565697267UL, "10.0.1.7" },
+ { "NZ@ZE=O84_", 533335275UL, 539988520UL, "10.0.1.7" },
+ { "6,cEI`F_P>", 3972869246UL, 3974773167UL, "10.0.1.6" },
+ { "c,5AQ/T5)6", 2835605783UL, 2847870057UL, "10.0.1.8" },
+ { ".O,>>BT)RX", 3857978174UL, 3871917949UL, "10.0.1.5" },
+ { "XY\\X::LX50", 1749241099UL, 1752196488UL, "10.0.1.6" },
+ { "+550F^/.01", 3781824099UL, 3783248219UL, "10.0.1.6" },
+ { "<.X9E2S5+9", 3232479481UL, 3234387706UL, "10.0.1.7" },
+ { "]\\.UH8_0a1", 2419699252UL, 2423002920UL, "10.0.1.4" },
+ { "8(6=(T0/Z0", 728266737UL, 729026070UL, "10.0.1.7" },
+ { "8*6a;Sc*X+", 4223431086UL, 4230156966UL, "10.0.1.2" },
+ { "<QW:;3K6;H", 2731158143UL, 2743975456UL, "10.0.1.5" },
+ { "7C@EY@-Y?_", 760770733UL, 761576669UL, "10.0.1.5" },
+ { "aPb3E1WD4K", 2500489218UL, 2502731301UL, "10.0.1.1" },
+ { "?@12R<=1BH", 1494795329UL, 1502505505UL, "10.0.1.8" },
+ { "QR(a+Q=1FU", 3238535074UL, 3238996435UL, "10.0.1.6" },
+ { "`C9^FV,960", 2628553463UL, 2628733766UL, "10.0.1.6" },
+ { "UNHVP..^8H", 977096483UL, 977319837UL, "10.0.1.4" },
+ { ":Y.2W2[(35", 2777083668UL, 2784182515UL, "10.0.1.7" },
+ { "M/HV^_HZ4O", 3623390946UL, 3624445007UL, "10.0.1.7" },
+ { "ZY16KQ<ICD", 1831153193UL, 1838563516UL, "10.0.1.4" },
+ { "bV2,`a.PY9", 1962228869UL, 1962648654UL, "10.0.1.1" },
+ { "U;9:-+5N]9", 269504649UL, 277560877UL, "10.0.1.1" },
+ { "1S/:aJ[1(;", 578069729UL, 581063326UL, "10.0.1.2" },
+ { "Nb-X^]M)I:", 330865696UL, 331009896UL, "10.0.1.6" },
+ { "2;M;ES>J5/", 2776949824UL, 2784182515UL, "10.0.1.7" },
+ { "[>RZHG97Q9", 71954686UL, 72034069UL, "10.0.1.6" },
+ { "J3/G[)9<^Z", 2799896459UL, 2805183696UL, "10.0.1.7" },
+ { "N-)88>[O`,", 50404102UL, 51792557UL, "10.0.1.5" },
+ { "NP:=FR\\OaA", 3837333776UL, 3837792034UL, "10.0.1.7" },
+ { "`@L+W;a,O[", 1512157148UL, 1522285852UL, "10.0.1.6" },
+ { "W2`P:-+1T[", 2945171975UL, 2946196424UL, "10.0.1.5" },
+ { "-6G7K^YDIN", 3168617340UL, 3170513015UL, "10.0.1.7" },
+ { "U>*>9ZI6V5", 668514946UL, 674097631UL, "10.0.1.6" },
+ { ".I?^6Ic9RK", 938419020UL, 942832691UL, "10.0.1.6" },
+ { "0OZH^9BKM[", 3682518606UL, 3686781297UL, "10.0.1.8" },
+ { "5?50UGZ:ML", 868610882UL, 869425986UL, "10.0.1.5" },
+ { "?K2NF@3=IU", 381218851UL, 383925769UL, "10.0.1.1" },
+ { "YI@G-2X?UB", 3688706179UL, 3693197681UL, "10.0.1.5" },
+ { "7cY</BSaL=", 3976870223UL, 3978903843UL, "10.0.1.6" },
+ { "A(`KF:[RH8", 3292979676UL, 3294849139UL, "10.0.1.6" },
+ { ";=ZT\\W^P+H", 1401102653UL, 1416290674UL, "10.0.1.4" },
+ { "b2?WFF56;R", 480494704UL, 486971192UL, "10.0.1.4" },
+ { "CTR74,J+N.", 137446045UL, 146633907UL, "10.0.1.8" },
+ { "<b;*R+QDST", 1304985302UL, 1308223778UL, "10.0.1.5" },
+ { "\\R^7=9UCG`", 126218373UL, 129199837UL, "10.0.1.5" },
+ { "1bQS5]WOXB", 1853470245UL, 1855329369UL, "10.0.1.4" },
+ { "M(@X^b[L:K", 3019630308UL, 3022260113UL, "10.0.1.1" },
+ { "431cBF8,YO", 1679726993UL, 1685224295UL, "10.0.1.7" },
+ { "(bEIQJ:E./", 2922607787UL, 2925521819UL, "10.0.1.6" },
+ { "WS/3H*)7F;", 419488232UL, 422140585UL, "10.0.1.5" },
+ { "ZJF[Ia6Q)+", 3960568056UL, 3962489998UL, "10.0.1.7" },
+ { "<]*QCK8U,>", 2590140172UL, 2598117636UL, "10.0.1.7" },
+ { "\\[a\\^=V_M0", 689410119UL, 698690782UL, "10.0.1.6" },
+ { "7;RM+8J9YC", 1530175299UL, 1531107082UL, "10.0.1.7" },
+ { "4*=.SPR[AV", 3928582722UL, 3928853792UL, "10.0.1.1" },
+ { "-2F+^88P4U", 3023552752UL, 3025823613UL, "10.0.1.7" },
+ { "X;-F`(N?9D", 570465234UL, 572485994UL, "10.0.1.7" },
+ { "R=F_D-K2a]", 1287750228UL, 1290935562UL, "10.0.1.7" },
+ { "X*+2aaC.EG", 3200948713UL, 3201088518UL, "10.0.1.5" },
+ { "[1ZXONX2]a", 4108881567UL, 4109865744UL, "10.0.1.4" },
+ { "FL;\\GWacaV", 458449508UL, 467374054UL, "10.0.1.4" },
+ { "\\MQ_XNT7L-", 1259349383UL, 1259509450UL, "10.0.1.7" },
+ { "VD6D0]ba_\\", 3842502950UL, 3842588691UL, "10.0.1.1" },
+};
+
+#include "ketama_test_cases_spy.h"
--- /dev/null
+/*
+ * Copyright (C) 2006-2009 Brian Aker
+ * All rights reserved.
+ *
+ * Use and distribution licensed under the BSD license. See
+ * the COPYING file in the parent directory for full text.
+ */
+
+#pragma once
+
+static struct {
+ const char *key;
+ unsigned long hash1;
+ unsigned long hash2;
+ const char *server;
+} ketama_test_cases_spy[99]= {
+ { "SVa_]_V41)", 443691461UL, 445379617UL, "10.0.1.2" },
+ { "*/Z;?V(.\\8", 1422915503UL, 1428303028UL, "10.0.1.4" },
+ { "30C1*Z*S/_", 1473165754UL, 1480075959UL, "10.0.1.2" },
+ { "ERR:EC58G>", 2148406511UL, 2168579133UL, "10.0.1.7" },
+ { "1I=cTMNTKF", 2882686667UL, 2885206587UL, "10.0.1.4" },
+ { "]VG<`I*Z8)", 1103544263UL, 1104827657UL, "10.0.1.5" },
+ { "UUTC`-V159", 3716288206UL, 3727224240UL, "10.0.1.7" },
+ { "@7RU6C6T+Z", 3862737685UL, 3871917949UL, "10.0.1.6" },
+ { "/XLN0@+36;", 1623269830UL, 1627683651UL, "10.0.1.7" },
+ { "4(`X;\\V.^c", 373546328UL, 383925769UL, "10.0.1.6" },
+ { "726bW=9*a4", 4213440020UL, 4213950705UL, "10.0.1.3" },
+ { "\\`)<B)UE,c", 951096736UL, 955226069UL, "10.0.1.8" },
+ { "P1[Ma3=K1/", 1989324036UL, 1994028240UL, "10.0.1.1" },
+ { "C89I.-V?cT", 1604239957UL, 1606398093UL, "10.0.1.5" },
+ { "D[HE+cFXDK", 2117036136UL, 2117124014UL, "10.0.1.5" },
+ { "P1L?NAB[)K", 2129972569UL, 2132542634UL, "10.0.1.7" },
+ { "cDT0)Z5P6,", 176485284UL, 178675413UL, "10.0.1.1" },
+ { "@JW`+[WAO8", 2720940826UL, 2743975456UL, "10.0.1.2" },
+ { "\\39DKW^)N_", 3548879868UL, 3550704865UL, "10.0.1.6" },
+ { "EM75N0+[X1", 1558531507UL, 1559308507UL, "10.0.1.5" },
+ { "`,SS]NBP,b", 1883545960UL, 1884847278UL, "10.0.1.1" },
+ { "XX1a9LT+F?", 653487707UL, 656410408UL, "10.0.1.6" },
+ { "Zc\\-,F-c6V", 1160802451UL, 1171575728UL, "10.0.1.6" },
+ { "1*RTMC7,03", 1602398012UL, 1606398093UL, "10.0.1.5" },
+ { "*Xc+V0P>32", 536016577UL, 539988520UL, "10.0.1.7" },
+ { "U))Fb-(`,.", 4128682289UL, 4136854163UL, "10.0.1.7" },
+ { "R-08RNTaRT", 3718170086UL, 3727224240UL, "10.0.1.5" },
+ { "(LHcO203I3", 1007779411UL, 1014643570UL, "10.0.1.1" },
+ { "=256P+;Qc8", 3976201210UL, 3976304873UL, "10.0.1.3" },
+ { "OI5XZ_BBT(", 2155922164UL, 2168579133UL, "10.0.1.5" },
+ { "2TLRL/UL;:", 1086800909UL, 1095659802UL, "10.0.1.2" },
+ { "WHD\\O1`ZRW", 3087923411UL, 3095471560UL, "10.0.1.1" },
+ { ".=54)_c;=T", 2497691631UL, 2502731301UL, "10.0.1.6" },
+ { ";G<W-XWZ@b", 2888169733UL, 2888728739UL, "10.0.1.7" },
+ { "(,>E`)FT\\4", 580747448UL, 581063326UL, "10.0.1.5" },
+ { "HZAU*;P*N]", 2564670474UL, 2565697267UL, "10.0.1.1" },
+ { "NZ@ZE=O84_", 533335275UL, 539988520UL, "10.0.1.7" },
+ { "6,cEI`F_P>", 3972869246UL, 3974773167UL, "10.0.1.3" },
+ { "c,5AQ/T5)6", 2835605783UL, 2847870057UL, "10.0.1.7" },
+ { ".O,>>BT)RX", 3857978174UL, 3871917949UL, "10.0.1.7" },
+ { "XY\\X::LX50", 1749241099UL, 1752196488UL, "10.0.1.7" },
+ { "+550F^/.01", 3781824099UL, 3783248219UL, "10.0.1.2" },
+ { "<.X9E2S5+9", 3232479481UL, 3234387706UL, "10.0.1.7" },
+ { "]\\.UH8_0a1", 2419699252UL, 2423002920UL, "10.0.1.6" },
+ { "8(6=(T0/Z0", 728266737UL, 729026070UL, "10.0.1.6" },
+ { "8*6a;Sc*X+", 4223431086UL, 4230156966UL, "10.0.1.5" },
+ { "<QW:;3K6;H", 2731158143UL, 2743975456UL, "10.0.1.7" },
+ { "7C@EY@-Y?_", 760770733UL, 761576669UL, "10.0.1.5" },
+ { "aPb3E1WD4K", 2500489218UL, 2502731301UL, "10.0.1.2" },
+ { "?@12R<=1BH", 1494795329UL, 1502505505UL, "10.0.1.1" },
+ { "QR(a+Q=1FU", 3238535074UL, 3238996435UL, "10.0.1.5" },
+ { "`C9^FV,960", 2628553463UL, 2628733766UL, "10.0.1.3" },
+ { "UNHVP..^8H", 977096483UL, 977319837UL, "10.0.1.6" },
+ { ":Y.2W2[(35", 2777083668UL, 2784182515UL, "10.0.1.6" },
+ { "M/HV^_HZ4O", 3623390946UL, 3624445007UL, "10.0.1.4" },
+ { "ZY16KQ<ICD", 1831153193UL, 1838563516UL, "10.0.1.7" },
+ { "bV2,`a.PY9", 1962228869UL, 1962648654UL, "10.0.1.7" },
+ { "U;9:-+5N]9", 269504649UL, 277560877UL, "10.0.1.2" },
+ { "1S/:aJ[1(;", 578069729UL, 581063326UL, "10.0.1.5" },
+ { "Nb-X^]M)I:", 330865696UL, 331009896UL, "10.0.1.3" },
+ { "2;M;ES>J5/", 2776949824UL, 2784182515UL, "10.0.1.6" },
+ { "[>RZHG97Q9", 71954686UL, 72034069UL, "10.0.1.4" },
+ { "J3/G[)9<^Z", 2799896459UL, 2805183696UL, "10.0.1.6" },
+ { "N-)88>[O`,", 50404102UL, 51792557UL, "10.0.1.2" },
+ { "NP:=FR\\OaA", 3837333776UL, 3837792034UL, "10.0.1.7" },
+ { "`@L+W;a,O[", 1512157148UL, 1522285852UL, "10.0.1.5" },
+ { "W2`P:-+1T[", 2945171975UL, 2946196424UL, "10.0.1.7" },
+ { "-6G7K^YDIN", 3168617340UL, 3170513015UL, "10.0.1.5" },
+ { "U>*>9ZI6V5", 668514946UL, 674097631UL, "10.0.1.5" },
+ { ".I?^6Ic9RK", 938419020UL, 942832691UL, "10.0.1.6" },
+ { "0OZH^9BKM[", 3682518606UL, 3686781297UL, "10.0.1.2" },
+ { "5?50UGZ:ML", 868610882UL, 869425986UL, "10.0.1.6" },
+ { "?K2NF@3=IU", 381218851UL, 383925769UL, "10.0.1.6" },
+ { "YI@G-2X?UB", 3688706179UL, 3693197681UL, "10.0.1.6" },
+ { "7cY</BSaL=", 3976870223UL, 3978903843UL, "10.0.1.7" },
+ { "A(`KF:[RH8", 3292979676UL, 3294849139UL, "10.0.1.6" },
+ { ";=ZT\\W^P+H", 1401102653UL, 1416290674UL, "10.0.1.6" },
+ { "b2?WFF56;R", 480494704UL, 486971192UL, "10.0.1.7" },
+ { "CTR74,J+N.", 137446045UL, 146633907UL, "10.0.1.7" },
+ { "<b;*R+QDST", 1304985302UL, 1308223778UL, "10.0.1.5" },
+ { "\\R^7=9UCG`", 126218373UL, 129199837UL, "10.0.1.6" },
+ { "1bQS5]WOXB", 1853470245UL, 1855329369UL, "10.0.1.7" },
+ { "M(@X^b[L:K", 3019630308UL, 3022260113UL, "10.0.1.4" },
+ { "431cBF8,YO", 1679726993UL, 1685224295UL, "10.0.1.1" },
+ { "(bEIQJ:E./", 2922607787UL, 2925521819UL, "10.0.1.7" },
+ { "WS/3H*)7F;", 419488232UL, 422140585UL, "10.0.1.3" },
+ { "ZJF[Ia6Q)+", 3960568056UL, 3962489998UL, "10.0.1.5" },
+ { "<]*QCK8U,>", 2590140172UL, 2598117636UL, "10.0.1.5" },
+ { "\\[a\\^=V_M0", 689410119UL, 698690782UL, "10.0.1.7" },
+ { "7;RM+8J9YC", 1530175299UL, 1531107082UL, "10.0.1.7" },
+ { "4*=.SPR[AV", 3928582722UL, 3928853792UL, "10.0.1.3" },
+ { "-2F+^88P4U", 3023552752UL, 3025823613UL, "10.0.1.7" },
+ { "X;-F`(N?9D", 570465234UL, 572485994UL, "10.0.1.5" },
+ { "R=F_D-K2a]", 1287750228UL, 1290935562UL, "10.0.1.1" },
+ { "X*+2aaC.EG", 3200948713UL, 3201088518UL, "10.0.1.3" },
+ { "[1ZXONX2]a", 4108881567UL, 4109865744UL, "10.0.1.7" },
+ { "FL;\\GWacaV", 458449508UL, 467374054UL, "10.0.1.7" },
+ { "\\MQ_XNT7L-", 1259349383UL, 1259509450UL, "10.0.1.5" },
+ { "VD6D0]ba_\\", 3842502950UL, 3842588691UL, "10.0.1.7" },
+};
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached prepend") {
+ pair<string, MemcachedCluster> tests[] = {
+ {"network", MemcachedCluster::network()},
+ {"socket", MemcachedCluster::socket()}
+ };
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ test.flush();
+
+ REQUIRE_RC(MEMCACHED_NOTSTORED, memcached_prepend(memc, S(__func__), S("fail"), 0, 0));
+ REQUIRE_SUCCESS(memcached_set(memc, S(__func__), S("initial"), 0, 0));
+ REQUIRE_SUCCESS(memcached_prepend(memc, S(__func__), S("pre1"), 0, 0));
+ REQUIRE_SUCCESS(memcached_prepend(memc, S(__func__), S("pre2"), 0, 0));
+
+ memcached_return_t rc;
+ uint32_t flags;
+ size_t len;
+ char *val = memcached_get(memc, S(__func__), &len, &flags, &rc);
+ Malloced v(val);
+ REQUIRE_SUCCESS(rc);
+ REQUIRE("pre2pre1initial" == string(val, len));
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached regression binary_block_add") {
+ auto test = MemcachedCluster::network();
+ auto memc = &test.memc;
+ auto blob = random_ascii_string(1024);
+
+ test.enableBinaryProto();
+
+ for (auto i = 0; i < 20480; ++i) {
+ auto rkey = random_ascii_string(12);
+ memcached_return_t rc = memcached_add_by_key(memc, S("key"), rkey.c_str(), rkey.length(), blob.c_str(), blob.length(), 0, 0);
+
+ if (rc == MEMCACHED_MEMORY_ALLOCATION_FAILURE) {
+ break;
+ } else if (rc != MEMCACHED_DATA_EXISTS) {
+ REQUIRE_SUCCESS(rc);
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+
+#include "testing/lib/MemcachedCluster.hpp"
+
+static memcached_return_t server_display_function(const memcached_st *,
+ const memcached_instance_st * server,
+ void *context)
+{
+ if (context) {
+ auto bigger = reinterpret_cast<size_t *>(context);
+ REQUIRE(*bigger <= memcached_server_port(server));
+ *bigger = memcached_server_port(server);
+ }
+ return MEMCACHED_SUCCESS;
+}
+
+TEST_CASE("memcached servers") {
+ SECTION("memcached_servers_parse") {
+ SECTION("does not leak memory") {
+ memcached_server_st *s = memcached_servers_parse("1.2.3.4:1234");
+ REQUIRE(s);
+ memcached_server_free(s);
+ }
+ }
+
+ SECTION("memcached_server_list") {
+ SECTION("append with weight - all zeros") {
+ memcached_server_st *sl = memcached_server_list_append_with_weight(
+ nullptr, nullptr, 0, 0, 0
+ );
+ REQUIRE(sl);
+ memcached_server_list_free(sl);
+ }
+ SECTION("append with weight - host set only") {
+ memcached_server_st *sl = memcached_server_list_append_with_weight(
+ nullptr, "localhost", 0, 0, 0
+ );
+ REQUIRE(sl);
+ memcached_server_list_free(sl);
+ }
+ SECTION("append with weight - error set only") {
+ memcached_return_t rc;
+ memcached_server_st *sl = memcached_server_list_append_with_weight(
+ nullptr, nullptr, 0, 0, &rc
+ );
+ REQUIRE(sl);
+ REQUIRE(MEMCACHED_SUCCESS == rc);
+ memcached_server_list_free(sl);
+ }
+ }
+
+ SECTION("no configured servers") {
+ MemcachedPtr memc;
+
+ REQUIRE(MEMCACHED_NO_SERVERS == memcached_increment(*memc, S("key"), 1, nullptr));
+ }
+
+ SECTION("sort") {
+ MemcachedPtr memc;
+ auto local_memc = *memc;
+ LoneReturnMatcher test{local_memc};
+ size_t bigger = 0; /* Prime the value for the test_true in server_display_function */
+ memcached_server_fn callbacks[1]{server_display_function};
+
+ REQUIRE_SUCCESS(memcached_behavior_set(local_memc, MEMCACHED_BEHAVIOR_SORT_HOSTS, 1));
+
+ SECTION("variation 1") {
+ for (uint32_t x = 0; x < 7; x++) {
+ REQUIRE_SUCCESS(memcached_server_add_with_weight(local_memc, "localhost", random_port(), 0));
+ REQUIRE(x + 1 == memcached_server_count(local_memc));
+ }
+ memcached_server_cursor(local_memc, callbacks, &bigger, 1);
+ }
+
+ SECTION("variation 2") {
+ const memcached_instance_st *instance;
+
+ REQUIRE_SUCCESS(memcached_server_add_with_weight(local_memc, "MEMCACHED_BEHAVIOR_SORT_HOSTS", 43043, 0));
+ instance = memcached_server_instance_by_position(local_memc, 0);
+ REQUIRE(in_port_t(43043) == memcached_server_port(instance));
+
+ REQUIRE_SUCCESS(memcached_server_add_with_weight(local_memc, "MEMCACHED_BEHAVIOR_SORT_HOSTS", 43042, 0));
+
+ instance = memcached_server_instance_by_position(local_memc, 0);
+ REQUIRE(in_port_t(43042) == memcached_server_port(instance));
+
+ instance = memcached_server_instance_by_position(local_memc, 1);
+ REQUIRE(in_port_t(43043) == memcached_server_port(instance));
+
+ memcached_server_cursor(local_memc, callbacks, &bigger, 1);
+ }
+ }
+
+ SECTION("regression lp:1048945") {
+ MemcachedPtr memc_ptr(memcached_create(nullptr));
+ auto memc = *memc_ptr;
+ LoneReturnMatcher test{memc};
+ memcached_return status;
+
+ auto list = memcached_server_list_append_with_weight(nullptr, "a", 11211, 0, &status);
+ REQUIRE_SUCCESS(status);
+
+ list = memcached_server_list_append_with_weight(list, "b", 11211, 0, &status);
+ REQUIRE_SUCCESS(status);
+
+ list = memcached_server_list_append_with_weight(list, "c", 11211, 0, &status);
+ REQUIRE_SUCCESS(status);
+
+ REQUIRE(3 == memcached_server_list_count(list));
+
+ REQUIRE_SUCCESS(memcached_server_push(memc, list));
+ REQUIRE_SUCCESS(memcached_server_push(memc, list));
+ memcached_server_list_free(list);
+
+ auto server = memcached_server_by_key(memc, S(__func__), &status);
+ REQUIRE(server);
+ REQUIRE_SUCCESS(status);
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+TEST_CASE("memcached simple") {
+ pair<string, MemcachedCluster> tests[] = {
+ {"network", MemcachedCluster::network()},
+ {"socket", MemcachedCluster::socket()}
+ };
+
+ LOOPED_SECTION(tests) {
+ auto memc = &test.memc;
+
+ SECTION("flush") {
+ uint64_t id = memcached_query_id(memc);
+ REQUIRE_SUCCESS(memcached_flush(memc, 0));
+ REQUIRE(id + 1 == memcached_query_id(memc));
+ }
+
+ uint64_t buffered = GENERATE(0, 1);
+ uint64_t binary = GENERATE(0, 1);
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, buffered));
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, binary));
+
+ DYNAMIC_SECTION("set (buffered=" << buffered << ",binary=" << binary << ")") {
+ for (auto i = 0; i < 10; ++i) {
+ REQUIRE_RC(buffered ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS,
+ memcached_set(memc, S(__func__), S(__func__), 0, 0));
+ }
+ }
+
+ DYNAMIC_SECTION("add (buffered=" << buffered << ",binary=" << binary << ")") {
+ memcached_return_t rc;
+
+ Malloced empty(memcached_get(memc, S(__func__), nullptr, nullptr, &rc));
+ REQUIRE_FALSE(*empty);
+ REQUIRE_RC(MEMCACHED_NOTFOUND, rc);
+
+ REQUIRE_RC(buffered ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS,
+ memcached_set(memc, S(__func__), S(__func__), 0, 0));
+
+ // flush any pending requests
+ memcached_quit(memc);
+
+ uint32_t flags;
+ size_t len;
+ Malloced val(memcached_get(memc, S(__func__), &len, &flags, &rc));
+
+ REQUIRE(*val);
+ REQUIRE(string(__func__) == string(*val, len));
+
+ REQUIRE_RC(binary ? MEMCACHED_DATA_EXISTS : MEMCACHED_NOTSTORED,
+ memcached_add(memc, S(__func__), S("update"), 0, 0));
+ }
+
+ DYNAMIC_SECTION("replace (buffered=" << buffered << ",binary=" << binary << ")") {
+ REQUIRE_RC(buffered ? MEMCACHED_BUFFERED : MEMCACHED_SUCCESS,
+ memcached_set(memc, S(__func__), S(__func__), 0, 0));
+
+ REQUIRE_SUCCESS(memcached_replace(memc, S(__func__), S("replaced"), 0, 0));
+ }
+ }
+}
--- /dev/null
+#include "testing/lib/common.hpp"
+#include "testing/lib/MemcachedCluster.hpp"
+
+enum action_t {ADD, REPLACE, SET, APPEND, PREPEND};
+inline action_t operator ++ (action_t &a) {
+ return a = static_cast<action_t>(underlying_type<action_t>::type(a) + 1);
+}
+
+constexpr static const int keys = 5000;
+
+TEST_CASE("memcached noreply") {
+ auto test{MemcachedCluster::mixed()};
+ auto memc = &test.memc;
+
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NOREPLY, true));
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, true));
+ REQUIRE_SUCCESS(memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SUPPORT_CAS, true));
+
+ for (auto action = ADD; action <= PREPEND; ++action) {
+ for (auto i = 0; i < keys; ++i) {
+ auto key = to_string(i);
+ memcached_return_t rc;
+
+ switch (action) {
+ case ADD: rc = memcached_add(memc, key.c_str(), key.length(), key.c_str(), key.length(), 0, 0); break;
+ case REPLACE: rc = memcached_replace(memc, key.c_str(), key.length(), key.c_str(), key.length(), 0, 0); break;
+ case SET: rc = memcached_set(memc, key.c_str(), key.length(), key.c_str(), key.length(), 0, 0); break;
+ case APPEND: rc = memcached_append(memc, key.c_str(), key.length(), key.c_str(), key.length(), 0, 0); break;
+ case PREPEND: rc = memcached_prepend(memc, key.c_str(), key.length(), key.c_str(), key.length(), 0, 0); break;
+ default: FAIL();
+ }
+
+ if (rc != MEMCACHED_BUFFERED) {
+ REQUIRE_SUCCESS(rc);
+ }
+ }
+
+ REQUIRE_SUCCESS(memcached_flush_buffers(memc));
+
+ for (auto i = 0; i < keys; ++i) {
+ auto key = to_string(i);
+
+ size_t len;
+ memcached_return_t rc;
+ uint32_t flags;
+ Malloced val(memcached_get(memc, key.c_str(), key.length(), &len, &flags, &rc));
+
+ REQUIRE_SUCCESS(rc);
+ REQUIRE(*val);
+
+ switch (action) {
+ case ADD: [[fallthrough]];
+ case REPLACE: [[fallthrough]];
+ case SET: REQUIRE(key == *val); break;
+ case APPEND: REQUIRE(key + key == *val); break;
+ case PREPEND: REQUIRE(key + key + key == *val); break;
+ default: FAIL();
+ }
+ }
+ }
+}
+++ /dev/null
-servers localhost:11221,
- localhost : 11221
-
-
-retvalue 1