redis++ binaries for mac

This commit is contained in:
Grant Limberg 2020-05-11 15:24:13 -07:00
commit 8f3a0b17ad
No known key found for this signature in database
GPG key ID: 2BA62CCABBB4095A
96 changed files with 35078 additions and 0 deletions

View file

@ -0,0 +1,83 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
struct BenchmarkOptions {
std::size_t pool_size = 5;
std::size_t thread_num = 10;
std::size_t total_request_num = 100000;
std::size_t key_len = 10;
std::size_t val_len = 10;
};
template <typename RedisInstance>
class BenchmarkTest {
public:
BenchmarkTest(const BenchmarkOptions &opts, RedisInstance &instance);
~BenchmarkTest() {
_cleanup();
}
void run();
private:
template <typename Func>
void _run(const std::string &title, Func &&func);
template <typename Func>
std::size_t _run(Func &&func, std::size_t request_num);
void _test_get();
std::vector<std::string> _gen_keys() const;
std::string _gen_value() const;
void _cleanup();
const std::string& _key(std::size_t idx) const {
return _keys[idx % _keys.size()];
}
BenchmarkOptions _opts;
RedisInstance &_redis;
std::vector<std::string> _keys;
std::string _value;
};
}
}
}
#include "benchmark_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_H

View file

@ -0,0 +1,178 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP
#include <chrono>
#include <random>
#include <future>
#include <algorithm>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
BenchmarkTest<RedisInstance>::BenchmarkTest(const BenchmarkOptions &opts,
RedisInstance &instance) : _opts(opts), _redis(instance) {
REDIS_ASSERT(_opts.pool_size > 0
&& _opts.thread_num > 0
&& _opts.total_request_num > 0
&& _opts.key_len > 0
&& _opts.val_len > 0,
"Invalid benchmark test options.");
_keys = _gen_keys();
_value = _gen_value();
}
template <typename RedisInstance>
void BenchmarkTest<RedisInstance>::run() {
_cleanup();
_run("SET key value", [this](std::size_t idx) { this->_redis.set(this->_key(idx), _value); });
_run("GET key", [this](std::size_t idx) {
auto res = this->_redis.get(this->_key(idx));
(void)res;
});
_cleanup();
_run("LPUSH key value", [this](std::size_t idx) {
this->_redis.lpush(this->_key(idx), _value);
});
_run("LRANGE key 0 10", [this](std::size_t idx) {
std::vector<std::string> res;
res.reserve(10);
this->_redis.lrange(this->_key(idx), 0, 10, std::back_inserter(res));
});
_run("LPOP key", [this](std::size_t idx) {
auto res = this->_redis.lpop(this->_key(idx));
(void)res;
});
_cleanup();
_run("INCR key", [this](std::size_t idx) {
auto num = this->_redis.incr(this->_key(idx));
(void)num;
});
_cleanup();
_run("SADD key member", [this](std::size_t idx) {
auto num = this->_redis.sadd(this->_key(idx), _value);
(void)num;
});
_run("SPOP key", [this](std::size_t idx) {
auto res = this->_redis.spop(this->_key(idx));
(void)res;
});
_cleanup();
}
template <typename RedisInstance>
template <typename Func>
void BenchmarkTest<RedisInstance>::_run(const std::string &title, Func &&func) {
auto thread_num = _opts.thread_num;
auto requests_per_thread = _opts.total_request_num / thread_num;
auto total_request_num = requests_per_thread * thread_num;
std::vector<std::future<std::size_t>> res;
res.reserve(thread_num);
res.push_back(std::async(std::launch::async,
[this](Func &&func, std::size_t request_num) {
return this->_run(std::forward<Func>(func), request_num);
},
std::forward<Func>(func),
requests_per_thread));
auto total_in_msec = 0;
for (auto &fut : res) {
total_in_msec += fut.get();
}
auto total_in_sec = total_in_msec * 1.0 / 1000;
auto avg = total_in_msec * 1.0 / total_request_num;
auto ops = static_cast<std::size_t>(1000 / avg);
std::cout << "-----" << title << "-----" << std::endl;
std::cout << total_request_num << " requests cost " << total_in_sec << " seconds" << std::endl;
std::cout << ops << " requests per second" << std::endl;
}
template <typename RedisInstance>
template <typename Func>
std::size_t BenchmarkTest<RedisInstance>::_run(Func &&func, std::size_t request_num) {
auto start = std::chrono::steady_clock::now();
for (auto idx = 0U; idx != request_num; ++idx) {
func(idx);
}
auto stop = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
}
template <typename RedisInstance>
std::vector<std::string> BenchmarkTest<RedisInstance>::_gen_keys() const {
const auto KEY_NUM = 100;
std::vector<std::string> res;
res.reserve(KEY_NUM);
std::default_random_engine engine(std::random_device{}());
std::uniform_int_distribution<int> uniform_dist(0, 255);
for (auto i = 0; i != KEY_NUM; ++i) {
std::string str;
str.reserve(_opts.key_len);
for (std::size_t j = 0; j != _opts.key_len; ++j) {
str.push_back(static_cast<char>(uniform_dist(engine)));
}
res.push_back(str);
}
return res;
}
template <typename RedisInstance>
std::string BenchmarkTest<RedisInstance>::_gen_value() const {
return std::string(_opts.val_len, 'x');
}
template <typename RedisInstance>
void BenchmarkTest<RedisInstance>::_cleanup() {
for (const auto &key : _keys) {
_redis.del(key);
}
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_BENCHMARK_TEST_HPP

View file

@ -0,0 +1,49 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class ConnectionCmdTest {
public:
explicit ConnectionCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _run(Redis &redis);
RedisInstance &_redis;
};
}
}
}
#include "connection_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_H

View file

@ -0,0 +1,50 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void ConnectionCmdTest<RedisInstance>::run() {
cluster_specializing_test(*this, &ConnectionCmdTest<RedisInstance>::_run, _redis);
}
template <typename RedisInstance>
void ConnectionCmdTest<RedisInstance>::_run(Redis &instance) {
auto message = std::string("hello");
REDIS_ASSERT(instance.echo(message) == message, "failed to test echo");
REDIS_ASSERT(instance.ping() == "PONG", "failed to test ping");
REDIS_ASSERT(instance.ping(message) == message, "failed to test ping");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_CONNECTION_CMDS_TEST_HPP

View file

@ -0,0 +1,47 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class GeoCmdTest {
public:
explicit GeoCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
RedisInstance &_redis;
};
}
}
}
#include "geo_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_H

View file

@ -0,0 +1,149 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP
#include <vector>
#include <tuple>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void GeoCmdTest<RedisInstance>::run() {
auto key = test_key("geo");
auto dest = test_key("dest");
KeyDeleter<RedisInstance> deleter(_redis, {key, dest});
auto members = {
std::make_tuple("m1", 10.0, 11.0),
std::make_tuple("m2", 10.1, 11.1),
std::make_tuple("m3", 10.2, 11.2)
};
REDIS_ASSERT(_redis.geoadd(key, std::make_tuple("m1", 10.0, 11.0)) == 1,
"failed to test geoadd");
REDIS_ASSERT(_redis.geoadd(key, members) == 2, "failed to test geoadd");
auto dist = _redis.geodist(key, "m1", "m4", GeoUnit::KM);
REDIS_ASSERT(!dist, "failed to test geodist with nonexistent member");
std::vector<OptionalString> hashes;
_redis.geohash(key, {"m1", "m4"}, std::back_inserter(hashes));
REDIS_ASSERT(hashes.size() == 2, "failed to test geohash");
REDIS_ASSERT(bool(hashes[0]) && *(hashes[0]) == "s1zned3z8u0" && !(hashes[1]),
"failed to test geohash");
hashes.clear();
_redis.geohash(key, {"m4"}, std::back_inserter(hashes));
REDIS_ASSERT(hashes.size() == 1 && !(hashes[0]), "failed to test geohash");
std::vector<Optional<std::pair<double, double>>> pos;
_redis.geopos(key, {"m4"}, std::back_inserter(pos));
REDIS_ASSERT(pos.size() == 1 && !(pos[0]), "failed to test geopos");
auto num = _redis.georadius(key,
std::make_pair(10.1, 11.1),
100,
GeoUnit::KM,
dest,
false,
10);
REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadius with store option");
std::vector<std::string> mems;
_redis.georadius(key,
std::make_pair(10.1, 11.1),
100,
GeoUnit::KM,
10,
true,
std::back_inserter(mems));
REDIS_ASSERT(mems.size() == 3, "failed to test georadius with no option");
std::vector<std::tuple<std::string, double>> with_dist;
_redis.georadius(key,
std::make_pair(10.1, 11.1),
100,
GeoUnit::KM,
10,
true,
std::back_inserter(with_dist));
REDIS_ASSERT(with_dist.size() == 3, "failed to test georadius with dist");
std::vector<std::tuple<std::string, double, std::pair<double, double>>> with_dist_coord;
_redis.georadius(key,
std::make_pair(10.1, 11.1),
100,
GeoUnit::KM,
10,
true,
std::back_inserter(with_dist_coord));
REDIS_ASSERT(with_dist_coord.size() == 3, "failed to test georadius with dist and coord");
num = _redis.georadiusbymember(key,
"m1",
100,
GeoUnit::KM,
dest,
false,
10);
REDIS_ASSERT(bool(num) && *num == 3, "failed to test georadiusbymember with store option");
mems.clear();
_redis.georadiusbymember(key,
"m1",
100,
GeoUnit::KM,
10,
true,
std::back_inserter(mems));
REDIS_ASSERT(mems.size() == 3, "failed to test georadiusbymember with no option");
with_dist.clear();
_redis.georadiusbymember(key,
"m1",
100,
GeoUnit::KM,
10,
true,
std::back_inserter(with_dist));
REDIS_ASSERT(with_dist.size() == 3, "failed to test georadiusbymember with dist");
with_dist_coord.clear();
_redis.georadiusbymember(key,
"m1",
100,
GeoUnit::KM,
10,
true,
std::back_inserter(with_dist_coord));
REDIS_ASSERT(with_dist_coord.size() == 3,
"failed to test georadiusbymember with dist and coord");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_GEO_CMDS_TEST_HPP

View file

@ -0,0 +1,55 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class HashCmdTest {
public:
explicit HashCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_hash();
void _test_hash_batch();
void _test_numeric();
void _test_hscan();
RedisInstance &_redis;
};
}
}
}
#include "hash_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_H

View file

