2
0
mirror of https://github.com/boostorg/redis.git synced 2026-01-19 04:42:09 +00:00

Adds example that does not user awaitable ops.

This commit is contained in:
Marcelo Zimbres
2022-12-30 15:54:19 +01:00
parent 9cf00d6a23
commit 73ad66eb93
10 changed files with 416 additions and 312 deletions

View File

@@ -82,6 +82,15 @@ if (MSVC)
target_compile_definitions(cpp20_intro PRIVATE _WIN32_WINNT=0x0601)
endif()
add_executable(cpp20_intro_awaitable_ops examples/cpp20_intro_awaitable_ops.cpp)
target_link_libraries(cpp20_intro_awaitable_ops common)
target_compile_features(cpp20_intro_awaitable_ops PUBLIC cxx_std_20)
add_test(cpp20_intro_awaitable_ops cpp20_intro_awaitable_ops)
if (MSVC)
target_compile_options(cpp20_intro_awaitable_ops PRIVATE /bigobj)
target_compile_definitions(cpp20_intro_awaitable_ops PRIVATE _WIN32_WINNT=0x0601)
endif()
add_executable(cpp17_intro examples/cpp17_intro.cpp)
target_compile_features(cpp17_intro PUBLIC cxx_std_17)
add_test(cpp17_intro cpp17_intro)
@@ -199,6 +208,14 @@ if (MSVC)
target_compile_definitions(test_conn_exec PRIVATE _WIN32_WINNT=0x0601)
endif()
add_executable(test_conn_exec_retry tests/conn_exec_retry.cpp)
target_compile_features(test_conn_exec_retry PUBLIC cxx_std_20)
add_test(test_conn_exec_retry test_conn_exec_retry)
if (MSVC)
target_compile_options(test_conn_exec_retry PRIVATE /bigobj)
target_compile_definitions(test_conn_exec_retry PRIVATE _WIN32_WINNT=0x0601)
endif()
add_executable(test_conn_push tests/conn_push.cpp)
target_compile_features(test_conn_push PUBLIC cxx_std_20)
add_test(test_conn_push test_conn_push)

View File

