Skip to content

Commit 2fc54bc

Browse files
authored
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
1 parent 0c8c6fc commit 2fc54bc

19 files changed

+642
-407
lines changed

include/boost/redis/impl/multiplexer.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ multiplexer::elem::elem(request const& req, pipeline_adapter_type adapter)
1616
, adapter_{}
1717
, remaining_responses_{req.get_expected_responses()}
1818
, status_{status::waiting}
19-
, ec_{{}}
19+
, ec_{}
2020
, read_size_{0}
2121
{
2222
adapter_ = [this, adapter](resp3::node_view const& nd, system::error_code& ec) {

test/CMakeLists.txt

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ add_library(boost_redis_tests_common STATIC common.cpp)
2020
target_compile_features(boost_redis_tests_common PRIVATE cxx_std_17)
2121
target_link_libraries(boost_redis_tests_common PRIVATE boost_redis_project_options)
2222

23-
macro(make_test TEST_NAME STANDARD)
23+
macro(make_test TEST_NAME)
2424
set(EXE_NAME "boost_redis_${TEST_NAME}")
2525
add_executable(${EXE_NAME} ${TEST_NAME}.cpp)
2626
target_link_libraries(${EXE_NAME} PRIVATE
@@ -29,31 +29,32 @@ macro(make_test TEST_NAME STANDARD)
2929
boost_redis_project_options
3030
Boost::unit_test_framework
3131
)
32-
target_compile_features(${EXE_NAME} PRIVATE cxx_std_${STANDARD})
3332
add_test(${EXE_NAME} ${EXE_NAME})
3433
endmacro()
3534

36-
make_test(test_conn_quit 17)
37-
# TODO: Configure a Redis server with TLS in the CI and reenable this test.
38-
#make_test(test_conn_tls 17)
39-
make_test(test_low_level 17)
40-
make_test(test_conn_exec_retry 17)
41-
make_test(test_conn_exec_error 17)
42-
make_test(test_request 17)
43-
make_test(test_run 17)
44-
make_test(test_low_level_sync_sans_io 17)
45-
make_test(test_conn_check_health 17)
35+
# Unit tests
36+
make_test(test_low_level)
37+
make_test(test_request)
38+
make_test(test_low_level_sync_sans_io)
39+
make_test(test_any_adapter)
4640

47-
make_test(test_conn_exec 20)
48-
make_test(test_conn_push 20)
49-
make_test(test_conn_reconnect 20)
50-
make_test(test_conn_exec_cancel 20)
51-
make_test(test_conn_exec_cancel2 20)
52-
make_test(test_conn_echo_stress 20)
53-
make_test(test_any_adapter 17)
54-
make_test(test_conversions 17)
55-
make_test(test_issue_50 20)
56-
make_test(test_issue_181 17)
41+
# Tests that require a real Redis server
42+
make_test(test_conn_quit)
43+
# TODO: Configure a Redis server with TLS in the CI and reenable this test.
44+
#make_test(test_conn_tls)
45+
make_test(test_conn_exec_retry)
46+
make_test(test_conn_exec_error)
47+
make_test(test_run)
48+
make_test(test_conn_check_health)
49+
make_test(test_conn_exec)
50+
make_test(test_conn_push)
51+
make_test(test_conn_reconnect)
52+
make_test(test_conn_exec_cancel)
53+
make_test(test_conn_exec_cancel2)
54+
make_test(test_conn_echo_stress)
55+
make_test(test_issue_50)
56+
make_test(test_issue_181)
57+
make_test(test_conversions)
5758

5859
# Coverage
5960
set(

test/Jamfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,9 @@ lib redis_test_common
4848
# B2 runs tests in parallel, and some tests rely on having exclusive
4949
# access to a Redis server, so we only run the ones that don't require a DB server.
5050
local tests =
51-
test_low_level_sync_sans_io
5251
test_low_level
5352
test_request
54-
test_run
53+
test_low_level_sync_sans_io
5554
test_any_adapter
5655
;
5756

test/common.cpp

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <cstdlib>
77
#include <iostream>
8+
#include <stdexcept>
89

910
namespace net = boost::asio;
1011

@@ -55,22 +56,17 @@ boost::redis::config make_test_config()
5556
}
5657

5758
#ifdef BOOST_ASIO_HAS_CO_AWAIT
58-
auto start(net::awaitable<void> op) -> int
59+
void run_coroutine_test(net::awaitable<void> op, std::chrono::steady_clock::duration timeout)
5960
{
60-
try {
61-
net::io_context ioc;
62-
net::co_spawn(ioc, std::move(op), [](std::exception_ptr p) {
63-
if (p)
64-
std::rethrow_exception(p);
65-
});
66-
ioc.run();
67-
68-
return 0;
69-
70-
} catch (std::exception const& e) {
71-
std::cerr << "start> " << e.what() << std::endl;
72-
}
73-
74-
return 1;
61+
net::io_context ioc;
62+
bool finished = false;
63+
net::co_spawn(ioc, std::move(op), [&finished](std::exception_ptr p) {
64+
if (p)
65+
std::rethrow_exception(p);
66+
finished = true;
67+
});
68+
ioc.run_for(timeout);
69+
if (!finished)
70+
throw std::runtime_error("Coroutine test did not finish");
7571
}
7672
#endif // BOOST_ASIO_HAS_CO_AWAIT

test/common.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@
88
#include <boost/asio/use_awaitable.hpp>
99
#include <boost/system/error_code.hpp>
1010

11+
#include <chrono>
1112
#include <memory>
1213

14+
// The timeout for tests involving communication to a real server.
15+
// Some tests use a longer timeout by multiplying this value by some
16+
// integral number.
17+
inline constexpr std::chrono::seconds test_timeout{30};
18+
1319
#ifdef BOOST_ASIO_HAS_CO_AWAIT
1420
inline auto redir(boost::system::error_code& ec)
1521
{
1622
return boost::asio::redirect_error(boost::asio::use_awaitable, ec);
1723
}
18-
auto start(boost::asio::awaitable<void> op) -> int;
24+
void run_coroutine_test(
25+
boost::asio::awaitable<void>,
26+
std::chrono::steady_clock::duration timeout = test_timeout);
1927
#endif // BOOST_ASIO_HAS_CO_AWAIT
2028

2129
boost::redis::config make_test_config();

test/test_conn_check_health.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <boost/redis/connection.hpp>
88
#include <boost/redis/response.hpp>
99

10-
#include <boost/system/errc.hpp>
10+
#include <cstddef>
1111
#define BOOST_TEST_MODULE check_health
1212
#include <boost/test/included/unit_test.hpp>
1313

@@ -25,9 +25,12 @@ using boost::redis::ignore;
2525
using boost::redis::operation;
2626
using boost::redis::generic_response;
2727
using boost::redis::consume_one;
28+
using namespace std::chrono_literals;
2829

2930
// TODO: Test cancel(health_check)
3031

32+
namespace {
33+
3134
struct push_callback {
3235
connection* conn1;
3336
connection* conn2;
@@ -82,10 +85,13 @@ BOOST_AUTO_TEST_CASE(check_health)
8285
auto cfg1 = make_test_config();
8386
cfg1.health_check_id = "conn1";
8487
cfg1.reconnect_wait_interval = std::chrono::seconds::zero();
85-
error_code res1;
86-
conn1.async_run(cfg1, {}, [&](auto ec) {
88+
89+
bool run1_finished = false, run2_finished = false, exec_finished = false;
90+
91+
conn1.async_run(cfg1, {}, [&](error_code ec) {
92+
run1_finished = true;
8793
std::cout << "async_run 1 completed: " << ec.message() << std::endl;
88-
res1 = ec;
94+
BOOST_TEST(ec != error_code());
8995
});
9096

9197
//--------------------------------
@@ -96,32 +102,36 @@ BOOST_AUTO_TEST_CASE(check_health)
96102

97103
auto cfg2 = make_test_config();
98104
cfg2.health_check_id = "conn2";
99-
error_code res2;
100-
conn2.async_run(cfg2, {}, [&](auto ec) {
105+
conn2.async_run(cfg2, {}, [&](error_code ec) {
106+
run2_finished = true;
101107
std::cout << "async_run 2 completed: " << ec.message() << std::endl;
102-
res2 = ec;
108+
BOOST_TEST(ec != error_code());
103109
});
104110

105111
request req2;
106112
req2.push("MONITOR");
107113
generic_response resp2;
108114
conn2.set_receive_response(resp2);
109115

110-
conn2.async_exec(req2, ignore, [](auto ec, auto) {
116+
conn2.async_exec(req2, ignore, [&exec_finished](error_code ec, std::size_t) {
117+
exec_finished = true;
111118
std::cout << "async_exec: " << std::endl;
112-
BOOST_TEST(!ec);
119+
BOOST_TEST(ec == error_code());
113120
});
114121

115122
//--------------------------------
116123

117124
push_callback{&conn1, &conn2, &resp2, &req1}(); // Starts reading pushes.
118125

119-
ioc.run();
126+
ioc.run_for(2 * test_timeout);
120127

121-
BOOST_TEST(!!res1);
122-
BOOST_TEST(!!res2);
128+
BOOST_TEST(run1_finished);
129+
BOOST_TEST(run2_finished);
130+
BOOST_TEST(exec_finished);
123131

124132
// Waits before exiting otherwise it might cause subsequent tests
125133
// to fail.
126134
std::this_thread::sleep_for(std::chrono::seconds{10});
127135
}
136+
137+
} // namespace

0 commit comments

Comments
 (0)