@ -0,0 +1,177 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP
#include <unordered_map>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void HashCmdTest<RedisInstance>::run() {
_test_hash();
_test_hash_batch();
_test_numeric();
_test_hscan();
}
template <typename RedisInstance>
void HashCmdTest<RedisInstance>::_test_hash() {
auto key = test_key("hash");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto f1 = std::string("f1");
auto v1 = std::string("v1");
auto f2 = std::string("f2");
auto v2 = std::string("v2");
auto f3 = std::string("f3");
auto v3 = std::string("v3");
REDIS_ASSERT(_redis.hset(key, f1, v1), "failed to test hset");
REDIS_ASSERT(!_redis.hset(key, f1, v2), "failed to test hset with exist field");
auto res = _redis.hget(key, f1);
REDIS_ASSERT(res && *res == v2, "failed to test hget");
REDIS_ASSERT(_redis.hsetnx(key, f2, v1), "failed to test hsetnx");
REDIS_ASSERT(!_redis.hsetnx(key, f2, v2), "failed to test hsetnx with exist field");
res = _redis.hget(key, f2);
REDIS_ASSERT(res && *res == v1, "failed to test hget");
REDIS_ASSERT(!_redis.hexists(key, f3), "failed to test hexists");
REDIS_ASSERT(_redis.hset(key, std::make_pair(f3, v3)), "failed to test hset");
REDIS_ASSERT(_redis.hexists(key, f3), "failed to test hexists");
REDIS_ASSERT(_redis.hlen(key) == 3, "failed to test hlen");
REDIS_ASSERT(_redis.hstrlen(key, f1) == static_cast<long long>(v1.size()),
"failed to test hstrlen");
REDIS_ASSERT(_redis.hdel(key, f1) == 1, "failed to test hdel");
REDIS_ASSERT(_redis.hdel(key, {f1, f2, f3}) == 2, "failed to test hdel range");
}
template <typename RedisInstance>
void HashCmdTest<RedisInstance>::_test_hash_batch() {
auto key = test_key("hash");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto f1 = std::string("f1");
auto v1 = std::string("v1");
auto f2 = std::string("f2");
auto v2 = std::string("v2");
auto f3 = std::string("f3");
_redis.hmset(key, {std::make_pair(f1, v1),
std::make_pair(f2, v2)});
std::vector<std::string> fields;
_redis.hkeys(key, std::back_inserter(fields));
REDIS_ASSERT(fields.size() == 2, "failed to test hkeys");
std::vector<std::string> vals;
_redis.hvals(key, std::back_inserter(vals));
REDIS_ASSERT(vals.size() == 2, "failed to test hvals");
std::unordered_map<std::string, std::string> items;
_redis.hgetall(key, std::inserter(items, items.end()));
REDIS_ASSERT(items.size() == 2 && items[f1] == v1 && items[f2] == v2,
"failed to test hgetall");
std::vector<std::pair<std::string, std::string>> item_vec;
_redis.hgetall(key, std::back_inserter(item_vec));
REDIS_ASSERT(item_vec.size() == 2, "failed to test hgetall");
std::vector<OptionalString> res;
_redis.hmget(key, {f1, f2, f3}, std::back_inserter(res));
REDIS_ASSERT(res.size() == 3
&& bool(res[0]) && *(res[0]) == v1
&& bool(res[1]) && *(res[1]) == v2
&& !res[2],
"failed to test hmget");
}
template <typename RedisInstance>
void HashCmdTest<RedisInstance>::_test_numeric() {
auto key = test_key("numeric");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto field = "field";
REDIS_ASSERT(_redis.hincrby(key, field, 1) == 1, "failed to test hincrby");
REDIS_ASSERT(_redis.hincrby(key, field, -1) == 0, "failed to test hincrby");
REDIS_ASSERT(_redis.hincrbyfloat(key, field, 1.5) == 1.5, "failed to test hincrbyfloat");
}
template <typename RedisInstance>
void HashCmdTest<RedisInstance>::_test_hscan() {
auto key = test_key("hscan");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto items = std::unordered_map<std::string, std::string>{
std::make_pair("f1", "v1"),
std::make_pair("f2", "v2"),
std::make_pair("f3", "v3"),
};
_redis.hmset(key, items.begin(), items.end());
std::unordered_map<std::string, std::string> item_map;
auto cursor = 0;
while (true) {
cursor = _redis.hscan(key, cursor, "f*", 2, std::inserter(item_map, item_map.end()));
if (cursor == 0) {
break;
}
}
REDIS_ASSERT(item_map == items, "failed to test hscan with pattern and count");
std::vector<std::pair<std::string, std::string>> item_vec;
cursor = 0;
while (true) {
cursor = _redis.hscan(key, cursor, std::back_inserter(item_vec));
if (cursor == 0) {
break;
}
}
REDIS_ASSERT(item_vec.size() == items.size(), "failed to test hscan");
for (const auto &ele : item_vec) {
REDIS_ASSERT(items.find(ele.first) != items.end(), "failed to test hscan");
}
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HASH_CMDS_TEST_HPP

View file

@ -0,0 +1,47 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class HyperloglogCmdTest {
public:
explicit HyperloglogCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
RedisInstance &_redis;
};
}
}
}
#include "hyperloglog_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_H

View file

@ -0,0 +1,67 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void HyperloglogCmdTest<RedisInstance>::run() {
auto k1 = test_key("k1");
auto k2 = test_key("k2");
auto k3 = test_key("k3");
KeyDeleter<RedisInstance> deleter(_redis, {k1, k2, k3});
_redis.pfadd(k1, "a");
auto members1 = {"b", "c", "d", "e", "f", "g"};
_redis.pfadd(k1, members1);
auto cnt = _redis.pfcount(k1);
auto err = cnt * 1.0 / (1 + members1.size());
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfadd and pfcount");
auto members2 = {"a", "b", "c", "h", "i", "j", "k"};
_redis.pfadd(k2, members2);
auto total = 1 + members1.size() + members2.size() - 3;
cnt = _redis.pfcount({k1, k2});
err = cnt * 1.0 / total;
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount");
_redis.pfmerge(k3, {k1, k2});
cnt = _redis.pfcount(k3);
err = cnt * 1.0 / total;
REDIS_ASSERT(err < 1.02 && err > 0.98, "failed to test pfcount");
_redis.pfmerge(k3, k1);
REDIS_ASSERT(cnt == _redis.pfcount(k3), "failed to test pfmerge");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_HYPERLOGLOG_CMDS_TEST_HPP

View file

@ -0,0 +1,55 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class KeysCmdTest {
public:
explicit KeysCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_key();
void _test_randomkey(Redis &instance);
void _test_ttl();
void _test_scan(Redis &instance);
RedisInstance &_redis;
};
}
}
}
#include "keys_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_H

View file

@ -0,0 +1,166 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP
#include <vector>
#include <unordered_set>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void KeysCmdTest<RedisInstance>::run() {
_test_key();
cluster_specializing_test(*this, &KeysCmdTest<RedisInstance>::_test_randomkey, _redis);
_test_ttl();
cluster_specializing_test(*this, &KeysCmdTest<RedisInstance>::_test_scan, _redis);
}
template <typename RedisInstance>
void KeysCmdTest<RedisInstance>::_test_key() {
auto key = test_key("key");
auto dest = test_key("dest");
auto new_key_name = test_key("new-key");
auto not_exist_key = test_key("not-exist");
KeyDeleter<RedisInstance> deleter(_redis, {key, dest, new_key_name});
REDIS_ASSERT(_redis.exists(key) == 0, "failed to test exists");
auto val = std::string("val");
_redis.set(key, val);
REDIS_ASSERT(_redis.exists({key, not_exist_key}) == 1, "failed to test exists");
auto new_val = _redis.dump(key);
REDIS_ASSERT(bool(new_val), "failed to test dump");
_redis.restore(dest, *new_val, std::chrono::seconds(1000));
new_val = _redis.get(dest);
REDIS_ASSERT(bool(new_val) && *new_val == val, "failed to test dump and restore");
_redis.rename(dest, new_key_name);
bool not_exist = false;
try {
_redis.rename(not_exist_key, new_key_name);
} catch (const Error &e) {
not_exist = true;
}
REDIS_ASSERT(not_exist, "failed to test rename with nonexistent key");
REDIS_ASSERT(_redis.renamenx(new_key_name, dest), "failed to test renamenx");
REDIS_ASSERT(_redis.touch(not_exist_key) == 0, "failed to test touch");
REDIS_ASSERT(_redis.touch({key, dest, new_key_name}) == 2, "failed to test touch");
REDIS_ASSERT(_redis.type(key) == "string", "failed to test type");
REDIS_ASSERT(_redis.del({new_key_name, dest}) == 1, "failed to test del");
REDIS_ASSERT(_redis.unlink({new_key_name, key}) == 1, "failed to test unlink");
}
template <typename RedisInstance>
void KeysCmdTest<RedisInstance>::_test_randomkey(Redis &instance) {
auto key = test_key("randomkey");
KeyDeleter<Redis> deleter(instance, key);
instance.set(key, "value");
auto rand_key = instance.randomkey();
REDIS_ASSERT(bool(rand_key), "failed to test randomkey");
}
template <typename RedisInstance>
void KeysCmdTest<RedisInstance>::_test_ttl() {
using namespace std::chrono;
auto key = test_key("ttl");
KeyDeleter<RedisInstance> deleter(_redis, key);
_redis.set(key, "val", seconds(100));
auto ttl = _redis.ttl(key);
REDIS_ASSERT(ttl > 0 && ttl <= 100, "failed to test ttl");
REDIS_ASSERT(_redis.persist(key), "failed to test persist");
ttl = _redis.ttl(key);
REDIS_ASSERT(ttl == -1, "failed to test ttl");
REDIS_ASSERT(_redis.expire(key, seconds(100)),
"failed to test expire");
auto tp = time_point_cast<seconds>(system_clock::now() + seconds(100));
REDIS_ASSERT(_redis.expireat(key, tp), "failed to test expireat");
ttl = _redis.ttl(key);
REDIS_ASSERT(ttl > 0, "failed to test expireat");
REDIS_ASSERT(_redis.pexpire(key, milliseconds(100000)), "failed to test expire");
auto pttl = _redis.pttl(key);
REDIS_ASSERT(pttl > 0 && pttl <= 100000, "failed to test pttl");
auto tp_milli = time_point_cast<milliseconds>(system_clock::now() + milliseconds(100000));
REDIS_ASSERT(_redis.pexpireat(key, tp_milli), "failed to test pexpireat");
pttl = _redis.pttl(key);
REDIS_ASSERT(pttl > 0, "failed to test pexpireat");
}
template <typename RedisInstance>
void KeysCmdTest<RedisInstance>::_test_scan(Redis &instance) {
std::string key_pattern = "!@#$%^&()_+alseufoawhnlkszd";
auto k1 = test_key(key_pattern + "k1");
auto k2 = test_key(key_pattern + "k2");
auto k3 = test_key(key_pattern + "k3");
auto keys = {k1, k2, k3};
KeyDeleter<Redis> deleter(instance, keys);
instance.set(k1, "v");
instance.set(k2, "v");
instance.set(k3, "v");
auto cursor = 0;
std::unordered_set<std::string> res;
while (true) {
cursor = instance.scan(cursor, "*" + key_pattern + "*", 2, std::inserter(res, res.end()));
if (cursor == 0) {
break;
}
}
REDIS_ASSERT(res == std::unordered_set<std::string>(keys),
"failed to test scan");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_KEYS_CMDS_TEST_HPP

View file

@ -0,0 +1,55 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class ListCmdTest {
public:
explicit ListCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_lpoppush();
void _test_rpoppush();
void _test_list();
void _test_blocking();
RedisInstance &_redis;
};
}
}
}
#include "list_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_H

View file