@@ -15,7 +15,7 @@ the cases will be concerned with only three library entities
For example, the coroutine below uses a short-lived connection to read Redis
[hashes](https://redis.io/docs/data-types/hashes/)
in a `std::map` (see cpp20_intro.cpp, cpp17_intro.cpp and containers.cpp)
in a `std::map`
```cpp
auto async_main() -> net::awaitable<void>
@@ -41,30 +41,32 @@ auto async_main() -> net::awaitable<void>
}
```
For different versions of this example using different styles see
* cpp20_intro.cpp: Does not use awaitable operators
* cpp20_intro_awaitable_ops.cpp: The version above.
* cpp17_intro.cpp: Requires C++17 only.
* cpp20_intro_tls.cpp: Communicates over TLS.
The execution of `connection::async_exec` above is composed with
`connection::async_run` with the aid of the Asio awaitable `operator ||`
that ensures that one operation is cancelled as soon as the other
completes, these functions play the following roles
* `connection::async_exec`: Execute commands by queuing the request
for writing and wait for the response sent back by Redis. It can be
called from multiple places in your code concurrently.
for writing. It will wait for the response sent back by Redis and
can be called from multiple places in your code concurrently.
* `connection::async_run`: Coordinate low-level read and write
operations. More specifically, it will hand IO control to
`async_exec` when a response arrives, to
`aedis::connection::async_receive` when a server-push is received
`async_receive` when a server-push is received
and will trigger writes of pending requests when a reconnection
occurs.
The role played by `async_run` can be better understood in the context
of long-lived connections, which we will cover in the next section.
Before that however, the reader might want to skim over the examples
Before that however, the reader might want to skim over some further examples
* cpp17_intro.cpp: The Aedis hello-world program. Sends one command and quits the connection.
* cpp17_intro_sync.cpp: Shows how to use the connection class synchronously.
* cpp17_low_level_sync.cpp: Sends a ping synchronously using the low-level API.
* cpp20_intro.cpp: Like cpp17_intro.cpp but uses awaitable operators.
* cpp20_intro_tls.cpp: Same as intro.cpp but over TLS.
* cpp20_containers.cpp: Shows how to send and receive STL containers and how to use transactions.
* cpp20_serialization.cpp: Shows how to serialize types using Boost.Json.
* cpp20_resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels.
@@ -72,6 +74,7 @@ Before that however, the reader might want to skim over the examples
* cpp20_echo_server.cpp: A simple TCP echo server.
* cpp20_chat_room.cpp: A command line chat built on Redis pubsub.
* cpp20_low_level_async.cpp: Sends a ping asynchronously using the low-level API.
* cpp17_low_level_sync.cpp: Sends a ping synchronously using the low-level API.
To avoid repetition code that is common to some examples has been
grouped in common.hpp. The main function used in some async examples
@@ -159,7 +162,7 @@ auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
```
The definition of `receiver` and `healthy_checker` above can be found
in subscriber.cpp. Adding a loop around `async_run` produces a simple
in cpp20_subscriber.cpp. Adding a loop around `async_run` produces a simple
way to support reconnection _while there are pending operations on the connection_,
for example, to reconnect to the same address
@@ -187,7 +190,7 @@ auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
}
```
For failover with sentinels see `resolve_with_sentinel.cpp`. At
For failover with sentinels see `cpp20_resolve_with_sentinel.cpp`. At
this point the reasons for why `async_run` was introduced in Aedis
might have become apparent to the reader
@@ -225,7 +228,7 @@ co_await (conn.async_exec(...) || time.async_wait(...))
* The cancellation will be ignored if the request has already
been written to the socket.
* NOTE: It is usually a better idea to have a healthy checker than adding
per request timeout, see subscriber.cpp for an example.
per request timeout, see cpp20_subscriber.cpp for an example.
```cpp
co_await (conn.async_run(...) || time.async_wait(...))
@@ -302,7 +305,7 @@ std::map<std::string, mystruct> map {...};
req.push_range("HSET", "key", map);
```
Example serialization.cpp shows how store json strings in Redis.
Example cpp20_serialization.cpp shows how store json strings in Redis.
<a name="responses"></a>
@@ -1006,7 +1009,7 @@ Acknowledgement to people that helped shape Aedis
* `connection::async_receive_event` is now being used to communicate
internal events to the user, such as resolve, connect, push etc. For
examples see subscriber.cpp and `connection::event`.
examples see cpp20_subscriber.cpp and `connection::event`.
* The `aedis` directory has been moved to `include` to look more
similar to Boost libraries. Users should now replace `-I/aedis-path`

View File

@@ -45,7 +45,7 @@ auto main() -> int
auto on_run = [](auto ec)
{
if (ec)
return log(ec, "on_exec: ");
return log(ec, "on_run: ");
};
// async_exec callback.

View File

@@ -6,15 +6,19 @@
#include <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <aedis.hpp>
#include "common/common.hpp"
namespace net = boost::asio;
namespace resp3 = aedis::resp3;
using namespace net::experimental::awaitable_operators;
using aedis::adapt;
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
{
co_await connect(conn, "127.0.0.1", "6379");
co_await conn->async_run();
}
// Called from the main function (see main.cpp)
auto async_main() -> net::awaitable<void>
{
@@ -25,9 +29,10 @@ auto async_main() -> net::awaitable<void>
std::tuple<aedis::ignore, std::string, aedis::ignore> resp;
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
co_await connect(conn, "127.0.0.1", "6379");
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
auto ex = co_await net::this_coro::executor;
auto conn = std::make_shared<connection>(ex);
net::co_spawn(ex, run(conn), net::detached);
co_await conn->async_exec(req, adapt(resp));
std::cout << "PING: " << std::get<1>(resp) << std::endl;
}

View File

@@ -0,0 +1,35 @@
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE.txt)
*/
#include <boost/asio.hpp>
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <aedis.hpp>
#include "common/common.hpp"
namespace net = boost::asio;
namespace resp3 = aedis::resp3;
using namespace net::experimental::awaitable_operators;
using aedis::adapt;
// Called from the main function (see main.cpp)
auto async_main() -> net::awaitable<void>
{
resp3::request req;
req.push("HELLO", 3);
req.push("PING", "Hello world");
req.push("QUIT");
std::tuple<aedis::ignore, std::string, aedis::ignore> resp;
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
co_await connect(conn, "127.0.0.1", "6379");
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
std::cout << "PING: " << std::get<1>(resp) << std::endl;
}
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)

View File

