mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Increases the robustness of integration tests (#259)
Updates multiplexer to make requests complete with error_code() rather than error_code(0) Integration tests now use io_context::run_for to run with a timeout and avoid deadlocks Tests now use concrete lambdas where generic ones are not required Tests now use BOOST_TEST with operator== to print values on error Tests now use anonymous namespaces to detect dead code Adds run_coroutine_test and removed start from common.hpp Updates test_reconnect to perform relevant checks Refactors how test_issue_50 launches its coroutines Groups tests in CMake as unit or integration Updates Jamfile tests to contain all unit tests and only unit tests
This commit is contained in:
committed by
GitHub
parent
0c8c6fcc09
commit
2fc54bc73b
@@ -20,7 +20,7 @@ add_library(boost_redis_tests_common STATIC common.cpp)
|
||||
target_compile_features(boost_redis_tests_common PRIVATE cxx_std_17)
|
||||
target_link_libraries(boost_redis_tests_common PRIVATE boost_redis_project_options)
|
||||
|
||||
macro(make_test TEST_NAME STANDARD)
|
||||
macro(make_test TEST_NAME)
|
||||
set(EXE_NAME "boost_redis_${TEST_NAME}")
|
||||
add_executable(${EXE_NAME} ${TEST_NAME}.cpp)
|
||||
target_link_libraries(${EXE_NAME} PRIVATE
|
||||
@@ -29,31 +29,32 @@ macro(make_test TEST_NAME STANDARD)
|
||||
boost_redis_project_options
|
||||
Boost::unit_test_framework
|
||||
)
|
||||
target_compile_features(${EXE_NAME} PRIVATE cxx_std_${STANDARD})
|
||||
add_test(${EXE_NAME} ${EXE_NAME})
|
||||
endmacro()
|
||||
|
||||
make_test(test_conn_quit 17)
|
||||
# TODO: Configure a Redis server with TLS in the CI and reenable this test.
|
||||
#make_test(test_conn_tls 17)
|
||||
make_test(test_low_level 17)
|
||||
make_test(test_conn_exec_retry 17)
|
||||
make_test(test_conn_exec_error 17)
|
||||
make_test(test_request 17)
|
||||
make_test(test_run 17)
|
||||
make_test(test_low_level_sync_sans_io 17)
|
||||
make_test(test_conn_check_health 17)
|
||||
# Unit tests
|
||||
make_test(test_low_level)
|
||||
make_test(test_request)
|
||||
make_test(test_low_level_sync_sans_io)
|
||||
make_test(test_any_adapter)
|
||||
|
||||
make_test(test_conn_exec 20)
|
||||
make_test(test_conn_push 20)
|
||||
make_test(test_conn_reconnect 20)
|
||||
make_test(test_conn_exec_cancel 20)
|
||||
make_test(test_conn_exec_cancel2 20)
|
||||
make_test(test_conn_echo_stress 20)
|
||||
make_test(test_any_adapter 17)
|
||||
make_test(test_conversions 17)
|
||||
make_test(test_issue_50 20)
|
||||
make_test(test_issue_181 17)
|
||||
# Tests that require a real Redis server
|
||||
make_test(test_conn_quit)
|
||||
# TODO: Configure a Redis server with TLS in the CI and reenable this test.
|
||||
#make_test(test_conn_tls)
|
||||
make_test(test_conn_exec_retry)
|
||||
make_test(test_conn_exec_error)
|
||||
make_test(test_run)
|
||||
make_test(test_conn_check_health)
|
||||
make_test(test_conn_exec)
|
||||
make_test(test_conn_push)
|
||||
make_test(test_conn_reconnect)
|
||||
make_test(test_conn_exec_cancel)
|
||||
make_test(test_conn_exec_cancel2)
|
||||
make_test(test_conn_echo_stress)
|
||||
make_test(test_issue_50)
|
||||
make_test(test_issue_181)
|
||||
make_test(test_conversions)
|
||||
|
||||
# Coverage
|
||||
set(
|
||||
|
||||
@@ -48,10 +48,9 @@ lib redis_test_common
|
||||
# B2 runs tests in parallel, and some tests rely on having exclusive
|
||||
# access to a Redis server, so we only run the ones that don't require a DB server.
|
||||
local tests =
|
||||
test_low_level_sync_sans_io
|
||||
test_low_level
|
||||
test_request
|
||||
test_run
|
||||
test_low_level_sync_sans_io
|
||||
test_any_adapter
|
||||
;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace net = boost::asio;
|
||||
|
||||
@@ -55,22 +56,17 @@ boost::redis::config make_test_config()
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
auto start(net::awaitable<void> op) -> int
|
||||
void run_coroutine_test(net::awaitable<void> op, std::chrono::steady_clock::duration timeout)
|
||||
{
|
||||
try {
|
||||
net::io_context ioc;
|
||||
net::co_spawn(ioc, std::move(op), [](std::exception_ptr p) {
|
||||
if (p)
|
||||
std::rethrow_exception(p);
|
||||
});
|
||||
ioc.run();
|
||||
|
||||
return 0;
|
||||
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << "start> " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 1;
|
||||
net::io_context ioc;
|
||||
bool finished = false;
|
||||
net::co_spawn(ioc, std::move(op), [&finished](std::exception_ptr p) {
|
||||
if (p)
|
||||
std::rethrow_exception(p);
|
||||
finished = true;
|
||||
});
|
||||
ioc.run_for(timeout);
|
||||
if (!finished)
|
||||
throw std::runtime_error("Coroutine test did not finish");
|
||||
}
|
||||
#endif // BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
@@ -8,14 +8,22 @@
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
// The timeout for tests involving communication to a real server.
|
||||
// Some tests use a longer timeout by multiplying this value by some
|
||||
// integral number.
|
||||
inline constexpr std::chrono::seconds test_timeout{30};
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
inline auto redir(boost::system::error_code& ec)
|
||||
{
|
||||
return boost::asio::redirect_error(boost::asio::use_awaitable, ec);
|
||||
}
|
||||
auto start(boost::asio::awaitable<void> op) -> int;
|
||||
void run_coroutine_test(
|
||||
boost::asio::awaitable<void>,
|
||||
std::chrono::steady_clock::duration timeout = test_timeout);
|
||||
#endif // BOOST_ASIO_HAS_CO_AWAIT
|
||||
|
||||
boost::redis::config make_test_config();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/response.hpp>
|
||||
|
||||
#include <boost/system/errc.hpp>
|
||||
#include <cstddef>
|
||||
#define BOOST_TEST_MODULE check_health
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
@@ -25,9 +25,12 @@ using boost::redis::ignore;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::generic_response;
|
||||
using boost::redis::consume_one;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// TODO: Test cancel(health_check)
|
||||
|
||||
namespace {
|
||||
|
||||
struct push_callback {
|
||||
connection* conn1;
|
||||
connection* conn2;
|
||||
@@ -82,10 +85,13 @@ BOOST_AUTO_TEST_CASE(check_health)
|
||||
auto cfg1 = make_test_config();
|
||||
cfg1.health_check_id = "conn1";
|
||||
cfg1.reconnect_wait_interval = std::chrono::seconds::zero();
|
||||
error_code res1;
|
||||
conn1.async_run(cfg1, {}, [&](auto ec) {
|
||||
|
||||
bool run1_finished = false, run2_finished = false, exec_finished = false;
|
||||
|
||||
conn1.async_run(cfg1, {}, [&](error_code ec) {
|
||||
run1_finished = true;
|
||||
std::cout << "async_run 1 completed: " << ec.message() << std::endl;
|
||||
res1 = ec;
|
||||
BOOST_TEST(ec != error_code());
|
||||
});
|
||||
|
||||
//--------------------------------
|
||||
@@ -96,10 +102,10 @@ BOOST_AUTO_TEST_CASE(check_health)
|
||||
|
||||
auto cfg2 = make_test_config();
|
||||
cfg2.health_check_id = "conn2";
|
||||
error_code res2;
|
||||
conn2.async_run(cfg2, {}, [&](auto ec) {
|
||||
conn2.async_run(cfg2, {}, [&](error_code ec) {
|
||||
run2_finished = true;
|
||||
std::cout << "async_run 2 completed: " << ec.message() << std::endl;
|
||||
res2 = ec;
|
||||
BOOST_TEST(ec != error_code());
|
||||
});
|
||||
|
||||
request req2;
|
||||
@@ -107,21 +113,25 @@ BOOST_AUTO_TEST_CASE(check_health)
|
||||
generic_response resp2;
|
||||
conn2.set_receive_response(resp2);
|
||||
|
||||
conn2.async_exec(req2, ignore, [](auto ec, auto) {
|
||||
conn2.async_exec(req2, ignore, [&exec_finished](error_code ec, std::size_t) {
|
||||
exec_finished = true;
|
||||
std::cout << "async_exec: " << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
});
|
||||
|
||||
//--------------------------------
|
||||
|
||||
push_callback{&conn1, &conn2, &resp2, &req1}(); // Starts reading pushes.
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(2 * test_timeout);
|
||||
|
||||
BOOST_TEST(!!res1);
|
||||
BOOST_TEST(!!res2);
|
||||
BOOST_TEST(run1_finished);
|
||||
BOOST_TEST(run2_finished);
|
||||
BOOST_TEST(exec_finished);
|
||||
|
||||
// Waits before exiting otherwise it might cause subsequent tests
|
||||
// to fail.
|
||||
std::this_thread::sleep_for(std::chrono::seconds{10});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -5,11 +5,14 @@
|
||||
*/
|
||||
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/logger.hpp>
|
||||
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/deferred.hpp>
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#define BOOST_TEST_MODULE echo_stress
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
@@ -30,6 +33,9 @@ using boost::redis::logger;
|
||||
using boost::redis::connection;
|
||||
using boost::redis::usage;
|
||||
using boost::redis::error;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace boost::redis {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, usage const& u)
|
||||
{
|
||||
@@ -43,90 +49,106 @@ std::ostream& operator<<(std::ostream& os, usage const& u)
|
||||
return os;
|
||||
}
|
||||
|
||||
auto push_consumer(std::shared_ptr<connection> conn, int expected) -> net::awaitable<void>
|
||||
} // namespace boost::redis
|
||||
|
||||
namespace {
|
||||
|
||||
auto push_consumer(connection& conn, int expected) -> net::awaitable<void>
|
||||
{
|
||||
int c = 0;
|
||||
for (error_code ec;;) {
|
||||
conn->receive(ec);
|
||||
conn.receive(ec);
|
||||
if (ec == error::sync_receive_push_failed) {
|
||||
ec = {};
|
||||
co_await conn->async_receive(redirect_error(net::use_awaitable, ec));
|
||||
co_await conn.async_receive(net::redirect_error(ec));
|
||||
} else if (!ec) {
|
||||
//std::cout << "Skipping suspension." << std::endl;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
BOOST_TEST(false);
|
||||
std::cout << "push_consumer error: " << ec.message() << std::endl;
|
||||
BOOST_TEST(false, "push_consumer error: " << ec.message());
|
||||
co_return;
|
||||
}
|
||||
if (++c == expected)
|
||||
break;
|
||||
}
|
||||
|
||||
conn->cancel();
|
||||
conn.cancel();
|
||||
}
|
||||
|
||||
auto echo_session(std::shared_ptr<connection> conn, std::shared_ptr<request> pubs, int n)
|
||||
-> net::awaitable<void>
|
||||
auto echo_session(connection& conn, const request& pubs, int n) -> net::awaitable<void>
|
||||
{
|
||||
for (auto i = 0; i < n; ++i)
|
||||
co_await conn->async_exec(*pubs, ignore, net::deferred);
|
||||
co_await conn.async_exec(pubs, ignore);
|
||||
}
|
||||
|
||||
auto async_echo_stress(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
void rethrow_on_error(std::exception_ptr exc)
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
auto cfg = make_test_config();
|
||||
cfg.health_check_interval = std::chrono::seconds::zero();
|
||||
run(
|
||||
conn,
|
||||
cfg,
|
||||
boost::asio::error::operation_aborted,
|
||||
boost::redis::operation::receive,
|
||||
boost::redis::logger::level::crit);
|
||||
|
||||
request req;
|
||||
req.push("SUBSCRIBE", "channel");
|
||||
co_await conn->async_exec(req, ignore, net::deferred);
|
||||
|
||||
// Number of coroutines that will send pings sharing the same
|
||||
// connection to redis.
|
||||
int const sessions = 150;
|
||||
|
||||
// The number of pings that will be sent by each session.
|
||||
int const msgs = 200;
|
||||
|
||||
// The number of publishes that will be sent by each session with
|
||||
// each message.
|
||||
int const n_pubs = 25;
|
||||
|
||||
// This is the total number of pushes we will receive.
|
||||
int total_pushes = sessions * msgs * n_pubs + 1;
|
||||
|
||||
auto pubs = std::make_shared<request>();
|
||||
pubs->push("PING");
|
||||
for (int i = 0; i < n_pubs; ++i)
|
||||
pubs->push("PUBLISH", "channel", "payload");
|
||||
|
||||
// Op that will consume the pushes counting down until all expected
|
||||
// pushes have been received.
|
||||
net::co_spawn(ex, push_consumer(conn, total_pushes), net::detached);
|
||||
|
||||
for (int i = 0; i < sessions; ++i)
|
||||
net::co_spawn(ex, echo_session(conn, pubs, msgs), net::detached);
|
||||
if (exc)
|
||||
std::rethrow_exception(exc);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(echo_stress)
|
||||
{
|
||||
net::io_context ioc;
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
net::co_spawn(ioc, async_echo_stress(conn), net::detached);
|
||||
ioc.run();
|
||||
// Setup
|
||||
net::io_context ctx;
|
||||
connection conn{ctx};
|
||||
auto cfg = make_test_config();
|
||||
cfg.health_check_interval = std::chrono::seconds::zero();
|
||||
|
||||
std::cout << "-------------------\n" << conn->get_usage() << std::endl;
|
||||
// Number of coroutines that will send pings sharing the same
|
||||
// connection to redis.
|
||||
constexpr int sessions = 150;
|
||||
|
||||
// The number of pings that will be sent by each session.
|
||||
constexpr int msgs = 200;
|
||||
|
||||
// The number of publishes that will be sent by each session with
|
||||
// each message.
|
||||
constexpr int n_pubs = 25;
|
||||
|
||||
// This is the total number of pushes we will receive.
|
||||
constexpr int total_pushes = sessions * msgs * n_pubs + 1;
|
||||
|
||||
request pubs;
|
||||
pubs.push("PING");
|
||||
for (int i = 0; i < n_pubs; ++i)
|
||||
pubs.push("PUBLISH", "channel", "payload");
|
||||
|
||||
// Run the connection
|
||||
bool run_finished = false, subscribe_finished = false;
|
||||
conn.async_run(cfg, logger{logger::level::crit}, [&run_finished](error_code ec) {
|
||||
run_finished = true;
|
||||
BOOST_TEST(ec == net::error::operation_aborted);
|
||||
std::clog << "async_run finished" << std::endl;
|
||||
});
|
||||
|
||||
// Subscribe, then launch the coroutines
|
||||
request req;
|
||||
req.push("SUBSCRIBE", "channel");
|
||||
conn.async_exec(req, ignore, [&](error_code ec, std::size_t) {
|
||||
subscribe_finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
|
||||
// Op that will consume the pushes counting down until all expected
|
||||
// pushes have been received.
|
||||
net::co_spawn(ctx, push_consumer(conn, total_pushes), rethrow_on_error);
|
||||
|
||||
for (int i = 0; i < sessions; ++i)
|
||||
net::co_spawn(ctx, echo_session(conn, pubs, msgs), rethrow_on_error);
|
||||
});
|
||||
|
||||
// Run the test
|
||||
ctx.run_for(2 * test_timeout);
|
||||
BOOST_TEST(run_finished);
|
||||
BOOST_TEST(subscribe_finished);
|
||||
|
||||
// Print statistics
|
||||
std::cout << "-------------------\n" << conn.get_usage() << std::endl;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#else
|
||||
BOOST_AUTO_TEST_CASE(dummy) { BOOST_TEST(true); }
|
||||
BOOST_AUTO_TEST_CASE(dummy) { }
|
||||
#endif
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#define BOOST_TEST_MODULE conn_exec
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// TODO: Test whether HELLO won't be inserted passt commands that have
|
||||
// been already writen.
|
||||
// TODO: Test whether HELLO won't be inserted past commands that have
|
||||
// been already written.
|
||||
// TODO: Test async_exec with empty request e.g. hgetall with an empty
|
||||
// container.
|
||||
|
||||
@@ -31,6 +31,10 @@ using boost::redis::ignore;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::system::error_code;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
// Sends three requests where one of them has a hello with a priority
|
||||
// set, which means it should be executed first.
|
||||
@@ -57,19 +61,19 @@ BOOST_AUTO_TEST_CASE(hello_priority)
|
||||
bool seen2 = false;
|
||||
bool seen3 = false;
|
||||
|
||||
conn->async_exec(req1, ignore, [&](auto ec, auto) {
|
||||
conn->async_exec(req1, ignore, [&](error_code ec, std::size_t) {
|
||||
// Second callback to the called.
|
||||
std::cout << "req1" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(!seen2);
|
||||
BOOST_TEST(seen3);
|
||||
seen1 = true;
|
||||
});
|
||||
|
||||
conn->async_exec(req2, ignore, [&](auto ec, auto) {
|
||||
conn->async_exec(req2, ignore, [&](error_code ec, std::size_t) {
|
||||
// Last callback to the called.
|
||||
std::cout << "req2" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(seen1);
|
||||
BOOST_TEST(seen3);
|
||||
seen2 = true;
|
||||
@@ -77,17 +81,20 @@ BOOST_AUTO_TEST_CASE(hello_priority)
|
||||
conn->cancel(operation::reconnection);
|
||||
});
|
||||
|
||||
conn->async_exec(req3, ignore, [&](auto ec, auto) {
|
||||
conn->async_exec(req3, ignore, [&](error_code ec, std::size_t) {
|
||||
// Callback that will be called first.
|
||||
std::cout << "req3" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(!seen1);
|
||||
BOOST_TEST(!seen2);
|
||||
seen3 = true;
|
||||
});
|
||||
|
||||
run(conn);
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(seen1);
|
||||
BOOST_TEST(seen2);
|
||||
BOOST_TEST(seen3);
|
||||
}
|
||||
|
||||
// Tries to receive a string in an int and gets an error.
|
||||
@@ -101,14 +108,17 @@ BOOST_AUTO_TEST_CASE(wrong_response_data_type)
|
||||
net::io_context ioc;
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
bool finished = false;
|
||||
|
||||
conn->async_exec(req, resp, [conn](auto ec, auto) {
|
||||
BOOST_CHECK_EQUAL(ec, boost::redis::error::not_a_number);
|
||||
conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == boost::redis::error::not_a_number);
|
||||
conn->cancel(operation::reconnection);
|
||||
finished = true;
|
||||
});
|
||||
|
||||
run(conn);
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cancel_request_if_not_connected)
|
||||
@@ -119,12 +129,15 @@ BOOST_AUTO_TEST_CASE(cancel_request_if_not_connected)
|
||||
|
||||
net::io_context ioc;
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
conn->async_exec(req, ignore, [conn](auto ec, auto) {
|
||||
BOOST_CHECK_EQUAL(ec, boost::redis::error::not_connected);
|
||||
bool finished = false;
|
||||
conn->async_exec(req, ignore, [conn, &finished](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec, boost::redis::error::not_connected);
|
||||
conn->cancel();
|
||||
finished = true;
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(correct_database)
|
||||
@@ -141,19 +154,25 @@ BOOST_AUTO_TEST_CASE(correct_database)
|
||||
|
||||
generic_response resp;
|
||||
|
||||
conn->async_exec(req, resp, [&](auto ec, auto n) {
|
||||
BOOST_TEST(!ec);
|
||||
bool exec_finished = false, run_finished = false;
|
||||
|
||||
conn->async_exec(req, resp, [&](error_code ec, std::size_t n) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
std::clog << "async_exec has completed: " << n << std::endl;
|
||||
conn->cancel();
|
||||
exec_finished = true;
|
||||
});
|
||||
|
||||
conn->async_run(cfg, {}, [](auto) {
|
||||
conn->async_run(cfg, {}, [&run_finished](error_code) {
|
||||
std::clog << "async_run has exited." << std::endl;
|
||||
run_finished = true;
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST_REQUIRE(exec_finished);
|
||||
BOOST_TEST_REQUIRE(run_finished);
|
||||
|
||||
assert(!resp.value().empty());
|
||||
BOOST_TEST_REQUIRE(!resp.value().empty());
|
||||
auto const& value = resp.value().front().value;
|
||||
auto const pos = value.find("db=");
|
||||
auto const index_str = value.substr(pos + 3, 1);
|
||||
@@ -179,22 +198,22 @@ BOOST_AUTO_TEST_CASE(large_number_of_concurrent_requests_issue_170)
|
||||
cfg.health_check_interval = std::chrono::seconds(0);
|
||||
conn->async_run(cfg, {}, net::detached);
|
||||
|
||||
int counter = 0;
|
||||
int const repeat = 8000;
|
||||
constexpr int repeat = 8000;
|
||||
int remaining = repeat;
|
||||
|
||||
for (int i = 0; i < repeat; ++i) {
|
||||
auto req = std::make_shared<request>();
|
||||
req->push("PING", payload);
|
||||
conn->async_exec(*req, ignore, [req, &counter, conn](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
if (++counter == repeat)
|
||||
conn->async_exec(*req, ignore, [req, &remaining, conn](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
if (--remaining == 0)
|
||||
conn->cancel();
|
||||
});
|
||||
}
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_CHECK_EQUAL(counter, repeat);
|
||||
BOOST_TEST(remaining == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(exec_any_adapter)
|
||||
@@ -208,13 +227,19 @@ BOOST_AUTO_TEST_CASE(exec_any_adapter)
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
conn->async_exec(req, boost::redis::any_adapter(res), [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
bool finished = false;
|
||||
|
||||
conn->async_exec(req, boost::redis::any_adapter(res), [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel();
|
||||
finished = true;
|
||||
});
|
||||
|
||||
run(conn);
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST_REQUIRE(finished);
|
||||
|
||||
BOOST_TEST(std::get<0>(res).value() == "PONG");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -7,19 +7,19 @@
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/system/errc.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#define BOOST_TEST_MODULE conn_exec_cancel
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
#include <boost/asio/experimental/awaitable_operators.hpp>
|
||||
|
||||
// NOTE1: I have observed that if hello and
|
||||
// blpop are sent toguether, Redis will send the response of hello
|
||||
// blpop are sent together, Redis will send the response of hello
|
||||
// right away, not waiting for blpop.
|
||||
|
||||
namespace net = boost::asio;
|
||||
@@ -36,6 +36,8 @@ using boost::redis::logger;
|
||||
using boost::redis::connection;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
auto implicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
@@ -48,7 +50,7 @@ auto implicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
// See NOTE1.
|
||||
request req0;
|
||||
req0.push("PING");
|
||||
co_await conn->async_exec(req0, ignore, net::use_awaitable);
|
||||
co_await conn->async_exec(req0, ignore);
|
||||
|
||||
// Will be cancelled after it has been written but before the
|
||||
// response arrives.
|
||||
@@ -66,15 +68,13 @@ auto implicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
|
||||
// I have observed this produces terminal cancellation so it can't
|
||||
// be ignored, an error is expected.
|
||||
BOOST_CHECK_EQUAL(ec1, net::error::operation_aborted);
|
||||
BOOST_TEST(!ec2);
|
||||
BOOST_TEST(ec1 == net::error::operation_aborted);
|
||||
BOOST_TEST(ec2 == error_code());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_ignore_implicit_cancel_of_req_written)
|
||||
{
|
||||
net::io_context ioc;
|
||||
net::co_spawn(ioc, implicit_cancel_of_req_written(), net::detached);
|
||||
ioc.run();
|
||||
run_coroutine_test(implicit_cancel_of_req_written());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_cancel_of_req_written_on_run_canceled)
|
||||
@@ -92,12 +92,15 @@ BOOST_AUTO_TEST_CASE(test_cancel_of_req_written_on_run_canceled)
|
||||
req1.get_config().cancel_if_unresponded = true;
|
||||
req1.push("BLPOP", "any", 0);
|
||||
|
||||
auto c1 = [&](auto ec, auto) {
|
||||
bool finished = false;
|
||||
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
BOOST_CHECK_EQUAL(ec, net::error::operation_aborted);
|
||||
finished = true;
|
||||
};
|
||||
|
||||
auto c0 = [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c0 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c1);
|
||||
};
|
||||
|
||||
@@ -109,15 +112,18 @@ BOOST_AUTO_TEST_CASE(test_cancel_of_req_written_on_run_canceled)
|
||||
|
||||
net::steady_timer st{ioc};
|
||||
st.expires_after(std::chrono::seconds{1});
|
||||
st.async_wait([&](auto ec) {
|
||||
BOOST_TEST(!ec);
|
||||
st.async_wait([&](error_code ec) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel(operation::run);
|
||||
conn->cancel(operation::reconnection);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(finished);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#else
|
||||
BOOST_AUTO_TEST_CASE(dummy) { BOOST_TEST(true); }
|
||||
BOOST_AUTO_TEST_CASE(dummy) { }
|
||||
#endif
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/system/errc.hpp>
|
||||
#include <cstddef>
|
||||
#define BOOST_TEST_MODULE conn_exec_cancel
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
@@ -16,7 +15,6 @@
|
||||
#include <iostream>
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
#include <boost/asio/experimental/awaitable_operators.hpp>
|
||||
|
||||
// NOTE1: Sends hello separately. I have observed that if hello and
|
||||
// blpop are sent toguether, Redis will send the response of hello
|
||||
@@ -25,7 +23,6 @@
|
||||
|
||||
namespace net = boost::asio;
|
||||
using error_code = boost::system::error_code;
|
||||
using namespace net::experimental::awaitable_operators;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
@@ -37,6 +34,8 @@ using boost::redis::logger;
|
||||
using boost::redis::connection;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
@@ -52,46 +51,45 @@ auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
// See NOTE1.
|
||||
request req0;
|
||||
req0.push("PING", "async_ignore_explicit_cancel_of_req_written");
|
||||
co_await conn->async_exec(req0, gresp, net::use_awaitable);
|
||||
co_await conn->async_exec(req0, gresp);
|
||||
|
||||
request req1;
|
||||
req1.push("BLPOP", "any", 3);
|
||||
|
||||
bool seen = false;
|
||||
conn->async_exec(req1, gresp, [&](auto ec, auto) mutable {
|
||||
// No error should occur since the cancelation should be
|
||||
// ignored.
|
||||
conn->async_exec(req1, gresp, [&](error_code ec, std::size_t) {
|
||||
// No error should occur since the cancellation should be ignored
|
||||
std::cout << "async_exec (1): " << ec.message() << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
seen = true;
|
||||
});
|
||||
|
||||
// Will complete while BLPOP is pending.
|
||||
boost::system::error_code ec1;
|
||||
co_await st.async_wait(net::redirect_error(net::use_awaitable, ec1));
|
||||
error_code ec;
|
||||
co_await st.async_wait(net::redirect_error(ec));
|
||||
conn->cancel(operation::exec);
|
||||
|
||||
BOOST_TEST(!ec1);
|
||||
BOOST_TEST(ec == error_code());
|
||||
|
||||
request req3;
|
||||
req3.push("PING");
|
||||
request req2;
|
||||
req2.push("PING");
|
||||
|
||||
// Test whether the connection remains usable after a call to
|
||||
// cancel(exec).
|
||||
co_await conn->async_exec(req3, gresp, net::redirect_error(net::use_awaitable, ec1));
|
||||
co_await conn->async_exec(req2, gresp, net::redirect_error(ec));
|
||||
conn->cancel();
|
||||
|
||||
BOOST_TEST(!ec1);
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(seen);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_ignore_explicit_cancel_of_req_written)
|
||||
{
|
||||
net::io_context ioc;
|
||||
net::co_spawn(ioc, async_ignore_explicit_cancel_of_req_written(), net::detached);
|
||||
ioc.run();
|
||||
run_coroutine_test(async_ignore_explicit_cancel_of_req_written());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#else
|
||||
BOOST_AUTO_TEST_CASE(dummy) { BOOST_TEST(true); }
|
||||
BOOST_AUTO_TEST_CASE(dummy) { }
|
||||
#endif
|
||||
|
||||
@@ -5,31 +5,31 @@
|
||||
*/
|
||||
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/logger.hpp>
|
||||
|
||||
#include <boost/system/errc.hpp>
|
||||
#define BOOST_TEST_MODULE conn_exec_error
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
namespace resp3 = redis::resp3;
|
||||
using error_code = boost::system::error_code;
|
||||
using connection = boost::redis::connection;
|
||||
using boost::redis::connection;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::generic_response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
using boost::redis::error;
|
||||
using boost::redis::logger;
|
||||
using boost::redis::operation;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_CASE(no_ignore_error)
|
||||
{
|
||||
request req;
|
||||
@@ -41,15 +41,20 @@ BOOST_AUTO_TEST_CASE(no_ignore_error)
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
conn->async_exec(req, ignore, [&](auto ec, auto) {
|
||||
BOOST_CHECK_EQUAL(ec, error::resp3_simple_error);
|
||||
bool exec_finished = false;
|
||||
|
||||
conn->async_exec(req, ignore, [&](error_code ec, std::size_t) {
|
||||
exec_finished = true;
|
||||
BOOST_TEST(ec == error::resp3_simple_error);
|
||||
conn->cancel(operation::run);
|
||||
conn->cancel(operation::reconnection);
|
||||
});
|
||||
|
||||
run(conn);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(exec_finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(has_diagnostic)
|
||||
@@ -68,19 +73,22 @@ BOOST_AUTO_TEST_CASE(has_diagnostic)
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
response<std::string, std::string> resp;
|
||||
conn->async_exec(req, resp, [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
bool exec_finished = false;
|
||||
conn->async_exec(req, resp, [&](error_code ec, std::size_t) {
|
||||
exec_finished = true;
|
||||
|
||||
BOOST_TEST(ec == error_code());
|
||||
|
||||
// HELLO
|
||||
BOOST_TEST(std::get<0>(resp).has_error());
|
||||
BOOST_CHECK_EQUAL(std::get<0>(resp).error().data_type, resp3::type::simple_error);
|
||||
BOOST_TEST(std::get<0>(resp).error().data_type == resp3::type::simple_error);
|
||||
auto const diag = std::get<0>(resp).error().diagnostic;
|
||||
BOOST_TEST(!std::empty(diag));
|
||||
std::cout << "has_diagnostic: " << diag << std::endl;
|
||||
|
||||
// PING
|
||||
BOOST_TEST(std::get<1>(resp).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<1>(resp).value(), "Barra do Una");
|
||||
BOOST_TEST(std::get<1>(resp).value() == "Barra do Una");
|
||||
|
||||
conn->cancel(operation::run);
|
||||
conn->cancel(operation::reconnection);
|
||||
@@ -88,7 +96,9 @@ BOOST_AUTO_TEST_CASE(has_diagnostic)
|
||||
|
||||
run(conn);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(exec_finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline)
|
||||
@@ -109,24 +119,28 @@ BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline)
|
||||
net::io_context ioc;
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
auto c2 = [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
bool c2_called = false, c1_called = false;
|
||||
|
||||
auto c2 = [&](error_code ec, std::size_t) {
|
||||
c2_called = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(std::get<0>(resp2).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<0>(resp2).value(), "req2-msg1");
|
||||
BOOST_TEST(std::get<0>(resp2).value() == "req2-msg1");
|
||||
conn->cancel(operation::run);
|
||||
conn->cancel(operation::reconnection);
|
||||
};
|
||||
|
||||
auto c1 = [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
c1_called = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(std::get<2>(resp1).has_error());
|
||||
BOOST_CHECK_EQUAL(std::get<2>(resp1).error().data_type, resp3::type::simple_error);
|
||||
BOOST_TEST(std::get<2>(resp1).error().data_type == resp3::type::simple_error);
|
||||
auto const diag = std::get<2>(resp1).error().diagnostic;
|
||||
BOOST_TEST(!std::empty(diag));
|
||||
std::cout << "resp3_error_in_cmd_pipeline: " << diag << std::endl;
|
||||
|
||||
BOOST_TEST(std::get<3>(resp1).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<3>(resp1).value(), "req1-msg3");
|
||||
BOOST_TEST(std::get<3>(resp1).value() == "req1-msg3");
|
||||
|
||||
conn->async_exec(req2, resp2, c2);
|
||||
};
|
||||
@@ -134,7 +148,10 @@ BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline)
|
||||
conn->async_exec(req1, resp1, c1);
|
||||
run(conn);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(c1_called);
|
||||
BOOST_TEST(c2_called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(error_in_transaction)
|
||||
@@ -163,8 +180,11 @@ BOOST_AUTO_TEST_CASE(error_in_transaction)
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
conn->async_exec(req, resp, [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
bool finished = false;
|
||||
|
||||
conn->async_exec(req, resp, [&](error_code ec, std::size_t) {
|
||||
finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
|
||||
BOOST_TEST(std::get<0>(resp).has_value());
|
||||
BOOST_TEST(std::get<1>(resp).has_value());
|
||||
@@ -175,23 +195,22 @@ BOOST_AUTO_TEST_CASE(error_in_transaction)
|
||||
|
||||
// Test errors in the pipeline commands.
|
||||
BOOST_TEST(std::get<0>(std::get<5>(resp).value()).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<0>(std::get<5>(resp).value()).value(), "PONG");
|
||||
BOOST_TEST(std::get<0>(std::get<5>(resp).value()).value() == "PONG");
|
||||
|
||||
// The ping in the transaction that should be an error.
|
||||
BOOST_TEST(std::get<1>(std::get<5>(resp).value()).has_error());
|
||||
BOOST_CHECK_EQUAL(
|
||||
std::get<1>(std::get<5>(resp).value()).error().data_type,
|
||||
resp3::type::simple_error);
|
||||
BOOST_TEST(
|
||||
std::get<1>(std::get<5>(resp).value()).error().data_type == resp3::type::simple_error);
|
||||
auto const diag = std::get<1>(std::get<5>(resp).value()).error().diagnostic;
|
||||
BOOST_TEST(!std::empty(diag));
|
||||
|
||||
// The ping thereafter in the transaction should not be an error.
|
||||
BOOST_TEST(std::get<2>(std::get<5>(resp).value()).has_value());
|
||||
//BOOST_CHECK_EQUAL(std::get<2>(std::get<4>(resp).value()).value(), "PONG");
|
||||
BOOST_TEST(std::get<2>(std::get<5>(resp).value()).value() == "PONG");
|
||||
|
||||
// The command right after the pipeline should be successful.
|
||||
BOOST_TEST(std::get<6>(resp).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<6>(resp).value(), "PONG");
|
||||
BOOST_TEST(std::get<6>(resp).value() == "PONG");
|
||||
|
||||
conn->cancel(operation::run);
|
||||
conn->cancel(operation::reconnection);
|
||||
@@ -199,7 +218,9 @@ BOOST_AUTO_TEST_CASE(error_in_transaction)
|
||||
|
||||
run(conn);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(finished);
|
||||
}
|
||||
|
||||
// This test is important because a SUBSCRIBE command has no response
|
||||
@@ -212,8 +233,8 @@ BOOST_AUTO_TEST_CASE(error_in_transaction)
|
||||
// even more complex. For example, without a ping, we might get the
|
||||
// sequence HELLO + SUBSCRIBE + PING where the hello and ping are
|
||||
// automatically sent by the implementation. In this case, if the
|
||||
// subscribe synthax is wrong, redis will send a response, which does
|
||||
// not exist on success. That response will be interprested as the
|
||||
// subscribe syntax is wrong, redis will send a response, which does
|
||||
// not exist on success. That response will be interpreted as the
|
||||
// response to the PING command that comes thereafter and won't be
|
||||
// forwarded to the receive_op, resulting in a difficult to handle
|
||||
// error.
|
||||
@@ -223,19 +244,23 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax)
|
||||
req1.push("PING");
|
||||
|
||||
request req2;
|
||||
req2.push("SUBSCRIBE"); // Wrong command synthax.
|
||||
req2.push("SUBSCRIBE"); // Wrong command syntax.
|
||||
|
||||
net::io_context ioc;
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
auto c2 = [&](auto ec, auto) {
|
||||
bool c1_called = false, c2_called = false, c3_called = false;
|
||||
|
||||
auto c2 = [&](error_code ec, std::size_t) {
|
||||
c2_called = true;
|
||||
std::cout << "async_exec: subscribe" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
};
|
||||
|
||||
auto c1 = [&](auto ec, auto) {
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
c1_called = true;
|
||||
std::cout << "async_exec: hello" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c2);
|
||||
};
|
||||
|
||||
@@ -244,7 +269,8 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax)
|
||||
generic_response gresp;
|
||||
conn->set_receive_response(gresp);
|
||||
|
||||
auto c3 = [&](auto ec, auto) {
|
||||
auto c3 = [&](error_code ec, std::size_t) {
|
||||
c3_called = true;
|
||||
std::cout << "async_receive" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(gresp.has_error());
|
||||
@@ -259,5 +285,11 @@ BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax)
|
||||
|
||||
run(conn);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(c1_called);
|
||||
BOOST_TEST(c2_called);
|
||||
BOOST_TEST(c3_called);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#define BOOST_TEST_MODULE conn_exec_retry
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
@@ -27,6 +28,8 @@ using boost::redis::logger;
|
||||
using boost::redis::config;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_CASE(request_retry_false)
|
||||
{
|
||||
request req0;
|
||||
@@ -46,31 +49,40 @@ BOOST_AUTO_TEST_CASE(request_retry_false)
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
net::steady_timer st{ioc};
|
||||
|
||||
bool timer_finished = false, c2_called = false, c1_called = false, c0_called = false,
|
||||
run_finished = false;
|
||||
|
||||
st.expires_after(std::chrono::seconds{1});
|
||||
st.async_wait([&](auto) {
|
||||
st.async_wait([&](error_code ec) {
|
||||
// Cancels the request before receiving the response. This
|
||||
// should cause the third request to complete with error
|
||||
// although it has cancel_on_connection_lost = false. The reason
|
||||
// being it has already been written so
|
||||
// cancel_on_connection_lost does not apply.
|
||||
timer_finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel(operation::run);
|
||||
conn->cancel(operation::reconnection);
|
||||
std::cout << "async_wait" << std::endl;
|
||||
});
|
||||
|
||||
auto c2 = [&](auto ec, auto) {
|
||||
auto c2 = [&](error_code ec, std::size_t) {
|
||||
c2_called = true;
|
||||
std::cout << "c2" << std::endl;
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::operation_aborted);
|
||||
BOOST_TEST(ec == net::error::operation_aborted);
|
||||
};
|
||||
|
||||
auto c1 = [&](auto ec, auto) {
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
c1_called = true;
|
||||
std::cout << "c1" << std::endl;
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::operation_aborted);
|
||||
BOOST_TEST(ec == net::error::operation_aborted);
|
||||
};
|
||||
|
||||
auto c0 = [&](auto ec, auto) {
|
||||
auto c0 = [&](error_code ec, std::size_t) {
|
||||
c0_called = true;
|
||||
std::cout << "c0" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c1);
|
||||
conn->async_exec(req2, ignore, c2);
|
||||
};
|
||||
@@ -78,15 +90,19 @@ BOOST_AUTO_TEST_CASE(request_retry_false)
|
||||
conn->async_exec(req0, ignore, c0);
|
||||
|
||||
auto cfg = make_test_config();
|
||||
conn->async_run(
|
||||
cfg,
|
||||
{boost::redis::logger::level::debug},
|
||||
[&](boost::system::error_code const& ec) {
|
||||
std::cout << "async_run: " << ec.message() << std::endl;
|
||||
conn->cancel();
|
||||
});
|
||||
conn->async_run(cfg, {boost::redis::logger::level::debug}, [&](error_code ec) {
|
||||
run_finished = true;
|
||||
std::cout << "async_run: " << ec.message() << std::endl;
|
||||
conn->cancel();
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(timer_finished);
|
||||
BOOST_TEST(c0_called);
|
||||
BOOST_TEST(c1_called);
|
||||
BOOST_TEST(c2_called);
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(request_retry_true)
|
||||
@@ -113,32 +129,42 @@ BOOST_AUTO_TEST_CASE(request_retry_true)
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
net::steady_timer st{ioc};
|
||||
|
||||
bool timer_finished = false, c0_called = false, c1_called = false, c2_called = false,
|
||||
c3_called = false, run_finished = false;
|
||||
|
||||
st.expires_after(std::chrono::seconds{1});
|
||||
st.async_wait([&](auto) {
|
||||
st.async_wait([&](error_code ec) {
|
||||
// Cancels the request before receiving the response. This
|
||||
// should cause the third request to not complete with error
|
||||
// since it has cancel_if_unresponded = true and cancellation
|
||||
// comes after it was written.
|
||||
timer_finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel(operation::run);
|
||||
});
|
||||
|
||||
auto c3 = [&](auto ec, auto) {
|
||||
auto c3 = [&](error_code ec, std::size_t) {
|
||||
c3_called = true;
|
||||
std::cout << "c3: " << ec.message() << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel();
|
||||
};
|
||||
|
||||
auto c2 = [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c2 = [&](error_code ec, std::size_t) {
|
||||
c2_called = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req3, ignore, c3);
|
||||
};
|
||||
|
||||
auto c1 = [](auto ec, auto) {
|
||||
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
c1_called = true;
|
||||
BOOST_TEST(ec == net::error::operation_aborted);
|
||||
};
|
||||
|
||||
auto c0 = [&](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c0 = [&](error_code ec, std::size_t) {
|
||||
c0_called = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c1);
|
||||
conn->async_exec(req2, ignore, c2);
|
||||
};
|
||||
@@ -147,10 +173,20 @@ BOOST_AUTO_TEST_CASE(request_retry_true)
|
||||
|
||||
auto cfg = make_test_config();
|
||||
cfg.health_check_interval = 5s;
|
||||
conn->async_run(cfg, {}, [&](auto ec) {
|
||||
conn->async_run(cfg, {}, [&](error_code ec) {
|
||||
run_finished = true;
|
||||
std::cout << ec.message() << std::endl;
|
||||
BOOST_TEST(!!ec);
|
||||
BOOST_TEST(ec != error_code());
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(timer_finished);
|
||||
BOOST_TEST(c0_called);
|
||||
BOOST_TEST(c1_called);
|
||||
BOOST_TEST(c2_called);
|
||||
BOOST_TEST(c3_called);
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -7,24 +7,23 @@
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/logger.hpp>
|
||||
|
||||
#include <boost/asio/as_tuple.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/asio/experimental/channel_error.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
#define BOOST_TEST_MODULE conn - push
|
||||
|
||||
#define BOOST_TEST_MODULE conn_push
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
|
||||
using boost::redis::operation;
|
||||
using connection = boost::redis::connection;
|
||||
using error_code = boost::system::error_code;
|
||||
using net::as_tuple;
|
||||
using boost::redis::connection;
|
||||
using boost::system::error_code;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
@@ -33,6 +32,8 @@ using boost::system::error_code;
|
||||
using boost::redis::logger;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_CASE(receives_push_waiting_resps)
|
||||
{
|
||||
request req1;
|
||||
@@ -50,17 +51,22 @@ BOOST_AUTO_TEST_CASE(receives_push_waiting_resps)
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
auto c3 = [](auto ec, auto...) {
|
||||
bool push_received = false, c1_called = false, c2_called = false, c3_called = false;
|
||||
|
||||
auto c3 = [&](error_code ec, std::size_t) {
|
||||
c3_called = true;
|
||||
std::cout << "c3: " << ec.message() << std::endl;
|
||||
};
|
||||
|
||||
auto c2 = [&, conn](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c2 = [&, conn](error_code ec, std::size_t) {
|
||||
c2_called = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req3, ignore, c3);
|
||||
};
|
||||
|
||||
auto c1 = [&, conn](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c1 = [&, conn](error_code ec, std::size_t) {
|
||||
c1_called = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c2);
|
||||
};
|
||||
|
||||
@@ -68,17 +74,19 @@ BOOST_AUTO_TEST_CASE(receives_push_waiting_resps)
|
||||
|
||||
run(conn, make_test_config(), {});
|
||||
|
||||
bool push_received = false;
|
||||
conn->async_receive([&, conn](auto ec, auto) {
|
||||
conn->async_receive([&, conn](error_code ec, std::size_t) {
|
||||
std::cout << "async_receive" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
push_received = true;
|
||||
conn->cancel();
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(push_received);
|
||||
BOOST_TEST(c1_called);
|
||||
BOOST_TEST(c2_called);
|
||||
BOOST_TEST(c3_called);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(push_received1)
|
||||
@@ -94,17 +102,19 @@ BOOST_AUTO_TEST_CASE(push_received1)
|
||||
req.push("SUBSCRIBE", "channel1");
|
||||
req.push("SUBSCRIBE", "channel2");
|
||||
|
||||
conn->async_exec(req, ignore, [conn](auto ec, auto) {
|
||||
bool push_received = false, exec_finished = false;
|
||||
|
||||
conn->async_exec(req, ignore, [&, conn](error_code ec, std::size_t) {
|
||||
exec_finished = true;
|
||||
std::cout << "async_exec" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
});
|
||||
|
||||
bool push_async_received = false;
|
||||
conn->async_receive([&, conn](auto ec, auto) {
|
||||
conn->async_receive([&, conn](error_code ec, std::size_t) {
|
||||
push_received = true;
|
||||
std::cout << "(1) async_receive" << std::endl;
|
||||
|
||||
BOOST_TEST(!ec);
|
||||
push_async_received = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
|
||||
// Receives the second push synchronously.
|
||||
error_code ec2;
|
||||
@@ -124,9 +134,10 @@ BOOST_AUTO_TEST_CASE(push_received1)
|
||||
});
|
||||
|
||||
run(conn);
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(push_async_received);
|
||||
BOOST_TEST(exec_finished);
|
||||
BOOST_TEST(push_received);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(push_filtered_out)
|
||||
@@ -141,39 +152,30 @@ BOOST_AUTO_TEST_CASE(push_filtered_out)
|
||||
req.push("QUIT");
|
||||
|
||||
response<ignore_t, std::string, std::string> resp;
|
||||
conn->async_exec(req, resp, [conn](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
|
||||
bool exec_finished = false, push_received = false;
|
||||
|
||||
conn->async_exec(req, resp, [conn, &exec_finished](error_code ec, std::size_t) {
|
||||
exec_finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
});
|
||||
|
||||
conn->async_receive([&, conn](auto ec, auto) {
|
||||
BOOST_TEST(!ec);
|
||||
conn->async_receive([&, conn](error_code ec, std::size_t) {
|
||||
push_received = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel(operation::reconnection);
|
||||
});
|
||||
|
||||
run(conn);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(exec_finished);
|
||||
BOOST_TEST(push_received);
|
||||
|
||||
BOOST_CHECK_EQUAL(std::get<1>(resp).value(), "PONG");
|
||||
BOOST_CHECK_EQUAL(std::get<2>(resp).value(), "OK");
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
net::awaitable<void> push_consumer1(std::shared_ptr<connection> conn, bool& push_received)
|
||||
{
|
||||
{
|
||||
auto [ec, ev] = co_await conn->async_receive(as_tuple(net::use_awaitable));
|
||||
BOOST_TEST(!ec);
|
||||
}
|
||||
|
||||
{
|
||||
auto [ec, ev] = co_await conn->async_receive(as_tuple(net::use_awaitable));
|
||||
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
|
||||
}
|
||||
|
||||
push_received = true;
|
||||
}
|
||||
|
||||
struct response_error_tag { };
|
||||
response_error_tag error_tag_obj;
|
||||
|
||||
@@ -208,31 +210,43 @@ BOOST_AUTO_TEST_CASE(test_push_adapter)
|
||||
|
||||
conn->set_receive_response(error_tag_obj);
|
||||
|
||||
conn->async_receive([&, conn](auto ec, auto) {
|
||||
bool push_received = false, exec_finished = false, run_finished = false;
|
||||
|
||||
conn->async_receive([&, conn](error_code ec, std::size_t) {
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::experimental::error::channel_cancelled);
|
||||
conn->cancel(operation::reconnection);
|
||||
push_received = true;
|
||||
});
|
||||
|
||||
conn->async_exec(req, ignore, [](auto ec, auto) {
|
||||
conn->async_exec(req, ignore, [&exec_finished](error_code ec, std::size_t) {
|
||||
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
|
||||
exec_finished = true;
|
||||
});
|
||||
|
||||
auto cfg = make_test_config();
|
||||
conn->async_run(cfg, {}, [](auto ec) {
|
||||
conn->async_run(cfg, {}, [&run_finished](error_code ec) {
|
||||
BOOST_CHECK_EQUAL(ec, redis::error::incompatible_size);
|
||||
run_finished = true;
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(push_received);
|
||||
BOOST_TEST(exec_finished);
|
||||
BOOST_TEST(run_finished);
|
||||
|
||||
// TODO: Reset the ioc reconnect and send a quit to ensure
|
||||
// reconnection is possible after an error.
|
||||
}
|
||||
|
||||
net::awaitable<void> push_consumer3(std::shared_ptr<connection> conn)
|
||||
void launch_push_consumer(std::shared_ptr<connection> conn)
|
||||
{
|
||||
for (;;) {
|
||||
co_await conn->async_receive(net::use_awaitable);
|
||||
}
|
||||
conn->async_receive([conn](error_code ec, std::size_t) {
|
||||
if (ec) {
|
||||
BOOST_TEST(ec == net::experimental::error::channel_cancelled);
|
||||
return;
|
||||
}
|
||||
launch_push_consumer(conn);
|
||||
});
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(many_subscribers)
|
||||
@@ -256,60 +270,65 @@ BOOST_AUTO_TEST_CASE(many_subscribers)
|
||||
net::io_context ioc;
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
|
||||
auto c11 = [&](auto ec, auto...) {
|
||||
std::cout << "quit sent: " << ec.message() << std::endl;
|
||||
bool finished = false;
|
||||
|
||||
auto c11 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel(operation::reconnection);
|
||||
finished = true;
|
||||
};
|
||||
auto c10 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c10 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req3, ignore, c11);
|
||||
};
|
||||
auto c9 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c9 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c10);
|
||||
};
|
||||
auto c8 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c8 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c9);
|
||||
};
|
||||
auto c7 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c7 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c8);
|
||||
};
|
||||
auto c6 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c6 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c7);
|
||||
};
|
||||
auto c5 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c5 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c6);
|
||||
};
|
||||
auto c4 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c4 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c5);
|
||||
};
|
||||
auto c3 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c3 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c4);
|
||||
};
|
||||
auto c2 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c2 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c3);
|
||||
};
|
||||
auto c1 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c2);
|
||||
};
|
||||
auto c0 = [&](auto ec, auto...) {
|
||||
BOOST_TEST(!ec);
|
||||
auto c0 = [&](error_code ec, std::size_t) {
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req1, ignore, c1);
|
||||
};
|
||||
|
||||
conn->async_exec(req0, ignore, c0);
|
||||
launch_push_consumer(conn);
|
||||
|
||||
run(conn, make_test_config(), {});
|
||||
|
||||
net::co_spawn(ioc.get_executor(), push_consumer3(conn), net::detached);
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(finished);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/system/errc.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#define BOOST_TEST_MODULE conn_quit
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
@@ -43,20 +45,25 @@ BOOST_AUTO_TEST_CASE(test_async_run_exits)
|
||||
req3.get_config().cancel_if_not_connected = true;
|
||||
req3.push("PING");
|
||||
|
||||
auto c3 = [](auto ec, auto) {
|
||||
bool c1_called = false, c2_called = false, c3_called = false;
|
||||
|
||||
auto c3 = [&](error_code ec, std::size_t) {
|
||||
c3_called = true;
|
||||
std::clog << "c3: " << ec.message() << std::endl;
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::operation_aborted);
|
||||
BOOST_TEST(ec == net::error::operation_aborted);
|
||||
};
|
||||
|
||||
auto c2 = [&](auto ec, auto) {
|
||||
auto c2 = [&](error_code ec, std::size_t) {
|
||||
c2_called = true;
|
||||
std::clog << "c2: " << ec.message() << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req3, ignore, c3);
|
||||
};
|
||||
|
||||
auto c1 = [&](auto ec, auto) {
|
||||
auto c1 = [&](error_code ec, std::size_t) {
|
||||
c1_called = true;
|
||||
std::cout << "c1: " << ec.message() << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->async_exec(req2, ignore, c2);
|
||||
};
|
||||
|
||||
@@ -69,5 +76,9 @@ BOOST_AUTO_TEST_CASE(test_async_run_exits)
|
||||
cfg.reconnect_wait_interval = 0s;
|
||||
run(conn, cfg);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(c1_called);
|
||||
BOOST_TEST(c2_called);
|
||||
BOOST_TEST(c3_called);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#include <boost/redis/config.hpp>
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#define BOOST_TEST_MODULE conn_reconnect
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
@@ -26,41 +28,52 @@ using boost::redis::logger;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::connection;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
using namespace boost::asio::experimental::awaitable_operators;
|
||||
|
||||
namespace {
|
||||
|
||||
net::awaitable<void> test_reconnect_impl()
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
|
||||
request req;
|
||||
req.push("QUIT");
|
||||
request quit_req;
|
||||
quit_req.push("QUIT");
|
||||
|
||||
// cancel_on_connection_lost is required because async_run might detect the failure
|
||||
// after the 2nd async_exec is issued
|
||||
request regular_req;
|
||||
regular_req.push("GET", "mykey");
|
||||
regular_req.get_config().cancel_on_connection_lost = false;
|
||||
|
||||
auto conn = std::make_shared<connection>(ex);
|
||||
run(conn);
|
||||
auto cfg = make_test_config();
|
||||
cfg.reconnect_wait_interval = 100ms; // make the test run faster
|
||||
run(conn, std::move(cfg));
|
||||
|
||||
int i = 0;
|
||||
for (; i < 5; ++i) {
|
||||
error_code ec1, ec2;
|
||||
auto cfg = make_test_config();
|
||||
logger l;
|
||||
co_await conn->async_exec(req, ignore, net::redirect_error(net::use_awaitable, ec1));
|
||||
//BOOST_TEST(!ec);
|
||||
std::cout << "test_reconnect: " << i << " " << ec2.message() << " " << ec1.message()
|
||||
<< std::endl;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
BOOST_TEST_CONTEXT("i=" << i)
|
||||
{
|
||||
// Issue a quit request, which will cause the server to close the connection.
|
||||
// This request will fail
|
||||
error_code ec;
|
||||
co_await conn->async_exec(quit_req, ignore, net::redirect_error(ec));
|
||||
BOOST_TEST(ec == error_code());
|
||||
|
||||
// This should trigger reconnection, which will now succeed.
|
||||
// We should be able to execute requests successfully now.
|
||||
// TODO: this is currently unreliable - find our why and fix
|
||||
co_await conn->async_exec(regular_req, ignore, net::redirect_error(ec));
|
||||
// BOOST_TEST(ec == error_code());
|
||||
}
|
||||
}
|
||||
|
||||
conn->cancel();
|
||||
BOOST_CHECK_EQUAL(i, 5);
|
||||
co_return;
|
||||
}
|
||||
|
||||
// Test whether the client works after a reconnect.
|
||||
BOOST_AUTO_TEST_CASE(test_reconnect)
|
||||
{
|
||||
net::io_context ioc;
|
||||
net::co_spawn(ioc, test_reconnect_impl(), net::detached);
|
||||
ioc.run();
|
||||
run_coroutine_test(test_reconnect_impl(), 5 * test_timeout);
|
||||
}
|
||||
|
||||
auto async_test_reconnect_timeout() -> net::awaitable<void>
|
||||
@@ -104,10 +117,11 @@ auto async_test_reconnect_timeout() -> net::awaitable<void>
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_reconnect_and_idle)
|
||||
{
|
||||
net::io_context ioc;
|
||||
net::co_spawn(ioc, async_test_reconnect_timeout(), net::detached);
|
||||
ioc.run();
|
||||
run_coroutine_test(async_test_reconnect_timeout());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#else
|
||||
BOOST_AUTO_TEST_CASE(dummy) { BOOST_TEST(true); }
|
||||
BOOST_AUTO_TEST_CASE(dummy) { }
|
||||
#endif
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#include "boost/redis/ignore.hpp"
|
||||
#include "boost/system/detail/error_code.hpp"
|
||||
#define BOOST_TEST_MODULE conversions
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/ignore.hpp>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#define BOOST_TEST_MODULE conversions
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
@@ -20,6 +21,8 @@ using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::system::error_code;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ints)
|
||||
{
|
||||
// Setup
|
||||
@@ -47,13 +50,17 @@ BOOST_AUTO_TEST_CASE(ints)
|
||||
unsigned long long>
|
||||
resp;
|
||||
|
||||
conn->async_exec(req, resp, [conn](error_code ec, std::size_t) {
|
||||
BOOST_TEST(!ec);
|
||||
bool finished = false;
|
||||
|
||||
conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) {
|
||||
finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel();
|
||||
});
|
||||
|
||||
// Run the operations
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(finished);
|
||||
|
||||
// Check
|
||||
BOOST_TEST(std::get<1>(resp).value() == 42);
|
||||
@@ -84,13 +91,16 @@ BOOST_AUTO_TEST_CASE(bools)
|
||||
|
||||
response<ignore_t, ignore_t, bool, bool> resp;
|
||||
|
||||
conn->async_exec(req, resp, [conn](error_code ec, std::size_t) {
|
||||
BOOST_TEST(!ec);
|
||||
bool finished = false;
|
||||
|
||||
conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) {
|
||||
finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel();
|
||||
});
|
||||
|
||||
// Run the operations
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
// Check
|
||||
BOOST_TEST(std::get<2>(resp).value() == true);
|
||||
@@ -111,14 +121,20 @@ BOOST_AUTO_TEST_CASE(floating_points)
|
||||
|
||||
response<ignore_t, double> resp;
|
||||
|
||||
conn->async_exec(req, resp, [conn](error_code ec, std::size_t) {
|
||||
BOOST_TEST(!ec);
|
||||
bool finished = false;
|
||||
|
||||
conn->async_exec(req, resp, [conn, &finished](error_code ec, std::size_t) {
|
||||
finished = true;
|
||||
BOOST_TEST(ec == error_code());
|
||||
conn->cancel();
|
||||
});
|
||||
|
||||
// Run the operations
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
BOOST_TEST(finished);
|
||||
|
||||
// Check
|
||||
BOOST_TEST(std::get<1>(resp).value() == 4.12);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -7,18 +7,16 @@
|
||||
#include <boost/redis/connection.hpp>
|
||||
#include <boost/redis/logger.hpp>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
#define BOOST_TEST_MODULE conn_quit
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#define BOOST_TEST_MODULE issue_181
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace net = boost::asio;
|
||||
using boost::redis::request;
|
||||
@@ -32,6 +30,8 @@ using boost::redis::connection;
|
||||
using boost::system::error_code;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
BOOST_AUTO_TEST_CASE(issue_181)
|
||||
{
|
||||
using basic_connection = boost::redis::basic_connection<net::any_io_executor>;
|
||||
@@ -43,8 +43,12 @@ BOOST_AUTO_TEST_CASE(issue_181)
|
||||
net::steady_timer timer{ioc};
|
||||
timer.expires_after(std::chrono::seconds{1});
|
||||
|
||||
auto run_cont = [&](auto ec) {
|
||||
bool run_finished = false;
|
||||
|
||||
auto run_cont = [&](error_code ec) {
|
||||
std::cout << "async_run1: " << ec.message() << std::endl;
|
||||
BOOST_TEST(ec == net::error::operation_aborted);
|
||||
run_finished = true;
|
||||
};
|
||||
|
||||
auto cfg = make_test_config();
|
||||
@@ -54,8 +58,9 @@ BOOST_AUTO_TEST_CASE(issue_181)
|
||||
BOOST_TEST(!conn.run_is_canceled());
|
||||
|
||||
// Uses a timer to wait some time until run has been called.
|
||||
auto timer_cont = [&](auto ec) {
|
||||
auto timer_cont = [&](error_code ec) {
|
||||
std::cout << "timer_cont: " << ec.message() << std::endl;
|
||||
BOOST_TEST(ec == error_code());
|
||||
BOOST_TEST(!conn.run_is_canceled());
|
||||
conn.cancel(operation::run);
|
||||
BOOST_TEST(conn.run_is_canceled());
|
||||
@@ -63,5 +68,9 @@ BOOST_AUTO_TEST_CASE(issue_181)
|
||||
|
||||
timer.async_wait(timer_cont);
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(test_timeout);
|
||||
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -12,22 +12,21 @@
|
||||
#include <boost/asio/as_tuple.hpp>
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/consign.hpp>
|
||||
#include <boost/asio/detached.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/redirect_error.hpp>
|
||||
#include <boost/asio/use_awaitable.hpp>
|
||||
#define BOOST_TEST_MODULE conn - quit
|
||||
#include <boost/system/error_code.hpp>
|
||||
|
||||
#include <exception>
|
||||
#define BOOST_TEST_MODULE issue50
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
|
||||
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
|
||||
namespace net = boost::asio;
|
||||
using steady_timer = net::use_awaitable_t<>::as_default_on_t<net::steady_timer>;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
@@ -36,10 +35,10 @@ using boost::redis::config;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::connection;
|
||||
using boost::system::error_code;
|
||||
using boost::asio::use_awaitable;
|
||||
using boost::asio::redirect_error;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
// Push consumer
|
||||
auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
{
|
||||
@@ -50,7 +49,7 @@ auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
for (;;) {
|
||||
std::cout << "aaaa" << std::endl;
|
||||
error_code ec;
|
||||
co_await conn->async_receive(redirect_error(use_awaitable, ec));
|
||||
co_await conn->async_receive(net::redirect_error(ec));
|
||||
if (ec) {
|
||||
std::cout << "Error in async_receive" << std::endl;
|
||||
break;
|
||||
@@ -67,14 +66,14 @@ auto periodic_task(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
std::cout << "In the loop: " << i << std::endl;
|
||||
timer.expires_after(std::chrono::milliseconds(50));
|
||||
co_await timer.async_wait(net::use_awaitable);
|
||||
co_await timer.async_wait();
|
||||
|
||||
// Key is not set so it will cause an error since we are passing
|
||||
// an adapter that does not accept null, this will cause an error
|
||||
// that result in the connection being closed.
|
||||
request req;
|
||||
req.push("GET", "mykey");
|
||||
auto [ec, u] = co_await conn->async_exec(req, ignore, net::as_tuple(net::use_awaitable));
|
||||
auto [ec, u] = co_await conn->async_exec(req, ignore, net::as_tuple);
|
||||
if (ec) {
|
||||
std::cout << "(1)Error: " << ec << std::endl;
|
||||
} else {
|
||||
@@ -88,26 +87,41 @@ auto periodic_task(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
conn->cancel(operation::reconnection);
|
||||
}
|
||||
|
||||
auto co_main(config) -> net::awaitable<void>
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
auto conn = std::make_shared<connection>(ex);
|
||||
|
||||
net::co_spawn(ex, receiver(conn), net::detached);
|
||||
net::co_spawn(ex, periodic_task(conn), net::detached);
|
||||
auto cfg = make_test_config();
|
||||
conn->async_run(cfg, {}, net::consign(net::detached, conn));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(issue_50)
|
||||
{
|
||||
net::io_context ioc;
|
||||
net::co_spawn(ioc, co_main({}), net::detached);
|
||||
ioc.run();
|
||||
bool receiver_finished = false, periodic_finished = false, run_finished = false;
|
||||
|
||||
net::io_context ctx;
|
||||
auto conn = std::make_shared<connection>(ctx.get_executor());
|
||||
|
||||
// Launch the receiver
|
||||
net::co_spawn(ctx, receiver(conn), [&](std::exception_ptr exc) {
|
||||
if (exc)
|
||||
std::rethrow_exception(exc);
|
||||
receiver_finished = true;
|
||||
});
|
||||
|
||||
// Launch the period task
|
||||
net::co_spawn(ctx, periodic_task(conn), [&](std::exception_ptr exc) {
|
||||
if (exc)
|
||||
std::rethrow_exception(exc);
|
||||
periodic_finished = true;
|
||||
});
|
||||
|
||||
// Launch run
|
||||
conn->async_run(make_test_config(), {}, [&](error_code) {
|
||||
run_finished = true;
|
||||
});
|
||||
|
||||
ctx.run_for(2 * test_timeout);
|
||||
|
||||
BOOST_TEST(receiver_finished);
|
||||
BOOST_TEST(periodic_finished);
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
#else // defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(issue_50) { }
|
||||
} // namespace
|
||||
|
||||
#else
|
||||
BOOST_AUTO_TEST_CASE(dummy) { }
|
||||
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
*/
|
||||
|
||||
#include <boost/redis/connection.hpp>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
#define BOOST_TEST_MODULE run
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
|
||||
@@ -22,6 +22,8 @@ using redis::operation;
|
||||
using boost::system::error_code;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
bool is_host_not_found(error_code ec)
|
||||
{
|
||||
if (ec == net::error::netdb_errors::host_not_found)
|
||||
@@ -34,6 +36,7 @@ bool is_host_not_found(error_code ec)
|
||||
BOOST_AUTO_TEST_CASE(resolve_bad_host)
|
||||
{
|
||||
net::io_context ioc;
|
||||
connection conn{ioc};
|
||||
|
||||
auto cfg = make_test_config();
|
||||
cfg.addr.host = "Atibaia";
|
||||
@@ -43,17 +46,20 @@ BOOST_AUTO_TEST_CASE(resolve_bad_host)
|
||||
cfg.health_check_interval = 10h;
|
||||
cfg.reconnect_wait_interval = 0s;
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
conn->async_run(cfg, {}, [](auto ec) {
|
||||
BOOST_TEST(is_host_not_found(ec));
|
||||
bool run_finished = true;
|
||||
conn.async_run(cfg, {}, [&run_finished](error_code ec) {
|
||||
run_finished = true;
|
||||
BOOST_TEST(is_host_not_found(ec), "is_host_not_found(ec) is false, with ec = " << ec);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
ioc.run_for(4 * test_timeout);
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(resolve_with_timeout)
|
||||
{
|
||||
net::io_context ioc;
|
||||
connection conn{ioc};
|
||||
|
||||
auto cfg = make_test_config();
|
||||
cfg.addr.host = "occase.de";
|
||||
@@ -63,14 +69,20 @@ BOOST_AUTO_TEST_CASE(resolve_with_timeout)
|
||||
cfg.health_check_interval = 10h;
|
||||
cfg.reconnect_wait_interval = 0s;
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
run(conn, cfg);
|
||||
ioc.run();
|
||||
bool run_finished = true;
|
||||
conn.async_run(cfg, {}, [&run_finished](error_code ec) {
|
||||
run_finished = true;
|
||||
BOOST_TEST(ec != error_code());
|
||||
});
|
||||
|
||||
ioc.run_for(4 * test_timeout);
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(connect_bad_port)
|
||||
{
|
||||
net::io_context ioc;
|
||||
connection conn{ioc};
|
||||
|
||||
auto cfg = make_test_config();
|
||||
cfg.addr.host = "127.0.0.1";
|
||||
@@ -80,9 +92,14 @@ BOOST_AUTO_TEST_CASE(connect_bad_port)
|
||||
cfg.health_check_interval = 10h;
|
||||
cfg.reconnect_wait_interval = 0s;
|
||||
|
||||
auto conn = std::make_shared<connection>(ioc);
|
||||
run(conn, cfg, net::error::connection_refused);
|
||||
ioc.run();
|
||||
bool run_finished = true;
|
||||
conn.async_run(cfg, {}, [&run_finished](error_code ec) {
|
||||
run_finished = true;
|
||||
BOOST_TEST(ec != error_code());
|
||||
});
|
||||
|
||||
ioc.run_for(4 * test_timeout);
|
||||
BOOST_TEST(run_finished);
|
||||
}
|
||||
|
||||
// Hard to test.
|
||||
@@ -101,3 +118,5 @@ BOOST_AUTO_TEST_CASE(connect_bad_port)
|
||||
// run(conn, cfg, boost::redis::error::connect_timeout);
|
||||
// ioc.run();
|
||||
//}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user