@ -0,0 +1,154 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void ListCmdTest<RedisInstance>::run() {
_test_lpoppush();
_test_rpoppush();
_test_list();
_test_blocking();
}
template <typename RedisInstance>
void ListCmdTest<RedisInstance>::_test_lpoppush() {
auto key = test_key("lpoppush");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto item = _redis.lpop(key);
REDIS_ASSERT(!item, "failed to test lpop");
REDIS_ASSERT(_redis.lpushx(key, "1") == 0, "failed to test lpushx");
REDIS_ASSERT(_redis.lpush(key, "1") == 1, "failed to test lpush");
REDIS_ASSERT(_redis.lpushx(key, "2") == 2, "failed to test lpushx");
REDIS_ASSERT(_redis.lpush(key, {"3", "4", "5"}) == 5, "failed to test lpush");
item = _redis.lpop(key);
REDIS_ASSERT(item && *item == "5", "failed to test lpop");
}
template <typename RedisInstance>
void ListCmdTest<RedisInstance>::_test_rpoppush() {
auto key = test_key("rpoppush");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto item = _redis.rpop(key);
REDIS_ASSERT(!item, "failed to test rpop");
REDIS_ASSERT(_redis.rpushx(key, "1") == 0, "failed to test rpushx");
REDIS_ASSERT(_redis.rpush(key, "1") == 1, "failed to test rpush");
REDIS_ASSERT(_redis.rpushx(key, "2") == 2, "failed to test rpushx");
REDIS_ASSERT(_redis.rpush(key, {"3", "4", "5"}) == 5, "failed to test rpush");
item = _redis.rpop(key);
REDIS_ASSERT(item && *item == "5", "failed to test rpop");
}
template <typename RedisInstance>
void ListCmdTest<RedisInstance>::_test_list() {
auto key = test_key("list");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto item = _redis.lindex(key, 0);
REDIS_ASSERT(!item, "failed to test lindex");
_redis.lpush(key, {"1", "2", "3", "4", "5"});
REDIS_ASSERT(_redis.lrem(key, 0, "3") == 1, "failed to test lrem");
REDIS_ASSERT(_redis.linsert(key, InsertPosition::BEFORE, "2", "3") == 5,
"failed to test lindex");
REDIS_ASSERT(_redis.llen(key) == 5, "failed to test llen");
_redis.lset(key, 0, "6");
item = _redis.lindex(key, 0);
REDIS_ASSERT(item && *item == "6", "failed to test lindex");
_redis.ltrim(key, 0, 2);
std::vector<std::string> res;
_redis.lrange(key, 0, -1, std::back_inserter(res));
REDIS_ASSERT(res == std::vector<std::string>({"6", "4", "3"}), "failed to test ltrim");
}
template <typename RedisInstance>
void ListCmdTest<RedisInstance>::_test_blocking() {
auto k1 = test_key("k1");
auto k2 = test_key("k2");
auto k3 = test_key("k3");
auto keys = {k1, k2, k3};
KeyDeleter<RedisInstance> deleter(_redis, keys);
std::string val("value");
_redis.lpush(k1, val);
auto res = _redis.blpop(keys.begin(), keys.end());
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop");
res = _redis.brpop(keys, std::chrono::seconds(1));
REDIS_ASSERT(!res, "failed to test brpop with timeout");
_redis.lpush(k1, val);
res = _redis.blpop(k1);
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test blpop");
res = _redis.blpop(k1, std::chrono::seconds(1));
REDIS_ASSERT(!res, "failed to test blpop with timeout");
_redis.lpush(k1, val);
res = _redis.brpop(k1);
REDIS_ASSERT(res && *res == std::make_pair(k1, val), "failed to test brpop");
res = _redis.brpop(k1, std::chrono::seconds(1));
REDIS_ASSERT(!res, "failed to test brpop with timeout");
auto str = _redis.brpoplpush(k2, k3, std::chrono::seconds(1));
REDIS_ASSERT(!str, "failed to test brpoplpush with timeout");
_redis.lpush(k2, val);
str = _redis.brpoplpush(k2, k3);
REDIS_ASSERT(str && *str == val, "failed to test brpoplpush");
str = _redis.rpoplpush(k3, k2);
REDIS_ASSERT(str && *str == val, "failed to test rpoplpush");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_LIST_CMDS_TEST_HPP

View file

@ -0,0 +1,57 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class PipelineTransactionTest {
public:
explicit PipelineTransactionTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
Pipeline _pipeline(const StringView &key);
Transaction _transaction(const StringView &key, bool piped);
void _test_pipeline(const StringView &key, Pipeline &pipe);
void _test_transaction(const StringView &key, Transaction &tx);
void _test_watch();
RedisInstance &_redis;
};
}
}
}
#include "pipeline_transaction_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_H

View file

@ -0,0 +1,184 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP
#include <string>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void PipelineTransactionTest<RedisInstance>::run() {
{
auto key = test_key("pipeline");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto pipe = _pipeline(key);
_test_pipeline(key, pipe);
}
{
auto key = test_key("transaction");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto tx = _transaction(key, true);
_test_transaction(key, tx);
}
{
auto key = test_key("transaction");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto tx = _transaction(key, false);
_test_transaction(key, tx);
}
_test_watch();
}
template <typename RedisInstance>
Pipeline PipelineTransactionTest<RedisInstance>::_pipeline(const StringView &) {
return _redis.pipeline();
}
template <>
inline Pipeline PipelineTransactionTest<RedisCluster>::_pipeline(const StringView &key) {
return _redis.pipeline(key);
}
template <typename RedisInstance>
Transaction PipelineTransactionTest<RedisInstance>::_transaction(const StringView &, bool piped) {
return _redis.transaction(piped);
}
template <>
inline Transaction PipelineTransactionTest<RedisCluster>::_transaction(const StringView &key,
bool piped) {
return _redis.transaction(key, piped);
}
template <typename RedisInstance>
void PipelineTransactionTest<RedisInstance>::_test_pipeline(const StringView &key,
Pipeline &pipe) {
std::string val("value");
auto replies = pipe.set(key, val)
.get(key)
.strlen(key)
.exec();
REDIS_ASSERT(replies.get<bool>(0), "failed to test pipeline with set operation");
auto new_val = replies.get<OptionalString>(1);
std::size_t len = replies.get<long long>(2);
REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(),
"failed to test pipeline with string operations");
REDIS_ASSERT(reply::parse<bool>(replies.get(0)), "failed to test pipeline with set operation");
new_val = reply::parse<OptionalString>(replies.get(1));
len = reply::parse<long long>(replies.get(2));
REDIS_ASSERT(bool(new_val) && *new_val == val && len == val.size(),
"failed to test pipeline with string operations");
}
template <typename RedisInstance>
void PipelineTransactionTest<RedisInstance>::_test_transaction(const StringView &key,
Transaction &tx) {
std::unordered_map<std::string, std::string> m = {
std::make_pair("f1", "v1"),
std::make_pair("f2", "v2"),
std::make_pair("f3", "v3")
};
auto replies = tx.hmset(key, m.begin(), m.end())
.hgetall(key)
.hdel(key, "f1")
.exec();
replies.get<void>(0);
decltype(m) mm;
replies.get(1, std::inserter(mm, mm.end()));
REDIS_ASSERT(mm == m, "failed to test transaction");
REDIS_ASSERT(replies.get<long long>(2) == 1, "failed to test transaction");
tx.set(key, "value")
.get(key)
.incr(key);
tx.discard();
replies = tx.del(key)
.set(key, "value")
.exec();
REDIS_ASSERT(replies.get<long long>(0) == 1, "failed to test transaction");
REDIS_ASSERT(replies.get<bool>(1), "failed to test transaction");
}
template <typename RedisInstance>
void PipelineTransactionTest<RedisInstance>::_test_watch() {
auto key = test_key("watch");
KeyDeleter<RedisInstance> deleter(_redis, key);
{
auto tx = _transaction(key, false);
auto redis = tx.redis();
redis.watch(key);
auto replies = tx.set(key, "1").get(key).exec();
REDIS_ASSERT(replies.size() == 2
&& replies.template get<bool>(0) == true, "failed to test watch");
auto val = replies.template get<sw::redis::OptionalString>(1);
REDIS_ASSERT(val && *val == "1", "failed to test watch");
}
try {
auto tx = _transaction(key, false);
auto redis = tx.redis();
redis.watch(key);
// Key has been modified by other client.
_redis.set(key, "val");
// Transaction should fail, and throw WatchError
tx.set(key, "1").exec();
REDIS_ASSERT(false, "failed to test watch");
} catch (const sw::redis::WatchError &err) {
// Catch the error.
}
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_PIPELINE_TRANSACTION_TEST_HPP

View file

@ -0,0 +1,53 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class PubSubTest {
public:
explicit PubSubTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_sub_channel();
void _test_sub_pattern();
void _test_unsubscribe();
RedisInstance &_redis;
};
}
}
}
#include "pubsub_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_H

View file

@ -0,0 +1,244 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP
#include <unordered_map>
#include <unordered_set>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void PubSubTest<RedisInstance>::run() {
_test_sub_channel();
_test_sub_pattern();
_test_unsubscribe();
}
template <typename RedisInstance>
void PubSubTest<RedisInstance>::_test_sub_channel() {
auto sub = _redis.subscriber();
auto msgs = {"msg1", "msg2"};
auto channel1 = test_key("c1");
sub.on_message([&msgs, &channel1](std::string channel, std::string msg) {
static std::size_t idx = 0;
REDIS_ASSERT(channel == channel1 && msg == *(msgs.begin() + idx),
"failed to test subscribe");
++idx;
});
sub.subscribe(channel1);
// Consume the SUBSCRIBE message.
sub.consume();
for (const auto &msg : msgs) {
_redis.publish(channel1, msg);
sub.consume();
}
sub.unsubscribe(channel1);
// Consume the UNSUBSCRIBE message.
sub.consume();
auto channel2 = test_key("c2");
auto channel3 = test_key("c3");
auto channel4 = test_key("c4");
std::unordered_set<std::string> channels;
sub.on_meta([&channels](Subscriber::MsgType type,
OptionalString channel,
long long num) {
REDIS_ASSERT(bool(channel), "failed to test subscribe");
if (type == Subscriber::MsgType::SUBSCRIBE) {
auto iter = channels.find(*channel);
REDIS_ASSERT(iter == channels.end(), "failed to test subscribe");
channels.insert(*channel);
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
"failed to test subscribe");
} else if (type == Subscriber::MsgType::UNSUBSCRIBE) {
auto iter = channels.find(*channel);
REDIS_ASSERT(iter != channels.end(), "failed to test subscribe");
channels.erase(*channel);
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
"failed to test subscribe");
} else {
REDIS_ASSERT(false, "Unknown message type");
}
});
std::unordered_map<std::string, std::string> messages = {
{channel2, "msg2"},
{channel3, "msg3"},
{channel4, "msg4"},
};
sub.on_message([&messages](std::string channel, std::string msg) {
REDIS_ASSERT(messages.find(channel) != messages.end(),
"failed to test subscribe");
REDIS_ASSERT(messages[channel] == msg, "failed to test subscribe");
});
sub.subscribe({channel2, channel3, channel4});
for (std::size_t idx = 0; idx != channels.size(); ++idx) {
sub.consume();
}
for (const auto &ele : messages) {
_redis.publish(ele.first, ele.second);
sub.consume();
}
auto tmp = {channel2, channel3, channel4};
sub.unsubscribe(tmp);
for (std::size_t idx = 0; idx != tmp.size(); ++idx) {
sub.consume();
}
}
template <typename RedisInstance>
void PubSubTest<RedisInstance>::_test_sub_pattern() {
auto sub = _redis.subscriber();
auto msgs = {"msg1", "msg2"};
auto pattern1 = test_key("pattern*");
std::string channel1 = test_key("pattern1");
sub.on_pmessage([&msgs, &pattern1, &channel1](std::string pattern,
std::string channel,
std::string msg) {
static std::size_t idx = 0;
REDIS_ASSERT(pattern == pattern1
&& channel == channel1
&& msg == *(msgs.begin() + idx),
"failed to test psubscribe");
++idx;
});
sub.psubscribe(pattern1);
// Consume the PSUBSCRIBE message.
sub.consume();
for (const auto &msg : msgs) {
_redis.publish(channel1, msg);
sub.consume();
}
sub.punsubscribe(pattern1);
// Consume the PUNSUBSCRIBE message.
sub.consume();
auto channel2 = test_key("pattern22");
auto channel3 = test_key("pattern33");
auto channel4 = test_key("pattern44");
std::unordered_set<std::string> channels;
sub.on_meta([&channels](Subscriber::MsgType type,
OptionalString channel,
long long num) {
REDIS_ASSERT(bool(channel), "failed to test psubscribe");
if (type == Subscriber::MsgType::PSUBSCRIBE) {
auto iter = channels.find(*channel);
REDIS_ASSERT(iter == channels.end(), "failed to test psubscribe");
channels.insert(*channel);
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
"failed to test psubscribe");
} else if (type == Subscriber::MsgType::PUNSUBSCRIBE) {
auto iter = channels.find(*channel);
REDIS_ASSERT(iter != channels.end(), "failed to test psubscribe");
channels.erase(*channel);
REDIS_ASSERT(static_cast<std::size_t>(num) == channels.size(),
"failed to test psubscribe");
} else {
REDIS_ASSERT(false, "Unknown message type");
}
});
auto pattern2 = test_key("pattern2*");
auto pattern3 = test_key("pattern3*");
auto pattern4 = test_key("pattern4*");
std::unordered_set<std::string> patterns = {pattern2, pattern3, pattern4};
std::unordered_map<std::string, std::string> messages = {
{channel2, "msg2"},
{channel3, "msg3"},
{channel4, "msg4"},
};
sub.on_pmessage([&patterns, &messages](std::string pattern,
std::string channel,
std::string msg) {
REDIS_ASSERT(patterns.find(pattern) != patterns.end(),
"failed to test psubscribe");
REDIS_ASSERT(messages[channel] == msg, "failed to test psubscribe");
});
sub.psubscribe({pattern2, pattern3, pattern4});
for (std::size_t idx = 0; idx != channels.size(); ++idx) {
sub.consume();
}
for (const auto &ele : messages) {
_redis.publish(ele.first, ele.second);
sub.consume();
}
auto tmp = {pattern2, pattern3, pattern4};
sub.punsubscribe(tmp);
for (std::size_t idx = 0; idx != tmp.size(); ++idx) {
sub.consume();
}
}
template <typename RedisInstance>
void PubSubTest<RedisInstance>::_test_unsubscribe() {
auto sub = _redis.subscriber();
sub.on_meta([](Subscriber::MsgType type,
OptionalString channel,
long long num) {
REDIS_ASSERT(type == Subscriber::MsgType::UNSUBSCRIBE,
"failed to test unsub");
REDIS_ASSERT(!channel, "failed to test unsub");
REDIS_ASSERT(num == 0, "failed to test unsub");
});
sub.unsubscribe();
sub.consume();
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SUBPUB_TEST_HPP

View file

@ -0,0 +1,76 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class SanityTest {
public:
SanityTest(const ConnectionOptions &opts, RedisInstance &instance)
: _opts(opts), _redis(instance) {}
void run();
private:
void _test_uri_ctor();
void _ping(Redis &instance);
void _test_move_ctor();
void _test_cmdargs();
void _test_generic_command();
void _test_hash_tag();
void _test_hash_tag(std::initializer_list<std::string> keys);
std::string _test_key(const std::string &key);
void _test_ping(Redis &instance);
void _test_pipeline(const StringView &key, Pipeline &pipeline);
void _test_transaction(const StringView &key, Transaction &transaction);
Pipeline _pipeline(const StringView &key);
Transaction _transaction(const StringView &key);
ConnectionOptions _opts;
RedisInstance &_redis;
};
}
}
}
#include "sanity_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_H