@@ -9,7 +9,7 @@
namespace aedis::resp3::detail {
auto has_push_response(std::string_view cmd) -> bool
auto has_response(std::string_view cmd) -> bool
{
if (cmd == "SUBSCRIBE") return true;
if (cmd == "PSUBSCRIBE") return true;

View File

@@ -62,7 +62,7 @@ void to_bulk(Request& to, T n)
namespace detail {
auto has_push_response(std::string_view cmd) -> bool;
auto has_response(std::string_view cmd) -> bool;
template <class T>
struct add_bulk_impl {
@@ -386,7 +386,7 @@ public:
private:
void check_cmd(std::string_view cmd)
{
if (!detail::has_push_response(cmd))
if (!detail::has_response(cmd))
++commands_;
if (cmd == "HELLO")

View File

@@ -122,123 +122,3 @@ BOOST_AUTO_TEST_CASE(cancel_request_if_not_connected)
ioc.run();
}
// TODO: This test is broken.
BOOST_AUTO_TEST_CASE(request_retry_false)
{
resp3::request req0;
req0.get_config().coalesce = false;
req0.get_config().cancel_on_connection_lost = true;
req0.push("HELLO", 3);
resp3::request req1;
req1.get_config().coalesce = true;
req1.get_config().cancel_on_connection_lost = true;
req1.push("BLPOP", "any", 0);
resp3::request req2;
req2.get_config().coalesce = true;
req2.get_config().cancel_on_connection_lost = false;
req2.get_config().cancel_if_unresponded = true;
req2.push("PING");
net::io_context ioc;
connection conn{ioc};
net::steady_timer st{ioc};
st.expires_after(std::chrono::seconds{1});
st.async_wait([&](auto){
// Cancels the request before receiving the response. This
// should cause the second request to complete with error
// although it has cancel_on_connection_lost = false.
conn.cancel(aedis::operation::run);
});
auto const endpoints = resolve();
net::connect(conn.next_layer(), endpoints);
conn.async_exec(req0, adapt(), [](auto ec, auto){
BOOST_TEST(!ec);
});
conn.async_exec(req1, adapt(), [](auto ec, auto){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
conn.async_exec(req2, adapt(), [](auto ec, auto){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
conn.async_run([](auto ec){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
ioc.run();
}
BOOST_AUTO_TEST_CASE(request_retry_true)
{
resp3::request req0;
req0.get_config().coalesce = false;
req0.get_config().cancel_on_connection_lost = true;
req0.push("HELLO", 3);
resp3::request req1;
req1.get_config().coalesce = true;
req1.get_config().cancel_on_connection_lost = true;
req1.push("BLPOP", "any", 0);
resp3::request req2;
req2.get_config().coalesce = true;
req2.get_config().cancel_on_connection_lost = false;
req2.get_config().cancel_if_unresponded = false;
req2.push("PING");
resp3::request req3;
req3.get_config().coalesce = true;
req3.get_config().cancel_on_connection_lost = true;
req3.get_config().cancel_if_unresponded = true;
req3.push("QUIT");
net::io_context ioc;
connection conn{ioc};
net::steady_timer st{ioc};
st.expires_after(std::chrono::seconds{1});
st.async_wait([&](auto){
// Cancels the request before receiving the response. This
// should cause the second request to complete with error
// although it has cancel_on_connection_lost = false.
conn.cancel(aedis::operation::run);
});
auto const endpoints = resolve();
net::connect(conn.next_layer(), endpoints);
conn.async_exec(req0, adapt(), [](auto ec, auto){
BOOST_TEST(!ec);
});
conn.async_exec(req1, adapt(), [](auto ec, auto){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
conn.async_exec(req2, adapt(), [&](auto ec, auto){
BOOST_TEST(!ec);
conn.async_exec(req3, adapt(), [&](auto ec, auto){
BOOST_TEST(!ec);
});
});
conn.async_run([&](auto ec){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
conn.reset_stream();
net::connect(conn.next_layer(), endpoints);
conn.async_run([&](auto ec){
std::cout << ec.message() << std::endl;
BOOST_TEST(!ec);
});
});
ioc.run();
}

148
tests/conn_exec_retry.cpp Normal file
View File

@@ -0,0 +1,148 @@
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE.txt)
*/
#include <iostream>
#include <boost/asio.hpp>
#include <boost/system/errc.hpp>
#define BOOST_TEST_MODULE low level
#include <boost/test/included/unit_test.hpp>
#include <aedis.hpp>
#include <aedis/src.hpp>
#include "common.hpp"
namespace net = boost::asio;
namespace resp3 = aedis::resp3;
using error_code = boost::system::error_code;
using connection = aedis::connection;
using aedis::adapt;
BOOST_AUTO_TEST_CASE(request_retry_false)
{
resp3::request req0;
req0.get_config().coalesce = false;
req0.get_config().cancel_on_connection_lost = true;
req0.push("HELLO", 3);
resp3::request req1;
req1.get_config().coalesce = true;
req1.get_config().cancel_on_connection_lost = true;
req1.push("BLPOP", "any", 0);
resp3::request req2;
req2.get_config().coalesce = true;
req2.get_config().cancel_on_connection_lost = false;
req2.get_config().cancel_if_unresponded = true;
req2.push("PING");
net::io_context ioc;
connection conn{ioc};
net::steady_timer st{ioc};
st.expires_after(std::chrono::seconds{1});
st.async_wait([&](auto){
// 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 is has already been written so
// cancel_on_connection_lost does not apply.
conn.cancel(aedis::operation::run);
});
auto const endpoints = resolve();
net::connect(conn.next_layer(), endpoints);
conn.async_exec(req0, adapt(), [](auto ec, auto){
BOOST_TEST(!ec);
});
conn.async_exec(req1, adapt(), [](auto ec, auto){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
conn.async_exec(req2, adapt(), [](auto ec, auto){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
conn.async_run([](auto ec){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
ioc.run();
}
BOOST_AUTO_TEST_CASE(request_retry_true)
{
resp3::request req0;
req0.get_config().coalesce = false;
req0.get_config().cancel_on_connection_lost = true;
req0.push("HELLO", 3);
resp3::request req1;
req1.get_config().coalesce = true;
req1.get_config().cancel_on_connection_lost = true;
req1.push("BLPOP", "any", 0);
resp3::request req2;
req2.get_config().coalesce = true;
req2.get_config().cancel_on_connection_lost = false;
req2.get_config().cancel_if_unresponded = false;
req2.push("PING");
resp3::request req3;
req3.get_config().coalesce = true;
req3.get_config().cancel_on_connection_lost = true;
req3.get_config().cancel_if_unresponded = true;
req3.push("QUIT");
net::io_context ioc;
connection conn{ioc};
net::steady_timer st{ioc};
st.expires_after(std::chrono::seconds{1});
st.async_wait([&](auto){
// Cancels the request before receiving the response. This
// should cause the thrid request to not complete with error
// since it has cancel_if_unresponded = true and cancellation commes
// after it was written.
conn.cancel(aedis::operation::run);
});
auto const endpoints = resolve();
net::connect(conn.next_layer(), endpoints);
conn.async_exec(req0, adapt(), [](auto ec, auto){
BOOST_TEST(!ec);
});
conn.async_exec(req1, adapt(), [](auto ec, auto){
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
});
conn.async_exec(req2, adapt(), [&](auto ec, auto){
BOOST_TEST(!ec);
conn.async_exec(req3, adapt(), [&](auto ec, auto){
BOOST_TEST(!ec);
});
});
conn.async_run([&](auto ec){
// The first cacellation.
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
conn.reset_stream();
// Reconnects and runs again to test req3.
net::connect(conn.next_layer(), endpoints);
conn.async_run([&](auto ec){
std::cout << ec.message() << std::endl;
BOOST_TEST(!ec);
});
});
ioc.run();
}

View File

@@ -295,173 +295,189 @@ std::vector<node_type> const attr_e1a
std::vector<node_type> const attr_e1b
{ {resp3::type::attribute, 0UL, 0UL, {}} };
#define S01 "#11\r\n"
#define S02 "#f\r\n"
#define S03 "#t\r\n"
#define S04 "$?\r\n;0\r\n"
#define S05 "%11\r\n"
#define S06 "$?\r\n;4\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"
#define S07 "$?\r\n;b\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"
#define S08 "*1\r\n:11\r\n"
#define S09 ":-3\r\n"
#define S10 ":11\r\n"
#define S11 ":3\r\n"
#define S12 "_\r\n"
#define S13 ">4\r\n+pubsub\r\n+message\r\n+some-channel\r\n+some message\r\n"
#define S14 ">0\r\n"
#define S15 "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"
#define S16 "%4\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n"
#define S17 "*1\r\n" S16
#define S18 "|1\r\n+key-popularity\r\n%2\r\n$1\r\na\r\n,0.1923\r\n$1\r\nb\r\n,0.0012\r\n"
#define S19 "|0\r\n"
#define S20 "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"
#define S21 "*1\r\n*1\r\n$2\r\nab\r\n"
#define S22 "*1\r\n*1\r\n*1\r\n*1\r\n*1\r\n*1\r\na\r\n"
#define S23 "*0\r\n"
#define S24 "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"
#define S25 "~6\r\n+orange\r\n+apple\r\n+one\r\n+two\r\n+three\r\n+orange\r\n"
#define S26 "*1\r\n" S25
#define S27 "~0\r\n"
#define S28 "-Error\r\n"
#define S29 "-\r\n"
#define S30 "%0\r\n"
#define S31 ",1.23\r\n"
#define S32 ",inf\r\n"
#define S33 ",-inf\r\n"
#define S34 ",1.23\r\n"
#define S35 ",er\r\n"
#define S36 "!21\r\nSYNTAX invalid syntax\r\n"
#define S37 "!0\r\n\r\n"
#define S38 "!3\r\nfoo\r\n"
#define S39 "=15\r\ntxt:Some string\r\n"
#define S40 "=0\r\n\r\n"
#define S41 "(3492890328409238509324850943850943825024385\r\n"
#define S42 "(\r\n"
#define S43 "+OK\r\n"
#define S44 "+\r\n"
#define S45 "s11\r\n"
#define S46 ":adf\r\n"
#define S47 "%rt\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n"
#define S48 "$?\r\n;d\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"
#define S49 "$l\r\nhh\r\n"
#define S50 ":\r\n"
#define S51 "#\r\n"
#define S52 ",\r\n"
#define S53 "$2\r\nhh\r\n"
#define S54 "$26\r\nhhaa\aaaa\raaaaa\r\naaaaaaaaaa\r\n"
#define S55 "$0\r\n\r\n"
#define S01a "#11\r\n"
#define S01b "#f\r\n"
#define S01c "#t\r\n"
#define S01d "#\r\n"
#define S02a "$?\r\n;0\r\n"
#define S02b "$?\r\n;4\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"
#define S02c "$?\r\n;b\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"
#define S02d "$?\r\n;d\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"
#define S03a "%11\r\n"
#define S03b "%4\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n$4\r\nkey3\r\n$6\r\nvalue3\r\n"
#define S03c "%0\r\n"
#define S03d "%rt\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n"
#define S04a "*1\r\n:11\r\n"
#define S04b "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"
#define S04c "*1\r\n" S03b
#define S04d "*1\r\n" S09a
#define S04e "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"
#define S04f "*1\r\n*1\r\n$2\r\nab\r\n"
#define S04g "*1\r\n*1\r\n*1\r\n*1\r\n*1\r\n*1\r\na\r\n"
#define S04h "*0\r\n"
#define S04i "*3\r\n$2\r\n11\r\n$2\r\n22\r\n$1\r\n3\r\n"
#define S05a ":-3\r\n"
#define S05b ":11\r\n"
#define s05c ":3\r\n"
#define S05d ":adf\r\n"
#define S05e ":\r\n"
#define S06a "_\r\n"
#define S07a ">4\r\n+pubsub\r\n+message\r\n+some-channel\r\n+some message\r\n"
#define S07b ">0\r\n"
#define S08a "|1\r\n+key-popularity\r\n%2\r\n$1\r\na\r\n,0.1923\r\n$1\r\nb\r\n,0.0012\r\n"
#define S08b "|0\r\n"
#define S09a "~6\r\n+orange\r\n+apple\r\n+one\r\n+two\r\n+three\r\n+orange\r\n"
#define S09b "~0\r\n"
#define S10a "-Error\r\n"
#define S10b "-\r\n"
#define S11a ",1.23\r\n"
#define S11b ",inf\r\n"
#define S11c ",-inf\r\n"
#define S11d ",1.23\r\n"
#define S11e ",er\r\n"
#define S11f ",\r\n"
#define S12a "!21\r\nSYNTAX invalid syntax\r\n"
#define S12b "!0\r\n\r\n"
#define S12c "!3\r\nfoo\r\n"
#define S13a "=15\r\ntxt:Some string\r\n"
#define S13b "=0\r\n\r\n"
#define S14a "(3492890328409238509324850943850943825024385\r\n"
#define S14b "(\r\n"
#define S15a "+OK\r\n"
#define S15b "+\r\n"
#define S16a "s11\r\n"
#define S17a "$l\r\nhh\r\n"
#define S17b "$2\r\nhh\r\n"
#define S18c "$26\r\nhhaa\aaaa\raaaaa\r\naaaaaaaaaa\r\n"
#define S18d "$0\r\n\r\n"
#define NUMBER_TEST_CONDITIONS(test) \
test(ex, make_expected(S01, std::optional<bool>{}, aedis::error::unexpected_bool_value)); \
test(ex, make_expected(S02, bool{false})); \
test(ex, make_expected(S02, node_type{resp3::type::boolean, 1UL, 0UL, {"f"}})); \
test(ex, make_expected(S03, bool{true})); \
test(ex, make_expected(S03, node_type{resp3::type::boolean, 1UL, 0UL, {"t"}})); \
test(ex, make_expected(S03, op_bool_ok)); \
test(ex, make_expected(S03, std::map<int, int>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S03, std::set<int>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S03, std::unordered_map<int, int>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S03, std::unordered_set<int>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S04, streamed_string_e2)); \
test(ex, make_expected(S05, int{}, aedis::error::expects_resp3_simple_type));\
test(ex, make_expected(S05, std::optional<int>{}, aedis::error::expects_resp3_simple_type));; \
test(ex, make_expected(S06, int{}, aedis::error::not_a_number)); \
test(ex, make_expected(S06, std::string{"Hello word"})); \
test(ex, make_expected(S06, streamed_string_e1)); \
test(ex, make_expected(S07, std::string{}, aedis::error::not_a_number)); \
test(ex, make_expected(S08, std::tuple<int>{11})); \
test(ex, make_expected(S09, node_type{resp3::type::number, 1UL, 0UL, {"-3"}})); \
test(ex, make_expected(S10, int{11})); \
test(ex, make_expected(S10, op_int_ok)); \
test(ex, make_expected(S10, std::list<std::string>{}, aedis::error::expects_resp3_aggregate)); \
test(ex, make_expected(S10, std::map<std::string, std::string>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S10, std::set<std::string>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S10, std::unordered_map<std::string, std::string>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S10, std::unordered_set<std::string>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S11, array_type2{}, aedis::error::expects_resp3_aggregate));\
test(ex, make_expected(S11, node_type{resp3::type::number, 1UL, 0UL, {"3"}})); \
test(ex, make_expected(S12, array_type{}, aedis::error::resp3_null));\
test(ex, make_expected(S12, int{0}, aedis::error::resp3_null)); \
test(ex, make_expected(S12, map_type{}, aedis::error::resp3_null));\
test(ex, make_expected(S12, op_type_01{}));\
test(ex, make_expected(S12, op_type_02{}));\
test(ex, make_expected(S12, op_type_03{}));\
test(ex, make_expected(S12, op_type_04{}));\
test(ex, make_expected(S12, op_type_05{}));\
test(ex, make_expected(S12, op_type_06{}));\
test(ex, make_expected(S12, op_type_07{}));\
test(ex, make_expected(S12, op_type_08{}));\
test(ex, make_expected(S12, op_type_09{}));\
test(ex, make_expected(S12, std::list<int>{}, aedis::error::resp3_null));\
test(ex, make_expected(S12, std::vector<int>{}, aedis::error::resp3_null));\
test(ex, make_expected(S13, push_e1a)); \
test(ex, make_expected(S14, push_e1b)); \
test(ex, make_expected(S15, map_type{}, aedis::error::expects_resp3_map));\
test(ex, make_expected(S16, map_e1f));\
test(ex, make_expected(S16, map_e1g));\
test(ex, make_expected(S16, map_e1k));\
test(ex, make_expected(S16, map_e1l));\
test(ex, make_expected(S16, map_expected_1a));\
test(ex, make_expected(S16, map_expected_1b));\
test(ex, make_expected(S16, map_expected_1c));\
test(ex, make_expected(S16, map_expected_1d));\
test(ex, make_expected(S16, map_expected_1e));\
test(ex, make_expected(S17, std::tuple<op_map_type>{map_expected_1d}));\
test(ex, make_expected(S18, attr_e1a)); \
test(ex, make_expected(S19, attr_e1b)); \
test(ex, make_expected(S20, array_e1a));\
test(ex, make_expected(S20, array_e1b));\
test(ex, make_expected(S20, array_e1c));\
test(ex, make_expected(S20, array_e1f));\
test(ex, make_expected(S20, array_e1g));\
test(ex, make_expected(S20, array_e1h));\
test(ex, make_expected(S20, array_type2{}, aedis::error::incompatible_size));\
test(ex, make_expected(S20, tuple_int_2{}, aedis::error::incompatible_size));\
test(ex, make_expected(S21, array_type2{}, aedis::error::nested_aggregate_not_supported));\
test(ex, make_expected(S22, vec_node_type{}, aedis::error::exceeeds_max_nested_depth));\
test(ex, make_expected(S23, array_e1d));\
test(ex, make_expected(S23, array_e1e));\
test(ex, make_expected(S24, set_type{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S25, set_e1c)); \
test(ex, make_expected(S25, set_e1d)); \
test(ex, make_expected(S25, set_e1f)); \
test(ex, make_expected(S25, set_e1g)); \
test(ex, make_expected(S25, set_expected1a)); \
test(ex, make_expected(S25, set_expected_1e)); \
test(ex, make_expected(S25, set_type{"apple", "one", "orange", "three", "two"})); \
test(ex, make_expected(S26, std::tuple<uset_type>{set_e1c})); \
test(ex, make_expected(S27, std::vector<node_type>{ {resp3::type::set, 0UL, 0UL, {}} })); \
test(ex, make_expected(S28, aedis::ignore{}, aedis::error::resp3_simple_error)); \
test(ex, make_expected(S28, node_type{resp3::type::simple_error, 1UL, 0UL, {"Error"}}, aedis::error::resp3_simple_error)); \
test(ex, make_expected(S29, node_type{resp3::type::simple_error, 1UL, 0UL, {""}}, aedis::error::resp3_simple_error)); \
test(ex, make_expected(S30, map_type{}));\
test(ex, make_expected(S31, node_type{resp3::type::doublean, 1UL, 0UL, {"1.23"}}));\
test(ex, make_expected(S32, node_type{resp3::type::doublean, 1UL, 0UL, {"inf"}}));\
test(ex, make_expected(S33, node_type{resp3::type::doublean, 1UL, 0UL, {"-inf"}}));\
test(ex, make_expected(S34, double{1.23}));\
test(ex, make_expected(S35, double{0}, aedis::error::not_a_double));\
test(ex, make_expected(S36, node_type{resp3::type::blob_error, 1UL, 0UL, {"SYNTAX invalid syntax"}}, aedis::error::resp3_blob_error));\
test(ex, make_expected(S37, node_type{resp3::type::blob_error, 1UL, 0UL, {}}, aedis::error::resp3_blob_error));\
test(ex, make_expected(S38, aedis::ignore{}, aedis::error::resp3_blob_error));\
test(ex, make_expected(S39, node_type{resp3::type::verbatim_string, 1UL, 0UL, {"txt:Some string"}}));\
test(ex, make_expected(S40, node_type{resp3::type::verbatim_string, 1UL, 0UL, {}}));\
test(ex, make_expected(S41, node_type{resp3::type::big_number, 1UL, 0UL, {"3492890328409238509324850943850943825024385"}}));\
test(ex, make_expected(S42, int{}, aedis::error::empty_field));\
test(ex, make_expected(S43, std::optional<std::string>{"OK"}));\
test(ex, make_expected(S43, std::string{"OK"}));\
test(ex, make_expected(S44, std::optional<std::string>{""}));\
test(ex, make_expected(S44, std::string{""}));\
test(ex, make_expected(S45, int{}, aedis::error::invalid_data_type));\
test(ex, make_expected(S46, int{11}, aedis::error::not_a_number));\
test(ex, make_expected(S47, map_type{}, aedis::error::not_a_number));\
test(ex, make_expected(S48, std::string{}, aedis::error::not_a_number));\
test(ex, make_expected(S49, std::string{}, aedis::error::not_a_number));\
test(ex, make_expected(S50, int{}, aedis::error::empty_field));\
test(ex, make_expected(S51, std::optional<bool>{}, aedis::error::empty_field));\
test(ex, make_expected(S52, std::string{}, aedis::error::empty_field));\
test(ex, make_expected(S53, node_type{resp3::type::blob_string, 1UL, 0UL, {"hh"}}));\
test(ex, make_expected(S54, node_type{resp3::type::blob_string, 1UL, 0UL, {"hhaa\aaaa\raaaaa\r\naaaaaaaaaa"}}));\
test(ex, make_expected(S55, node_type{resp3::type::blob_string, 1UL, 0UL, {}}));\
test(ex, make_expected(S01a, std::optional<bool>{}, aedis::error::unexpected_bool_value)); \
test(ex, make_expected(S01b, bool{false})); \
test(ex, make_expected(S01b, node_type{resp3::type::boolean, 1UL, 0UL, {"f"}})); \
test(ex, make_expected(S01c, bool{true})); \
test(ex, make_expected(S01c, node_type{resp3::type::boolean, 1UL, 0UL, {"t"}})); \
test(ex, make_expected(S01c, op_bool_ok)); \
test(ex, make_expected(S01c, std::map<int, int>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S01c, std::set<int>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S01c, std::unordered_map<int, int>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S01c, std::unordered_set<int>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S02a, streamed_string_e2)); \
test(ex, make_expected(S03a, int{}, aedis::error::expects_resp3_simple_type));\
test(ex, make_expected(S03a, std::optional<int>{}, aedis::error::expects_resp3_simple_type));; \
test(ex, make_expected(S02b, int{}, aedis::error::not_a_number)); \
test(ex, make_expected(S02b, std::string{"Hello word"})); \
test(ex, make_expected(S02b, streamed_string_e1)); \
test(ex, make_expected(S02c, std::string{}, aedis::error::not_a_number)); \
test(ex, make_expected(S04a, std::tuple<int>{11})); \
test(ex, make_expected(S05a, node_type{resp3::type::number, 1UL, 0UL, {"-3"}})); \
test(ex, make_expected(S05b, int{11})); \
test(ex, make_expected(S05b, op_int_ok)); \
test(ex, make_expected(S05b, std::list<std::string>{}, aedis::error::expects_resp3_aggregate)); \
test(ex, make_expected(S05b, std::map<std::string, std::string>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S05b, std::set<std::string>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S05b, std::unordered_map<std::string, std::string>{}, aedis::error::expects_resp3_map)); \
test(ex, make_expected(S05b, std::unordered_set<std::string>{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(s05c, array_type2{}, aedis::error::expects_resp3_aggregate));\
test(ex, make_expected(s05c, node_type{resp3::type::number, 1UL, 0UL, {"3"}})); \
test(ex, make_expected(S06a, array_type{}, aedis::error::resp3_null));\
test(ex, make_expected(S06a, int{0}, aedis::error::resp3_null)); \
test(ex, make_expected(S06a, map_type{}, aedis::error::resp3_null));\
test(ex, make_expected(S06a, op_type_01{}));\
test(ex, make_expected(S06a, op_type_02{}));\
test(ex, make_expected(S06a, op_type_03{}));\
test(ex, make_expected(S06a, op_type_04{}));\
test(ex, make_expected(S06a, op_type_05{}));\
test(ex, make_expected(S06a, op_type_06{}));\
test(ex, make_expected(S06a, op_type_07{}));\
test(ex, make_expected(S06a, op_type_08{}));\
test(ex, make_expected(S06a, op_type_09{}));\
test(ex, make_expected(S06a, std::list<int>{}, aedis::error::resp3_null));\
test(ex, make_expected(S06a, std::vector<int>{}, aedis::error::resp3_null));\
test(ex, make_expected(S07a, push_e1a)); \
test(ex, make_expected(S07b, push_e1b)); \
test(ex, make_expected(S04b, map_type{}, aedis::error::expects_resp3_map));\
test(ex, make_expected(S03b, map_e1f));\
test(ex, make_expected(S03b, map_e1g));\
test(ex, make_expected(S03b, map_e1k));\
test(ex, make_expected(S03b, map_e1l));\
test(ex, make_expected(S03b, map_expected_1a));\
test(ex, make_expected(S03b, map_expected_1b));\
test(ex, make_expected(S03b, map_expected_1c));\
test(ex, make_expected(S03b, map_expected_1d));\
test(ex, make_expected(S03b, map_expected_1e));\
test(ex, make_expected(S04c, std::tuple<op_map_type>{map_expected_1d}));\
test(ex, make_expected(S08a, attr_e1a)); \
test(ex, make_expected(S08b, attr_e1b)); \
test(ex, make_expected(S04e, array_e1a));\
test(ex, make_expected(S04e, array_e1b));\
test(ex, make_expected(S04e, array_e1c));\
test(ex, make_expected(S04e, array_e1f));\
test(ex, make_expected(S04e, array_e1g));\
test(ex, make_expected(S04e, array_e1h));\
test(ex, make_expected(S04e, array_type2{}, aedis::error::incompatible_size));\
test(ex, make_expected(S04e, tuple_int_2{}, aedis::error::incompatible_size));\
test(ex, make_expected(S04f, array_type2{}, aedis::error::nested_aggregate_not_supported));\
test(ex, make_expected(S04g, vec_node_type{}, aedis::error::exceeeds_max_nested_depth));\
test(ex, make_expected(S04h, array_e1d));\
test(ex, make_expected(S04h, array_e1e));\
test(ex, make_expected(S04i, set_type{}, aedis::error::expects_resp3_set)); \
test(ex, make_expected(S09a, set_e1c)); \
test(ex, make_expected(S09a, set_e1d)); \
test(ex, make_expected(S09a, set_e1f)); \
test(ex, make_expected(S09a, set_e1g)); \
test(ex, make_expected(S09a, set_expected1a)); \
test(ex, make_expected(S09a, set_expected_1e)); \
test(ex, make_expected(S09a, set_type{"apple", "one", "orange", "three", "two"})); \
test(ex, make_expected(S04d, std::tuple<uset_type>{set_e1c})); \
test(ex, make_expected(S09b, std::vector<node_type>{ {resp3::type::set, 0UL, 0UL, {}} })); \
test(ex, make_expected(S10a, aedis::ignore{}, aedis::error::resp3_simple_error)); \
test(ex, make_expected(S10a, node_type{resp3::type::simple_error, 1UL, 0UL, {"Error"}}, aedis::error::resp3_simple_error)); \
test(ex, make_expected(S10b, node_type{resp3::type::simple_error, 1UL, 0UL, {""}}, aedis::error::resp3_simple_error)); \
test(ex, make_expected(S03c, map_type{}));\
test(ex, make_expected(S11a, node_type{resp3::type::doublean, 1UL, 0UL, {"1.23"}}));\
test(ex, make_expected(S11b, node_type{resp3::type::doublean, 1UL, 0UL, {"inf"}}));\
test(ex, make_expected(S11c, node_type{resp3::type::doublean, 1UL, 0UL, {"-inf"}}));\
test(ex, make_expected(S11d, double{1.23}));\
test(ex, make_expected(S11e, double{0}, aedis::error::not_a_double));\
test(ex, make_expected(S12a, node_type{resp3::type::blob_error, 1UL, 0UL, {"SYNTAX invalid syntax"}}, aedis::error::resp3_blob_error));\
test(ex, make_expected(S12b, node_type{resp3::type::blob_error, 1UL, 0UL, {}}, aedis::error::resp3_blob_error));\
test(ex, make_expected(S12c, aedis::ignore{}, aedis::error::resp3_blob_error));\
test(ex, make_expected(S13a, node_type{resp3::type::verbatim_string, 1UL, 0UL, {"txt:Some string"}}));\
test(ex, make_expected(S13b, node_type{resp3::type::verbatim_string, 1UL, 0UL, {}}));\
test(ex, make_expected(S14a, node_type{resp3::type::big_number, 1UL, 0UL, {"3492890328409238509324850943850943825024385"}}));\
test(ex, make_expected(S14b, int{}, aedis::error::empty_field));\
test(ex, make_expected(S15a, std::optional<std::string>{"OK"}));\
test(ex, make_expected(S15a, std::string{"OK"}));\
test(ex, make_expected(S15b, std::optional<std::string>{""}));\
test(ex, make_expected(S15b, std::string{""}));\
test(ex, make_expected(S16a, int{}, aedis::error::invalid_data_type));\
test(ex, make_expected(S05d, int{11}, aedis::error::not_a_number));\
test(ex, make_expected(S03d, map_type{}, aedis::error::not_a_number));\
test(ex, make_expected(S02d, std::string{}, aedis::error::not_a_number));\
test(ex, make_expected(S17a, std::string{}, aedis::error::not_a_number));\
test(ex, make_expected(S05e, int{}, aedis::error::empty_field));\
test(ex, make_expected(S01d, std::optional<bool>{}, aedis::error::empty_field));\
test(ex, make_expected(S11f, std::string{}, aedis::error::empty_field));\
test(ex, make_expected(S17b, node_type{resp3::type::blob_string, 1UL, 0UL, {"hh"}}));\
test(ex, make_expected(S18c, node_type{resp3::type::blob_string, 1UL, 0UL, {"hhaa\aaaa\raaaaa\r\naaaaaaaaaa"}}));\
test(ex, make_expected(S18d, node_type{resp3::type::blob_string, 1UL, 0UL, {}}));\
test(ex, make_expected(make_blob_string(blob), node_type{resp3::type::blob_string, 1UL, 0UL, {blob}}));\
BOOST_AUTO_TEST_CASE(parser)
@@ -489,7 +505,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_simple_error)
boost::system::error_code ec;
test_stream ts {ioc};
ts.append(S28);
ts.append(S10a);
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec);
BOOST_CHECK_EQUAL(ec, aedis::error::resp3_simple_error);
BOOST_TEST(!rbuffer.empty());
@@ -502,7 +518,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_blob_error)
boost::system::error_code ec;
test_stream ts {ioc};
ts.append(S36);
ts.append(S12a);
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec);
BOOST_CHECK_EQUAL(ec, aedis::error::resp3_blob_error);
BOOST_TEST(!rbuffer.empty());
@@ -515,7 +531,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_no_error)
boost::system::error_code ec;
test_stream ts {ioc};
ts.append(S10);
ts.append(S05b);
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec);
BOOST_TEST(!ec);
BOOST_TEST(rbuffer.empty());