View file

@ -0,0 +1,299 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void SanityTest<RedisInstance>::run() {
_test_uri_ctor();
_test_move_ctor();
cluster_specializing_test(*this, &SanityTest<RedisInstance>::_test_ping, _redis);
auto pipe_key = test_key("pipeline");
auto tx_key = test_key("transaction");
KeyDeleter<RedisInstance> deleter(_redis, {pipe_key, tx_key});
auto pipeline = _pipeline(pipe_key);
_test_pipeline(pipe_key, pipeline);
auto transaction = _transaction(tx_key);
_test_transaction(tx_key, transaction);
_test_cmdargs();
_test_generic_command();
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_uri_ctor() {
std::string uri;
switch (_opts.type) {
case sw::redis::ConnectionType::TCP:
uri = "tcp://" + _opts.host + ":" + std::to_string(_opts.port);
break;
case sw::redis::ConnectionType::UNIX:
REDIS_ASSERT(false, "NO test for UNIX Domain Socket");
break;
default:
REDIS_ASSERT(false, "Unknown connection type");
}
auto instance = RedisInstance(uri);
cluster_specializing_test(*this, &SanityTest<RedisInstance>::_ping, instance);
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_ping(Redis &instance) {
try {
auto pong = instance.ping();
REDIS_ASSERT(pong == "PONG", "Failed to test constructing Redis with uri");
} catch (const sw::redis::ReplyError &e) {
REDIS_ASSERT(e.what() == std::string("NOAUTH Authentication required."),
"Failed to test constructing Redis with uri");
}
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_move_ctor() {
auto test_move_ctor = std::move(_redis);
_redis = std::move(test_move_ctor);
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_cmdargs() {
auto lpush_num = [](Connection &connection, const StringView &key, long long num) {
connection.send("LPUSH %b %lld",
key.data(), key.size(),
num);
};
auto lpush_nums = [](Connection &connection,
const StringView &key,
const std::vector<long long> &nums) {
CmdArgs args;
args.append("LPUSH").append(key);
for (auto num : nums) {
args.append(std::to_string(num));
}
connection.send(args);
};
auto key = test_key("lpush_num");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto reply = _redis.command(lpush_num, key, 1);
REDIS_ASSERT(reply::parse<long long>(*reply) == 1, "failed to test cmdargs");
std::vector<long long> nums = {2, 3, 4, 5};
reply = _redis.command(lpush_nums, key, nums);
REDIS_ASSERT(reply::parse<long long>(*reply) == 5, "failed to test cmdargs");
std::vector<std::string> res;
_redis.lrange(key, 0, -1, std::back_inserter(res));
REDIS_ASSERT((res == std::vector<std::string>{"5", "4", "3", "2", "1"}),
"failed to test cmdargs");
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_generic_command() {
auto key = test_key("key");
auto not_exist_key = test_key("not_exist_key");
auto k1 = test_key("k1");
auto k2 = test_key("k2");
KeyDeleter<RedisInstance> deleter(_redis, {key, not_exist_key, k1, k2});
std::string cmd("set");
_redis.command(cmd, key, 123);
auto reply = _redis.command("get", key);
auto val = reply::parse<OptionalString>(*reply);
REDIS_ASSERT(val && *val == "123", "failed to test generic command");
val = _redis.template command<OptionalString>("get", key);
REDIS_ASSERT(val && *val == "123", "failed to test generic command");
std::vector<OptionalString> res;
_redis.command("mget", key, not_exist_key, std::back_inserter(res));
REDIS_ASSERT(res.size() == 2 && res[0] && *res[0] == "123" && !res[1],
"failed to test generic command");
reply = _redis.command("incr", key);
REDIS_ASSERT(reply::parse<long long>(*reply) == 124, "failed to test generic command");
_redis.command("mset", k1.c_str(), "v", k2.c_str(), "v");
reply = _redis.command("mget", k1, k2);
res.clear();
reply::to_array(*reply, std::back_inserter(res));
REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v",
"failed to test generic command");
res = _redis.template command<std::vector<OptionalString>>("mget", k1, k2);
REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v",
"failed to test generic command");
res.clear();
_redis.command("mget", k1, k2, std::back_inserter(res));
REDIS_ASSERT(res.size() == 2 && res[0] && *(res[0]) == "v" && res[1] && *(res[1]) == "v",
"failed to test generic command");
auto set_cmd_str = {"set", key.c_str(), "new_value"};
_redis.command(set_cmd_str.begin(), set_cmd_str.end());
auto get_cmd_str = {"get", key.c_str()};
reply = _redis.command(get_cmd_str.begin(), get_cmd_str.end());
val = reply::parse<OptionalString>(*reply);
REDIS_ASSERT(val && *val == "new_value", "failed to test generic command");
val = _redis.template command<OptionalString>(get_cmd_str.begin(), get_cmd_str.end());
REDIS_ASSERT(val && *val == "new_value", "failed to test generic command");
auto mget_cmd_str = {"mget", key.c_str(), not_exist_key.c_str()};
res.clear();
_redis.command(mget_cmd_str.begin(), mget_cmd_str.end(), std::back_inserter(res));
REDIS_ASSERT(res.size() == 2 && res[0] && *res[0] == "new_value" && !res[1],
"failed to test generic command");
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_hash_tag() {
_test_hash_tag({_test_key("{tag}postfix1"),
_test_key("{tag}postfix2"),
_test_key("{tag}postfix3")});
_test_hash_tag({_test_key("prefix1{tag}postfix1"),
_test_key("prefix2{tag}postfix2"),
_test_key("prefix3{tag}postfix3")});
_test_hash_tag({_test_key("prefix1{tag}"),
_test_key("prefix2{tag}"),
_test_key("prefix3{tag}")});
_test_hash_tag({_test_key("prefix{}postfix"),
_test_key("prefix{}postfix"),
_test_key("prefix{}postfix")});
_test_hash_tag({_test_key("prefix1{tag}post}fix1"),
_test_key("prefix2{tag}pos}tfix2"),
_test_key("prefix3{tag}postfi}x3")});
_test_hash_tag({_test_key("prefix1{t{ag}postfix1"),
_test_key("prefix2{t{ag}postfix2"),
_test_key("prefix3{t{ag}postfix3")});
_test_hash_tag({_test_key("prefix1{t{ag}postfi}x1"),
_test_key("prefix2{t{ag}post}fix2"),
_test_key("prefix3{t{ag}po}stfix3")});
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_hash_tag(std::initializer_list<std::string> keys) {
KeyDeleter<RedisInstance> deleter(_redis, keys.begin(), keys.end());
std::string value = "value";
std::vector<std::pair<std::string, std::string>> kvs;
for (const auto &key : keys) {
kvs.emplace_back(key, value);
}
_redis.mset(kvs.begin(), kvs.end());
std::vector<OptionalString> res;
res.reserve(keys.size());
_redis.mget(keys.begin(), keys.end(), std::back_inserter(res));
REDIS_ASSERT(res.size() == keys.size(), "failed to test hash tag");
for (const auto &ele : res) {
REDIS_ASSERT(ele && *ele == value, "failed to test hash tag");
}
}
template <typename RedisInstance>
std::string SanityTest<RedisInstance>::_test_key(const std::string &key) {
REDIS_ASSERT(key.size() > 1, "failed to generate key");
// Ensure that key prefix has NO hash tag. Also see the implementation of test_key.
return key.substr(1);
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_ping(Redis &instance) {
auto reply = instance.command("ping");
REDIS_ASSERT(reply && reply::parse<std::string>(*reply) == "PONG",
"failed to test generic command");
auto pong = instance.command<std::string>("ping");
REDIS_ASSERT(pong == "PONG", "failed to test generic command");
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_pipeline(const StringView &key, Pipeline &pipeline) {
auto pipe_replies = pipeline.command("set", key, "value").command("get", key).exec();
auto val = pipe_replies.get<OptionalString>(1);
REDIS_ASSERT(val && *val == "value", "failed to test generic command");
}
template <typename RedisInstance>
void SanityTest<RedisInstance>::_test_transaction(const StringView &key, Transaction &transaction) {
auto tx_replies = transaction.command("set", key, 456).command("incr", key).exec();
REDIS_ASSERT(tx_replies.get<long long>(1) == 457, "failed to test generic command");
}
template <typename RedisInstance>
Pipeline SanityTest<RedisInstance>::_pipeline(const StringView &) {
return _redis.pipeline();
}
template <>
inline Pipeline SanityTest<RedisCluster>::_pipeline(const StringView &key) {
return _redis.pipeline(key);
}
template <typename RedisInstance>
Transaction SanityTest<RedisInstance>::_transaction(const StringView &) {
return _redis.transaction();
}
template <>
inline Transaction SanityTest<RedisCluster>::_transaction(const StringView &key) {
return _redis.transaction(key);
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SANITY_TEST_HPP

View file

@ -0,0 +1,49 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class ScriptCmdTest {
public:
explicit ScriptCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _run(Redis &instance);
RedisInstance &_redis;
};
}
}
}
#include "script_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_H

View file

@ -0,0 +1,97 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP
#include <list>
#include <vector>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void ScriptCmdTest<RedisInstance>::run() {
cluster_specializing_test(*this,
&ScriptCmdTest<RedisInstance>::_run,
_redis);
}
template <typename RedisInstance>
void ScriptCmdTest<RedisInstance>::_run(Redis &instance) {
auto key1 = test_key("k1");
auto key2 = test_key("k2");
KeyDeleter<Redis> deleter(instance, {key1, key2});
std::string script = "redis.call('set', KEYS[1], 1);"
"redis.call('set', KEYS[2], 2);"
"local first = redis.call('get', KEYS[1]);"
"local second = redis.call('get', KEYS[2]);"
"return first + second";
auto num = instance.eval<long long>(script, {key1, key2}, {});
REDIS_ASSERT(num == 3, "failed to test scripting for cluster");
script = "return 1";
num = instance.eval<long long>(script, {}, {});
REDIS_ASSERT(num == 1, "failed to test eval");
auto script_with_args = "return {ARGV[1] + 1, ARGV[2] + 2, ARGV[3] + 3}";
std::vector<long long> res;
instance.eval(script_with_args,
{"k"},
{"1", "2", "3"},
std::back_inserter(res));
REDIS_ASSERT(res == std::vector<long long>({2, 4, 6}),
"failed to test eval with array reply");
auto sha1 = instance.script_load(script);
num = instance.evalsha<long long>(sha1, {}, {});
REDIS_ASSERT(num == 1, "failed to test evalsha");
auto sha2 = instance.script_load(script_with_args);
res.clear();
instance.evalsha(sha2,
{"k"},
{"1", "2", "3"},
std::back_inserter(res));
REDIS_ASSERT(res == std::vector<long long>({2, 4, 6}),
"failed to test evalsha with array reply");
std::list<bool> exist_res;
instance.script_exists({sha1, sha2, std::string("not exist")}, std::back_inserter(exist_res));
REDIS_ASSERT(exist_res == std::list<bool>({true, true, false}),
"failed to test script exists");
instance.script_flush();
exist_res.clear();
instance.script_exists({sha1, sha2, std::string("not exist")}, std::back_inserter(exist_res));
REDIS_ASSERT(exist_res == std::list<bool>({false, false, false}),
"failed to test script flush");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SCRIPT_CMDS_TEST_HPP

View file

@ -0,0 +1,53 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class SetCmdTest {
public:
explicit SetCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_set();
void _test_multi_set();
void _test_sscan();
RedisInstance &_redis;
};
}
}
}
#include "set_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_H

View file

@ -0,0 +1,184 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP
#include <unordered_set>
#include <vector>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void SetCmdTest<RedisInstance>::run() {
_test_set();
_test_multi_set();
_test_sscan();
}
template <typename RedisInstance>
void SetCmdTest<RedisInstance>::_test_set() {
auto key = test_key("set");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::string m1("m1");
std::string m2("m2");
std::string m3("m3");
REDIS_ASSERT(_redis.sadd(key, m1) == 1, "failed to test sadd");
auto members = {m1, m2, m3};
REDIS_ASSERT(_redis.sadd(key, members) == 2, "failed to test sadd with multiple members");
REDIS_ASSERT(_redis.scard(key) == 3, "failed to test scard");
REDIS_ASSERT(_redis.sismember(key, m1), "failed to test sismember");
std::unordered_set<std::string> res;
_redis.smembers(key, std::inserter(res, res.end()));
REDIS_ASSERT(res.find(m1) != res.end()
&& res.find(m2) != res.end()
&& res.find(m3) != res.end(),
"failed to test smembers");
auto ele = _redis.srandmember(key);
REDIS_ASSERT(bool(ele) && res.find(*ele) != res.end(), "failed to test srandmember");
std::vector<std::string> rand_members;
_redis.srandmember(key, 2, std::back_inserter(rand_members));
REDIS_ASSERT(rand_members.size() == 2, "failed to test srandmember");
ele = _redis.spop(key);
REDIS_ASSERT(bool(ele) && res.find(*ele) != res.end(), "failed to test spop");
rand_members.clear();
_redis.spop(key, 3, std::back_inserter(rand_members));
REDIS_ASSERT(rand_members.size() == 2, "failed to test srandmember");
rand_members.clear();
_redis.srandmember(key, 2, std::back_inserter(rand_members));
REDIS_ASSERT(rand_members.empty(), "failed to test srandmember with empty set");
_redis.spop(key, 2, std::back_inserter(rand_members));
REDIS_ASSERT(rand_members.empty(), "failed to test spop with empty set");
_redis.sadd(key, members);
REDIS_ASSERT(_redis.srem(key, m1) == 1, "failed to test srem");
REDIS_ASSERT(_redis.srem(key, members) == 2, "failed to test srem with mulitple members");
REDIS_ASSERT(_redis.srem(key, members) == 0, "failed to test srem with mulitple members");
}
template <typename RedisInstance>
void SetCmdTest<RedisInstance>::_test_multi_set() {
auto k1 = test_key("s1");
auto k2 = test_key("s2");
auto k3 = test_key("s3");
auto k4 = test_key("s4");
auto k5 = test_key("s5");
auto k6 = test_key("s6");
KeyDeleter<RedisInstance> keys(_redis, {k1, k2, k3, k4, k5, k6});
_redis.sadd(k1, {"a", "c"});
_redis.sadd(k2, {"a", "b"});
std::vector<std::string> sdiff;
_redis.sdiff({k1, k1}, std::back_inserter(sdiff));
REDIS_ASSERT(sdiff.empty(), "failed to test sdiff");
_redis.sdiff({k1, k2}, std::back_inserter(sdiff));
REDIS_ASSERT(sdiff == std::vector<std::string>({"c"}), "failed to test sdiff");
_redis.sdiffstore(k3, {k1, k2});
sdiff.clear();
_redis.smembers(k3, std::back_inserter(sdiff));
REDIS_ASSERT(sdiff == std::vector<std::string>({"c"}), "failed to test sdiffstore");
REDIS_ASSERT(_redis.sdiffstore(k3, k1) == 2, "failed to test sdiffstore");
REDIS_ASSERT(_redis.sinterstore(k3, k1) == 2, "failed to test sinterstore");
REDIS_ASSERT(_redis.sunionstore(k3, k1) == 2, "failed to test sunionstore");
std::vector<std::string> sinter;
_redis.sinter({k1, k2}, std::back_inserter(sinter));
REDIS_ASSERT(sinter == std::vector<std::string>({"a"}), "failed to test sinter");
_redis.sinterstore(k4, {k1, k2});
sinter.clear();
_redis.smembers(k4, std::back_inserter(sinter));
REDIS_ASSERT(sinter == std::vector<std::string>({"a"}), "failed to test sinterstore");
std::unordered_set<std::string> sunion;
_redis.sunion({k1, k2}, std::inserter(sunion, sunion.end()));
REDIS_ASSERT(sunion == std::unordered_set<std::string>({"a", "b", "c"}),
"failed to test sunion");
_redis.sunionstore(k5, {k1, k2});
sunion.clear();
_redis.smembers(k5, std::inserter(sunion, sunion.end()));
REDIS_ASSERT(sunion == std::unordered_set<std::string>({"a", "b", "c"}),
"failed to test sunionstore");
REDIS_ASSERT(_redis.smove(k5, k6, "a"), "failed to test smove");
}
template <typename RedisInstance>
void SetCmdTest<RedisInstance>::_test_sscan() {
auto key = test_key("sscan");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::unordered_set<std::string> members = {"m1", "m2", "m3"};
_redis.sadd(key, members.begin(), members.end());
std::unordered_set<std::string> res;
long long cursor = 0;
while (true) {
cursor = _redis.sscan(key, cursor, "m*", 1, std::inserter(res, res.end()));
if (cursor == 0) {
break;
}
}
REDIS_ASSERT(res == members, "failed to test sscan");
res.clear();
cursor = 0;
while (true) {
cursor = _redis.sscan(key, cursor, std::inserter(res, res.end()));
if (cursor == 0) {
break;
}
}
REDIS_ASSERT(res == members, "failed to test sscan");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_SET_CMDS_TEST_HPP

View file

@ -0,0 +1,54 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class StreamCmdsTest {
public:
explicit StreamCmdsTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
using Item = std::pair<std::string, std::unordered_map<std::string, std::string>>;
using Result = std::unordered_map<std::string, std::vector<Item>>;
void _test_stream_cmds();
void _test_group_cmds();
RedisInstance &_redis;
};
}
}
}
#include "stream_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_H

View file

@ -0,0 +1,225 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP
#include <vector>
#include <string>
#include <thread>
#include <chrono>
#include <unordered_map>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void StreamCmdsTest<RedisInstance>::run() {
_test_stream_cmds();
_test_group_cmds();
}
template <typename RedisInstance>
void StreamCmdsTest<RedisInstance>::_test_stream_cmds() {
auto key = test_key("stream");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::vector<std::pair<std::string, std::string>> attrs = {
{"f1", "v1"},
{"f2", "v2"}
};
auto id = "1565427842-0";
REDIS_ASSERT(_redis.xadd(key, id, attrs.begin(), attrs.end()) == id,
"failed to test xadd");
std::vector<std::pair<std::string, std::string>> keys = {std::make_pair(key, "0-0")};
Result result;
_redis.xread(keys.begin(), keys.end(), 1, std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 1
&& result.find(key) != result.end()
&& result[key].size() == 1
&& result[key][0].first == id
&& result[key][0].second.size() == 2,
"failed to test xread");
result.clear();
_redis.xread(key, std::string("0-0"), 1, std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 1
&& result.find(key) != result.end()
&& result[key].size() == 1
&& result[key][0].first == id
&& result[key][0].second.size() == 2,
"failed to test xread");
result.clear();
keys = {std::make_pair(key, id)};
_redis.xread(keys.begin(),
keys.end(),
std::chrono::seconds(1),
2,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 0, "failed to test xread");
_redis.xread(key,
id,
std::chrono::seconds(1),
2,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 0, "failed to test xread");
id = "1565427842-1";
REDIS_ASSERT(_redis.xadd(key, id, attrs.begin(), attrs.end()) == id,
"failed to test xadd");
REDIS_ASSERT(_redis.xlen(key) == 2, "failed to test xlen");
REDIS_ASSERT(_redis.xtrim(key, 1, false) == 1, "failed to test xtrim");
std::vector<Item> items;
_redis.xrange(key, "-", "+", std::back_inserter(items));
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xrange");
items.clear();
_redis.xrevrange(key, "+", "-", std::back_inserter(items));
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xrevrange");
REDIS_ASSERT(_redis.xdel(key, {id, "111-111"}) == 1, "failed to test xdel");
}
template <typename RedisInstance>
void StreamCmdsTest<RedisInstance>::_test_group_cmds() {
auto key = test_key("stream");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto group = "group";
auto consumer1 = "consumer1";
_redis.xgroup_create(key, group, "$", true);
std::vector<std::pair<std::string, std::string>> attrs = {
{"f1", "v1"},
{"f2", "v2"}
};
auto id = _redis.xadd(key, "*", attrs.begin(), attrs.end());
auto keys = {std::make_pair(key, ">")};
Result result;
_redis.xreadgroup(group,
consumer1,
keys.begin(),
keys.end(),
1,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 1
&& result.find(key) != result.end()
&& result[key].size() == 1
&& result[key][0].first == id,
"failed to test xreadgroup");
result.clear();
_redis.xreadgroup(group,
consumer1,
key,
std::string(">"),
1,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
result.clear();
_redis.xreadgroup(group,
"not-exist-consumer",
keys.begin(),
keys.end(),
1,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
result.clear();
_redis.xreadgroup(group,
consumer1,
keys.begin(),
keys.end(),
std::chrono::seconds(1),
1,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
result.clear();
_redis.xreadgroup(group,
consumer1,
key,
">",
std::chrono::seconds(1),
1,
std::inserter(result, result.end()));
REDIS_ASSERT(result.size() == 0, "failed to test xreadgroup");
using PendingResult = std::vector<std::tuple<std::string, std::string, long long, long long>>;
PendingResult pending_result;
_redis.xpending(key, group, "-", "+", 1, consumer1, std::back_inserter(pending_result));
REDIS_ASSERT(pending_result.size() == 1
&& std::get<0>(pending_result[0]) == id
&& std::get<1>(pending_result[0]) == consumer1,
"failed to test xpending");
std::this_thread::sleep_for(std::chrono::seconds(1));
auto consumer2 = "consumer2";
std::vector<Item> items;
auto ids = {id};
_redis.xclaim(key,
group,
consumer2,
std::chrono::milliseconds(10),
ids,
std::back_inserter(items));
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xclaim");
std::this_thread::sleep_for(std::chrono::seconds(1));
items.clear();
_redis.xclaim(key, group, consumer1, std::chrono::milliseconds(10), id, std::back_inserter(items));
REDIS_ASSERT(items.size() == 1 && items[0].first == id, "failed to test xclaim: " + std::to_string(items.size()));
_redis.xack(key, group, id);
REDIS_ASSERT(_redis.xgroup_delconsumer(key, group, consumer1) == 0,
"failed to test xgroup_delconsumer");
REDIS_ASSERT(_redis.xgroup_delconsumer(key, group, consumer2) == 0,
"failed to test xgroup_delconsumer");
REDIS_ASSERT(_redis.xgroup_destroy(key, group) == 1,
"failed to test xgroup_destroy");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STREAM_CMDS_TEST_HPP

View file

@ -0,0 +1,57 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class StringCmdTest {
public:
explicit StringCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_str();
void _test_bit();
void _test_numeric();
void _test_getset();
void _test_mgetset();
RedisInstance &_redis;
};
}
}
}
#include "string_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_H

View file

@ -0,0 +1,247 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP
#include <vector>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void StringCmdTest<RedisInstance>::run() {
_test_str();
_test_bit();
_test_numeric();
_test_getset();
_test_mgetset();
}
template <typename RedisInstance>
void StringCmdTest<RedisInstance>::_test_str() {
auto key = test_key("str");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::string val("value");
long long val_size = val.size();
auto len1 = _redis.append(key, val);
REDIS_ASSERT(len1 == val_size, "failed to append to non-existent key");
auto len2 = _redis.append(key, val);
REDIS_ASSERT(len2 == len1 + val_size, "failed to append to non-empty string");
auto len3 = _redis.append(key, {});
REDIS_ASSERT(len3 == len2, "failed to append empty string");
auto len4 = _redis.strlen(key);
REDIS_ASSERT(len4 == len3, "failed to test strlen");
REDIS_ASSERT(_redis.del(key) == 1, "failed to remove key");
auto len5 = _redis.append(key, {});
REDIS_ASSERT(len5 == 0, "failed to append empty string to non-existent key");
_redis.del(key);
REDIS_ASSERT(_redis.getrange(key, 0, 2) == "", "failed to test getrange on non-existent key");
_redis.set(key, val);
REDIS_ASSERT(_redis.getrange(key, 1, 2) == val.substr(1, 2), "failed to test getrange");
long long new_size = val.size() * 2;
REDIS_ASSERT(_redis.setrange(key, val.size(), val) == new_size, "failed to test setrange");
REDIS_ASSERT(_redis.getrange(key, 0, -1) == val + val, "failed to test setrange");
}
template <typename RedisInstance>
void StringCmdTest<RedisInstance>::_test_bit() {
auto key = test_key("bit");
KeyDeleter<RedisInstance> deleter(_redis, key);
REDIS_ASSERT(_redis.bitcount(key) == 0, "failed to test bitcount on non-existent key");
REDIS_ASSERT(_redis.getbit(key, 5) == 0, "failed to test getbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 1, 1) == 0,
"failed to test setbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 3, 1) == 0,
"failed to test setbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 7, 1) == 0,
"failed to test setbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 10, 1) == 0,
"failed to test setbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 10, 0) == 1,
"failed to test setbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 11, 1) == 0,
"failed to test setbit");
REDIS_ASSERT(_redis.template command<long long>("SETBIT", key, 21, 1) == 0,
"failed to test setbit");
// key -> 01010001, 00010000, 00000100
REDIS_ASSERT(_redis.getbit(key, 1) == 1, "failed to test getbit");
REDIS_ASSERT(_redis.getbit(key, 2) == 0, "failed to test getbit");
REDIS_ASSERT(_redis.getbit(key, 7) == 1, "failed to test getbit");
REDIS_ASSERT(_redis.getbit(key, 10) == 0, "failed to test getbit");
REDIS_ASSERT(_redis.getbit(key, 100) == 0, "failed to test getbit");
REDIS_ASSERT(_redis.bitcount(key) == 5, "failed to test bitcount");
REDIS_ASSERT(_redis.bitcount(key, 0, 0) == 3, "failed to test bitcount");
REDIS_ASSERT(_redis.bitcount(key, 0, 1) == 4, "failed to test bitcount");
REDIS_ASSERT(_redis.bitcount(key, -2, -1) == 2, "failed to test bitcount");
REDIS_ASSERT(_redis.bitpos(key, 1) == 1, "failed to test bitpos");
REDIS_ASSERT(_redis.bitpos(key, 0) == 0, "failed to test bitpos");
REDIS_ASSERT(_redis.bitpos(key, 1, 1, 1) == 11, "failed to test bitpos");
REDIS_ASSERT(_redis.bitpos(key, 0, 1, 1) == 8, "failed to test bitpos");
REDIS_ASSERT(_redis.bitpos(key, 1, -1, -1) == 21, "failed to test bitpos");
REDIS_ASSERT(_redis.bitpos(key, 0, -1, -1) == 16, "failed to test bitpos");
auto dest_key = test_key("bitop_dest");
auto src_key1 = test_key("bitop_src1");
auto src_key2 = test_key("bitop_src2");
KeyDeleter<RedisInstance> deleters(_redis, {dest_key, src_key1, src_key2});
// src_key1 -> 00010000
_redis.template command<long long>("SETBIT", src_key1, 3, 1);
// src_key2 -> 00000000, 00001000
_redis.template command<long long>("SETBIT", src_key2, 12, 1);
REDIS_ASSERT(_redis.bitop(BitOp::AND, dest_key, {src_key1, src_key2}) == 2,
"failed to test bitop");
// dest_key -> 00000000, 00000000
auto v = _redis.get(dest_key);
REDIS_ASSERT(v && *v == std::string(2, 0), "failed to test bitop");
REDIS_ASSERT(_redis.bitop(BitOp::NOT, dest_key, src_key1) == 1,
"failed to test bitop");
// dest_key -> 11101111
v = _redis.get(dest_key);
REDIS_ASSERT(v && *v == std::string(1, 0xEF), "failed to test bitop");
}
template <typename RedisInstance>
void StringCmdTest<RedisInstance>::_test_numeric() {
auto key = test_key("numeric");
KeyDeleter<RedisInstance> deleter(_redis, key);
REDIS_ASSERT(_redis.incr(key) == 1, "failed to test incr");
REDIS_ASSERT(_redis.decr(key) == 0, "failed to test decr");
REDIS_ASSERT(_redis.incrby(key, 3) == 3, "failed to test incrby");
REDIS_ASSERT(_redis.decrby(key, 3) == 0, "failed to test decrby");
REDIS_ASSERT(_redis.incrby(key, -3) == -3, "failed to test incrby");
REDIS_ASSERT(_redis.decrby(key, -3) == 0, "failed to test incrby");
REDIS_ASSERT(_redis.incrbyfloat(key, 1.5) == 1.5, "failed to test incrbyfloat");
}
template <typename RedisInstance>
void StringCmdTest<RedisInstance>::_test_getset() {
auto key = test_key("getset");
auto non_exist_key = test_key("non-existent");
KeyDeleter<RedisInstance> deleter(_redis, {key, non_exist_key});
std::string val("value");
REDIS_ASSERT(_redis.set(key, val), "failed to test set");
auto v = _redis.get(key);
REDIS_ASSERT(v && *v == val, "failed to test get");
v = _redis.getset(key, val + val);
REDIS_ASSERT(v && *v == val, "failed to test get");
REDIS_ASSERT(!_redis.set(key, val, std::chrono::milliseconds(0), UpdateType::NOT_EXIST),
"failed to test set with NOT_EXIST type");
REDIS_ASSERT(!_redis.set(non_exist_key, val, std::chrono::milliseconds(0), UpdateType::EXIST),
"failed to test set with EXIST type");
REDIS_ASSERT(!_redis.setnx(key, val), "failed to test setnx");
REDIS_ASSERT(_redis.setnx(non_exist_key, val), "failed to test setnx");
auto ttl = std::chrono::seconds(10);
_redis.set(key, val, ttl);
REDIS_ASSERT(_redis.ttl(key) <= ttl.count(), "failed to test set key with ttl");
_redis.setex(key, ttl, val);
REDIS_ASSERT(_redis.ttl(key) <= ttl.count(), "failed to test setex");
auto pttl = std::chrono::milliseconds(10000);
_redis.psetex(key, ttl, val);
REDIS_ASSERT(_redis.pttl(key) <= pttl.count(), "failed to test psetex");
}
template <typename RedisInstance>
void StringCmdTest<RedisInstance>::_test_mgetset() {
auto kvs = {std::make_pair(test_key("k1"), "v1"),
std::make_pair(test_key("k2"), "v2"),
std::make_pair(test_key("k3"), "v3")};
std::vector<std::string> keys;
std::vector<std::string> vals;
for (const auto &kv : kvs) {
keys.push_back(kv.first);
vals.push_back(kv.second);
}
KeyDeleter<RedisInstance> deleter(_redis, keys.begin(), keys.end());
_redis.mset(kvs);
std::vector<OptionalString> res;
_redis.mget(keys.begin(), keys.end(), std::back_inserter(res));
REDIS_ASSERT(res.size() == kvs.size(), "failed to test mget");
std::vector<std::string> res_vals;
for (const auto &ele : res) {
REDIS_ASSERT(bool(ele), "failed to test mget");
res_vals.push_back(*ele);
}
REDIS_ASSERT(vals == res_vals, "failed to test mget");
REDIS_ASSERT(!_redis.msetnx(kvs), "failed to test msetnx");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_STRING_CMDS_TEST_HPP

View file

@ -0,0 +1,303 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#include <unistd.h>
#include <chrono>
#include <tuple>
#include <iostream>
#include <sw/redis++/redis++.h>
#include "sanity_test.h"
#include "connection_cmds_test.h"
#include "keys_cmds_test.h"
#include "string_cmds_test.h"
#include "list_cmds_test.h"
#include "hash_cmds_test.h"
#include "set_cmds_test.h"
#include "zset_cmds_test.h"
#include "hyperloglog_cmds_test.h"
#include "geo_cmds_test.h"
#include "script_cmds_test.h"
#include "pubsub_test.h"
#include "pipeline_transaction_test.h"
#include "threads_test.h"
#include "stream_cmds_test.h"
#include "benchmark_test.h"
namespace {
void print_help();
auto parse_options(int argc, char **argv)
-> std::tuple<sw::redis::Optional<sw::redis::ConnectionOptions>,
sw::redis::Optional<sw::redis::ConnectionOptions>,
sw::redis::Optional<sw::redis::test::BenchmarkOptions>>;
template <typename RedisInstance>
void run_test(const sw::redis::ConnectionOptions &opts);
template <typename RedisInstance>
void run_benchmark(const sw::redis::ConnectionOptions &opts,
const sw::redis::test::BenchmarkOptions &benchmark_opts);
}
int main(int argc, char **argv) {
try {
sw::redis::Optional<sw::redis::ConnectionOptions> opts;
sw::redis::Optional<sw::redis::ConnectionOptions> cluster_node_opts;
sw::redis::Optional<sw::redis::test::BenchmarkOptions> benchmark_opts;
std::tie(opts, cluster_node_opts, benchmark_opts) = parse_options(argc, argv);
if (opts) {
std::cout << "Testing Redis..." << std::endl;
if (benchmark_opts) {
run_benchmark<sw::redis::Redis>(*opts, *benchmark_opts);
} else {
run_test<sw::redis::Redis>(*opts);
}
}
if (cluster_node_opts) {
std::cout << "Testing RedisCluster..." << std::endl;
if (benchmark_opts) {
run_benchmark<sw::redis::RedisCluster>(*cluster_node_opts, *benchmark_opts);
} else {
run_test<sw::redis::RedisCluster>(*cluster_node_opts);
}
}
std::cout << "Pass all tests" << std::endl;
} catch (const sw::redis::Error &e) {
std::cerr << "Test failed: " << e.what() << std::endl;
return -1;
}
return 0;
}
namespace {
void print_help() {
std::cerr << "Usage: test_redis++ -h host -p port"
<< " -n cluster_node -c cluster_port [-a auth] [-b]\n\n";
std::cerr << "See https://github.com/sewenew/redis-plus-plus#run-tests-optional"
<< " for details on how to run test" << std::endl;
}
auto parse_options(int argc, char **argv)
-> std::tuple<sw::redis::Optional<sw::redis::ConnectionOptions>,
sw::redis::Optional<sw::redis::ConnectionOptions>,
sw::redis::Optional<sw::redis::test::BenchmarkOptions>> {
std::string host;
int port = 0;
std::string auth;
std::string cluster_node;
int cluster_port = 0;
bool benchmark = false;
sw::redis::test::BenchmarkOptions tmp_benchmark_opts;
int opt = 0;
while ((opt = getopt(argc, argv, "h:p:a:n:c:k:v:r:t:bs:")) != -1) {
try {
switch (opt) {
case 'h':
host = optarg;
break;
case 'p':
port = std::stoi(optarg);
break;
case 'a':
auth = optarg;
break;
case 'n':
cluster_node = optarg;
break;
case 'c':
cluster_port = std::stoi(optarg);
break;
case 'b':
benchmark = true;
break;
case 'k':
tmp_benchmark_opts.key_len = std::stoi(optarg);
break;
case 'v':
tmp_benchmark_opts.val_len = std::stoi(optarg);
break;
case 'r':
tmp_benchmark_opts.total_request_num = std::stoi(optarg);
break;
case 't':
tmp_benchmark_opts.thread_num = std::stoi(optarg);
break;
case 's':
tmp_benchmark_opts.pool_size = std::stoi(optarg);
break;
default:
throw sw::redis::Error("Unknow command line option");
break;
}
} catch (const sw::redis::Error &e) {
print_help();
throw;
} catch (const std::exception &e) {
print_help();
throw sw::redis::Error("Invalid command line option");
}
}
sw::redis::Optional<sw::redis::ConnectionOptions> opts;
if (!host.empty() && port > 0) {
sw::redis::ConnectionOptions tmp;
tmp.host = host;
tmp.port = port;
tmp.password = auth;
opts = sw::redis::Optional<sw::redis::ConnectionOptions>(tmp);
}
sw::redis::Optional<sw::redis::ConnectionOptions> cluster_opts;
if (!cluster_node.empty() && cluster_port > 0) {
sw::redis::ConnectionOptions tmp;
tmp.host = cluster_node;
tmp.port = cluster_port;
tmp.password = auth;
cluster_opts = sw::redis::Optional<sw::redis::ConnectionOptions>(tmp);
}
if (!opts && !cluster_opts) {
print_help();
throw sw::redis::Error("Invalid connection options");
}
sw::redis::Optional<sw::redis::test::BenchmarkOptions> benchmark_opts;
if (benchmark) {
benchmark_opts = sw::redis::Optional<sw::redis::test::BenchmarkOptions>(tmp_benchmark_opts);
}
return std::make_tuple(std::move(opts), std::move(cluster_opts), std::move(benchmark_opts));
}
template <typename RedisInstance>
void run_test(const sw::redis::ConnectionOptions &opts) {
auto instance = RedisInstance(opts);
sw::redis::test::SanityTest<RedisInstance> sanity_test(opts, instance);
sanity_test.run();
std::cout << "Pass sanity tests" << std::endl;
sw::redis::test::ConnectionCmdTest<RedisInstance> connection_test(instance);
connection_test.run();
std::cout << "Pass connection commands tests" << std::endl;
sw::redis::test::KeysCmdTest<RedisInstance> keys_test(instance);
keys_test.run();
std::cout << "Pass keys commands tests" << std::endl;
sw::redis::test::StringCmdTest<RedisInstance> string_test(instance);
string_test.run();
std::cout << "Pass string commands tests" << std::endl;
sw::redis::test::ListCmdTest<RedisInstance> list_test(instance);
list_test.run();
std::cout << "Pass list commands tests" << std::endl;
sw::redis::test::HashCmdTest<RedisInstance> hash_test(instance);
hash_test.run();
std::cout << "Pass hash commands tests" << std::endl;
sw::redis::test::SetCmdTest<RedisInstance> set_test(instance);
set_test.run();
std::cout << "Pass set commands tests" << std::endl;
sw::redis::test::ZSetCmdTest<RedisInstance> zset_test(instance);
zset_test.run();
std::cout << "Pass zset commands tests" << std::endl;
sw::redis::test::HyperloglogCmdTest<RedisInstance> hll_test(instance);
hll_test.run();
std::cout << "Pass hyperloglog commands tests" << std::endl;
sw::redis::test::GeoCmdTest<RedisInstance> geo_test(instance);
geo_test.run();
std::cout << "Pass geo commands tests" << std::endl;
sw::redis::test::ScriptCmdTest<RedisInstance> script_test(instance);
script_test.run();
std::cout << "Pass script commands tests" << std::endl;
sw::redis::test::PubSubTest<RedisInstance> pubsub_test(instance);
pubsub_test.run();
std::cout << "Pass pubsub tests" << std::endl;
sw::redis::test::PipelineTransactionTest<RedisInstance> pipe_tx_test(instance);
pipe_tx_test.run();
std::cout << "Pass pipeline and transaction tests" << std::endl;
sw::redis::test::ThreadsTest<RedisInstance> threads_test(opts);
threads_test.run();
std::cout << "Pass threads tests" << std::endl;
sw::redis::test::StreamCmdsTest<RedisInstance> stream_test(instance);
stream_test.run();
std::cout << "Pass stream commands tests" << std::endl;
}
template <typename RedisInstance>
void run_benchmark(const sw::redis::ConnectionOptions &opts,
const sw::redis::test::BenchmarkOptions &benchmark_opts) {
std::cout << "Benchmark test options:" << std::endl;
std::cout << " Thread number: " << benchmark_opts.thread_num << std::endl;
std::cout << " Connection pool size: " << benchmark_opts.pool_size << std::endl;
std::cout << " Length of key: " << benchmark_opts.key_len << std::endl;
std::cout << " Length of value: " << benchmark_opts.val_len << std::endl;
auto instance = RedisInstance(opts);
sw::redis::test::BenchmarkTest<RedisInstance> benchmark_test(benchmark_opts, instance);
benchmark_test.run();
}
}

View file

@ -0,0 +1,51 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class ThreadsTest {
public:
explicit ThreadsTest(const ConnectionOptions &opts) : _opts(opts) {}
void run();
private:
void _test_multithreads(RedisInstance redis, int threads_num, int times);
void _test_timeout();
ConnectionOptions _opts;
};
}
}
}
#include "threads_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_H

View file

@ -0,0 +1,147 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP
#include <thread>
#include <chrono>
#include <atomic>
#include <memory>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void ThreadsTest<RedisInstance>::run() {
// 100 * 10000 = 1 million writes
auto thread_num = 100;
auto times = 10000;
// Default pool options: single connection and wait forever.
_test_multithreads(RedisInstance(_opts), thread_num, times);
// Pool with 10 connections.
ConnectionPoolOptions pool_opts;
pool_opts.size = 10;
_test_multithreads(RedisInstance(_opts, pool_opts), thread_num, times);
_test_timeout();
}
template <typename RedisInstance>
void ThreadsTest<RedisInstance>::_test_multithreads(RedisInstance redis,
int thread_num,
int times) {
std::vector<std::string> keys;
keys.reserve(thread_num);
for (auto idx = 0; idx != thread_num; ++idx) {
auto key = test_key("multi-threads::" + std::to_string(idx));
keys.push_back(key);
}
using DeleterUPtr = std::unique_ptr<KeyDeleter<RedisInstance>>;
std::vector<DeleterUPtr> deleters;
for (const auto &key : keys) {
deleters.emplace_back(new KeyDeleter<RedisInstance>(redis, key));
}
std::vector<std::thread> workers;
workers.reserve(thread_num);
for (const auto &key : keys) {
workers.emplace_back([&redis, key, times]() {
try {
for (auto i = 0; i != times; ++i) {
redis.incr(key);
}
} catch (...) {
// Something bad happens.
return;
}
});
}
for (auto &worker : workers) {
worker.join();
}
for (const auto &key : keys) {
auto val = redis.get(key);
REDIS_ASSERT(bool(val), "failed to test multithreads, cannot get value of " + key);
auto num = std::stoi(*val);
REDIS_ASSERT(num == times, "failed to test multithreads, num: "
+ *val + ", times: " + std::to_string(times));
}
}
template <typename RedisInstance>
void ThreadsTest<RedisInstance>::_test_timeout() {
using namespace std::chrono;
ConnectionPoolOptions pool_opts;
pool_opts.size = 1;
pool_opts.wait_timeout = milliseconds(100);
auto redis = RedisInstance(_opts, pool_opts);
auto key = test_key("key");
std::atomic<bool> slow_get_is_running{false};
auto slow_get = [&slow_get_is_running](Connection &connection, const StringView &key) {
slow_get_is_running = true;
// Sleep a while to simulate a slow get.
std::this_thread::sleep_for(seconds(5));
connection.send("GET %b", key.data(), key.size());
};
auto slow_get_thread = std::thread([&redis, slow_get, &key]() {
redis.command(slow_get, key);
});
auto get_thread = std::thread([&redis, &slow_get_is_running, &key]() {
try {
while (!slow_get_is_running) {
std::this_thread::sleep_for(milliseconds(10));
}
redis.get(key);
// Slow get is running, this thread should
// timeout before obtaining the connection.
// So it never reaches here.
REDIS_ASSERT(false, "failed to test pool timeout");
} catch (const Error &err) {
// This thread timeout.
}
});
slow_get_thread.join();
get_thread.join();
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_THREADS_TEST_HPP

View file

@ -0,0 +1,96 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_UTILS_H
#define SEWENEW_REDISPLUSPLUS_TEST_UTILS_H
#include <string>
#include <vector>
#include <sw/redis++/redis++.h>
#define REDIS_ASSERT(condition, msg) \
sw::redis::test::redis_assert((condition), (msg), __FILE__, __LINE__)
namespace sw {
namespace redis {
namespace test {
inline void redis_assert(bool condition,
const std::string &msg,
const std::string &file,
int line) {
if (!condition) {
auto err_msg = "ASSERT: " + msg + ". " + file + ":" + std::to_string(line);
throw Error(err_msg);
}
}
inline std::string test_key(const std::string &k) {
// Key prefix with hash tag,
// so that we can call multiple-key commands on RedisCluster.
return "{sw::redis::test}::" + k;
}
template <typename Test>
void cluster_specializing_test(Test &test, void (Test::*func)(Redis &instance), Redis &instance) {
(test.*func)(instance);
}
template <typename Test>
void cluster_specializing_test(Test &test,
void (Test::*func)(Redis &instance),
RedisCluster &cluster) {
auto instance = cluster.redis("hash-tag");
(test.*func)(instance);
}
template <typename RedisInstance>
class KeyDeleter {
public:
template <typename Input>
KeyDeleter(RedisInstance &redis, Input first, Input last) : _redis(redis), _keys(first, last) {
_delete();
}
KeyDeleter(RedisInstance &redis, std::initializer_list<std::string> il) :
KeyDeleter(redis, il.begin(), il.end()) {}
KeyDeleter(RedisInstance &redis, const std::string &key) : KeyDeleter(redis, {key}) {}
~KeyDeleter() {
_delete();
}
private:
void _delete() {
if (!_keys.empty()) {
_redis.del(_keys.begin(), _keys.end());
}
}
RedisInstance &_redis;
std::vector<std::string> _keys;
};
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_UTILS_H

View file

@ -0,0 +1,61 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H
#define SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H
#include <sw/redis++/redis++.h>
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
class ZSetCmdTest {
public:
explicit ZSetCmdTest(RedisInstance &instance) : _redis(instance) {}
void run();
private:
void _test_zset();
void _test_zscan();
void _test_range();
void _test_lex();
void _test_multi_zset();
void _test_zpop();
void _test_bzpop();
RedisInstance &_redis;
};
}
}
}
#include "zset_cmds_test.hpp"
#endif // end SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_H

View file

@ -0,0 +1,350 @@
/**************************************************************************
Copyright (c) 2017 sewenew
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*************************************************************************/
#ifndef SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP
#define SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP
#include <map>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include "utils.h"
namespace sw {
namespace redis {
namespace test {
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::run() {
_test_zset();
_test_zscan();
_test_range();
_test_lex();
_test_multi_zset();
_test_zpop();
_test_bzpop();
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_zset() {
auto key = test_key("zset");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::map<std::string, double> s = {
std::make_pair("m1", 1.2),
std::make_pair("m2", 2),
std::make_pair("m3", 3),
};
const auto &ele = *(s.begin());
REDIS_ASSERT(_redis.zadd(key, ele.first, ele.second, UpdateType::EXIST) == 0,
"failed to test zadd with noexistent member");
REDIS_ASSERT(_redis.zadd(key, s.begin(), s.end()) == 3, "failed to test zadd");
REDIS_ASSERT(_redis.zadd(key, ele.first, ele.second, UpdateType::NOT_EXIST) == 0,
"failed to test zadd with exist member");
REDIS_ASSERT(_redis.zadd(key, s.begin(), s.end(), UpdateType::ALWAYS, true) == 0,
"failed to test zadd");
REDIS_ASSERT(_redis.zcard(key) == 3, "failed to test zcard");
auto rank = _redis.zrank(key, "m2");
REDIS_ASSERT(bool(rank) && *rank == 1, "failed to test zrank");
rank = _redis.zrevrank(key, "m4");
REDIS_ASSERT(!rank, "failed to test zrevrank with nonexistent member");
auto score = _redis.zscore(key, "m4");
REDIS_ASSERT(!score, "failed to test zscore with nonexistent member");
REDIS_ASSERT(_redis.zincrby(key, 1, "m3") == 4, "failed to test zincrby");
score = _redis.zscore(key, "m3");
REDIS_ASSERT(score && *score == 4, "failed to test zscore");
REDIS_ASSERT(_redis.zrem(key, "m1") == 1, "failed to test zrem");
REDIS_ASSERT(_redis.zrem(key, {"m1", "m2", "m3", "m4"}) == 2, "failed to test zrem");
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_zscan() {
auto key = test_key("zscan");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::map<std::string, double> s = {
std::make_pair("m1", 1.2),
std::make_pair("m2", 2),
std::make_pair("m3", 3),
};
_redis.zadd(key, s.begin(), s.end());
std::map<std::string, double> res;
auto cursor = 0;
while (true) {
cursor = _redis.zscan(key, cursor, "m*", 2, std::inserter(res, res.end()));
if (cursor == 0) {
break;
}
}
REDIS_ASSERT(res == s, "failed to test zscan");
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_range() {
auto key = test_key("range");
KeyDeleter<RedisInstance> deleter(_redis, key);
std::map<std::string, double> s = {
std::make_pair("m1", 1),
std::make_pair("m2", 2),
std::make_pair("m3", 3),
};
_redis.zadd(key, s.begin(), s.end());
REDIS_ASSERT(_redis.zcount(key, UnboundedInterval<double>{}) == 3, "failed to test zcount");
std::vector<std::string> members;
_redis.zrange(key, 0, -1, std::back_inserter(members));
REDIS_ASSERT(members.size() == s.size(), "failed to test zrange");
for (const auto &mem : {"m1", "m2", "m3"}) {
REDIS_ASSERT(std::find(members.begin(), members.end(), mem) != members.end(),
"failed to test zrange");
}
std::map<std::string, double> res;
_redis.zrange(key, 0, -1, std::inserter(res, res.end()));
REDIS_ASSERT(s == res, "failed to test zrange with score");
members.clear();
_redis.zrevrange(key, 0, 0, std::back_inserter(members));
REDIS_ASSERT(members.size() == 1 && members[0] == "m3", "failed to test zrevrange");
res.clear();
_redis.zrevrange(key, 0, 0, std::inserter(res, res.end()));
REDIS_ASSERT(res.size() == 1 && res.find("m3") != res.end() && res["m3"] == 3,
"failed to test zrevrange with score");
members.clear();
_redis.zrangebyscore(key, UnboundedInterval<double>{}, std::back_inserter(members));
REDIS_ASSERT(members.size() == s.size(), "failed to test zrangebyscore");
for (const auto &mem : {"m1", "m2", "m3"}) {
REDIS_ASSERT(std::find(members.begin(), members.end(), mem) != members.end(),
"failed to test zrangebyscore");
}
members.clear();
_redis.zrangebyscore(key,
BoundedInterval<double>(1, 2, BoundType::RIGHT_OPEN),
std::back_inserter(members));
REDIS_ASSERT(members.size() == 1 && members[0] == "m1", "failed to test zrangebyscore");
res.clear();
_redis.zrangebyscore(key,
LeftBoundedInterval<double>(2, BoundType::OPEN),
std::inserter(res, res.end()));
REDIS_ASSERT(res.size() == 1 && res.find("m3") != res.end() && res["m3"] == 3,
"failed to test zrangebyscore");
members.clear();
_redis.zrevrangebyscore(key,
BoundedInterval<double>(1, 3, BoundType::CLOSED),
std::back_inserter(members));
REDIS_ASSERT(members == std::vector<std::string>({"m3", "m2", "m1"}),
"failed to test zrevrangebyscore");
res.clear();
_redis.zrevrangebyscore(key,
RightBoundedInterval<double>(1, BoundType::LEFT_OPEN),
std::inserter(res, res.end()));
REDIS_ASSERT(res.size() == 1 && res.find("m1") != res.end() && res["m1"] == 1,
"failed to test zrevrangebyscore");
REDIS_ASSERT(_redis.zremrangebyrank(key, 0, 0) == 1, "failed to test zremrangebyrank");
REDIS_ASSERT(_redis.zremrangebyscore(key,
BoundedInterval<double>(2, 3, BoundType::LEFT_OPEN)) == 1,
"failed to test zremrangebyscore");
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_lex() {
auto key = test_key("lex");
KeyDeleter<RedisInstance> deleter(_redis, key);
auto s = {
std::make_pair("m1", 0),
std::make_pair("m2", 0),
std::make_pair("m3", 0),
};
_redis.zadd(key, s.begin(), s.end());
REDIS_ASSERT(_redis.zlexcount(key, UnboundedInterval<std::string>{}) == 3,
"failed to test zlexcount");
std::vector<std::string> members;
_redis.zrangebylex(key,
LeftBoundedInterval<std::string>("m2", BoundType::OPEN),
std::back_inserter(members));
REDIS_ASSERT(members.size() == 1 && members[0] == "m3",
"failed to test zrangebylex");
members.clear();
_redis.zrevrangebylex(key,
RightBoundedInterval<std::string>("m1", BoundType::LEFT_OPEN),
std::back_inserter(members));
REDIS_ASSERT(members.size() == 1 && members[0] == "m1",
"failed to test zrevrangebylex");
REDIS_ASSERT(_redis.zremrangebylex(key,
BoundedInterval<std::string>("m1", "m3", BoundType::OPEN)) == 1,
"failed to test zremrangebylex");
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_multi_zset() {
auto k1 = test_key("k1");
auto k2 = test_key("k2");
auto k3 = test_key("k3");
KeyDeleter<RedisInstance> deleter(_redis, {k1, k2, k3});
_redis.zadd(k1, {std::make_pair("a", 1), std::make_pair("b", 2)});
_redis.zadd(k2, {std::make_pair("a", 2), std::make_pair("c", 3)});
REDIS_ASSERT(_redis.zinterstore(k3, {k1, k2}) == 1, "failed to test zinterstore");
auto score = _redis.zscore(k3, "a");
REDIS_ASSERT(bool(score) && *score == 3, "failed to test zinterstore");
REDIS_ASSERT(_redis.zinterstore(k3, k1, 2) == 2, "failed to test zinterstore");
_redis.del(k3);
REDIS_ASSERT(_redis.zinterstore(k3, {k1, k2}, Aggregation::MAX) == 1,
"failed to test zinterstore");
score = _redis.zscore(k3, "a");
REDIS_ASSERT(bool(score) && *score == 2, "failed to test zinterstore");
_redis.del(k3);
REDIS_ASSERT(_redis.zunionstore(k3,
{std::make_pair(k1, 1), std::make_pair(k2, 2)},
Aggregation::MIN) == 3,
"failed to test zunionstore");
std::vector<std::pair<std::string, double>> res;
_redis.zrange(k3, 0, -1, std::back_inserter(res));
for (const auto &ele : res) {
if (ele.first == "a") {
REDIS_ASSERT(ele.second == 1, "failed to test zunionstore");
} else if (ele.first == "b") {
REDIS_ASSERT(ele.second == 2, "failed to test zunionstore");
} else if (ele.first == "c") {
REDIS_ASSERT(ele.second == 6, "failed to test zunionstore");
} else {
REDIS_ASSERT(false, "failed to test zuionstore");
}
}
REDIS_ASSERT(_redis.zunionstore(k3, k1, 2) == 2, "failed to test zunionstore");
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_zpop() {
auto key = test_key("zpop");
KeyDeleter<RedisInstance> deleter(_redis, key);
_redis.zadd(key, {std::make_pair("m1", 1.1),
std::make_pair("m2", 2.2),
std::make_pair("m3", 3.3),
std::make_pair("m4", 4.4),
std::make_pair("m5", 5.5),
std::make_pair("m6", 6.6)});
auto item = _redis.zpopmax(key);
REDIS_ASSERT(item && item->first == "m6", "failed to test zpopmax");
item = _redis.zpopmin(key);
REDIS_ASSERT(item && item->first == "m1", "failed to test zpopmin");
std::vector<std::pair<std::string, double>> vec;
_redis.zpopmax(key, 2, std::back_inserter(vec));
REDIS_ASSERT(vec.size() == 2 && vec[0].first == "m5" && vec[1].first == "m4",
"failed to test zpopmax");
std::unordered_map<std::string, double> m;
_redis.zpopmin(key, 2, std::inserter(m, m.end()));
REDIS_ASSERT(m.size() == 2 && m.find("m3") != m.end() && m.find("m2") != m.end(),
"failed to test zpopmin");
}
template <typename RedisInstance>
void ZSetCmdTest<RedisInstance>::_test_bzpop() {
auto key1 = test_key("bzpop1");
auto key2 = test_key("bzpop2");
KeyDeleter<RedisInstance> deleter(_redis, {key1, key2});
_redis.zadd(key1, {std::make_pair("m1", 1.1),
std::make_pair("m2", 2.2),
std::make_pair("m3", 3.3),
std::make_pair("m4", 4.4),
std::make_pair("m5", 5.5),
std::make_pair("m6", 6.6)});
_redis.zadd(key2, {std::make_pair("m1", 1.1),
std::make_pair("m2", 2.2),
std::make_pair("m3", 3.3),
std::make_pair("m4", 4.4),
std::make_pair("m5", 5.5),
std::make_pair("m6", 6.6)});
auto item = _redis.bzpopmax(key1);
REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m6",
"failed to test bzpopmax");
item = _redis.bzpopmin(key1, std::chrono::seconds(1));
REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m1",
"failed to test zpopmin");
item = _redis.bzpopmax({key1, key2}, std::chrono::seconds(1));
REDIS_ASSERT(item && std::get<0>(*item) == key1 && std::get<1>(*item) == "m5",
"failed to test zpopmax");
item = _redis.bzpopmin({key2, key1});
REDIS_ASSERT(item && std::get<0>(*item) == key2 && std::get<1>(*item) == "m1",
"failed to test zpopmin");
}
}
}
}
#endif // end SEWENEW_REDISPLUSPLUS_TEST_ZSET_CMDS_TEST_HPP