From 9cf00d6a23cb50f36c1937dab1e711b1c64391c4 Mon Sep 17 00:00:00 2001 From: Marcelo Zimbres Date: Thu, 29 Dec 2022 14:34:37 +0100 Subject: [PATCH] Adds cpp17 async example. --- CMakeLists.txt | 134 +-- README.md | 43 +- examples/cpp17_intro.cpp | 93 ++ .../{intro_sync.cpp => cpp17_intro_sync.cpp} | 0 ...evel_sync.cpp => cpp17_low_level_sync.cpp} | 0 .../{chat_room.cpp => cpp20_chat_room.cpp} | 0 .../{containers.cpp => cpp20_containers.cpp} | 0 ...{echo_server.cpp => cpp20_echo_server.cpp} | 0 examples/{intro.cpp => cpp20_intro.cpp} | 0 .../{intro_tls.cpp => cpp20_intro_tls.cpp} | 0 ...el_async.cpp => cpp20_low_level_async.cpp} | 0 ...el.cpp => cpp20_resolve_with_sentinel.cpp} | 0 ...ialization.cpp => cpp20_serialization.cpp} | 0 .../{subscriber.cpp => cpp20_subscriber.cpp} | 0 tests/low_level.cpp | 976 +++++++----------- 15 files changed, 532 insertions(+), 714 deletions(-) create mode 100644 examples/cpp17_intro.cpp rename examples/{intro_sync.cpp => cpp17_intro_sync.cpp} (100%) rename examples/{low_level_sync.cpp => cpp17_low_level_sync.cpp} (100%) rename examples/{chat_room.cpp => cpp20_chat_room.cpp} (100%) rename examples/{containers.cpp => cpp20_containers.cpp} (100%) rename examples/{echo_server.cpp => cpp20_echo_server.cpp} (100%) rename examples/{intro.cpp => cpp20_intro.cpp} (100%) rename examples/{intro_tls.cpp => cpp20_intro_tls.cpp} (100%) rename examples/{low_level_async.cpp => cpp20_low_level_async.cpp} (100%) rename examples/{resolve_with_sentinel.cpp => cpp20_resolve_with_sentinel.cpp} (100%) rename examples/{serialization.cpp => cpp20_serialization.cpp} (100%) rename examples/{subscriber.cpp => cpp20_subscriber.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e957a7f..cce6f3da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,92 +73,100 @@ endif() # Executables #======================================================================= -add_executable(intro examples/intro.cpp) -target_link_libraries(intro common) -target_compile_features(intro PUBLIC cxx_std_20) -add_test(intro intro) +add_executable(cpp20_intro examples/cpp20_intro.cpp) +target_link_libraries(cpp20_intro common) +target_compile_features(cpp20_intro PUBLIC cxx_std_20) +add_test(cpp20_intro cpp20_intro) if (MSVC) - target_compile_options(intro PRIVATE /bigobj) - target_compile_definitions(intro PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_intro PRIVATE /bigobj) + target_compile_definitions(cpp20_intro PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(intro_sync examples/intro_sync.cpp) -target_compile_features(intro_sync PUBLIC cxx_std_20) -add_test(intro_sync intro_sync) -add_test(intro_sync intro_sync) +add_executable(cpp17_intro examples/cpp17_intro.cpp) +target_compile_features(cpp17_intro PUBLIC cxx_std_17) +add_test(cpp17_intro cpp17_intro) if (MSVC) - target_compile_options(intro_sync PRIVATE /bigobj) - target_compile_definitions(intro_sync PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp17_intro PRIVATE /bigobj) + target_compile_definitions(cpp17_intro PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(chat_room examples/chat_room.cpp) -target_compile_features(chat_room PUBLIC cxx_std_20) -target_link_libraries(chat_room common) +add_executable(cpp17_intro_sync examples/cpp17_intro_sync.cpp) +target_compile_features(cpp17_intro_sync PUBLIC cxx_std_17) +add_test(cpp17_intro_sync cpp17_intro_sync) +add_test(cpp17_intro_sync cpp17_intro_sync) if (MSVC) - target_compile_options(chat_room PRIVATE /bigobj) - target_compile_definitions(chat_room PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp17_intro_sync PRIVATE /bigobj) + target_compile_definitions(cpp17_intro_sync PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(containers examples/containers.cpp) -target_compile_features(containers PUBLIC cxx_std_20) -target_link_libraries(containers common) -add_test(containers containers) +add_executable(cpp20_chat_room examples/cpp20_chat_room.cpp) +target_compile_features(cpp20_chat_room PUBLIC cxx_std_20) +target_link_libraries(cpp20_chat_room common) if (MSVC) - target_compile_options(containers PRIVATE /bigobj) - target_compile_definitions(containers PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_chat_room PRIVATE /bigobj) + target_compile_definitions(cpp20_chat_room PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(echo_server examples/echo_server.cpp) -target_compile_features(echo_server PUBLIC cxx_std_20) -target_link_libraries(echo_server common) +add_executable(cpp20_containers examples/cpp20_containers.cpp) +target_compile_features(cpp20_containers PUBLIC cxx_std_20) +target_link_libraries(cpp20_containers common) +add_test(cpp20_containers cpp20_containers) if (MSVC) - target_compile_options(echo_server PRIVATE /bigobj) - target_compile_definitions(echo_server PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_containers PRIVATE /bigobj) + target_compile_definitions(cpp20_containers PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(resolve_with_sentinel examples/resolve_with_sentinel.cpp) -target_compile_features(resolve_with_sentinel PUBLIC cxx_std_20) -target_link_libraries(resolve_with_sentinel common) -#add_test(resolve_with_sentinel resolve_with_sentinel) +add_executable(cpp20_echo_server examples/cpp20_echo_server.cpp) +target_compile_features(cpp20_echo_server PUBLIC cxx_std_20) +target_link_libraries(cpp20_echo_server common) if (MSVC) - target_compile_options(resolve_with_sentinel PRIVATE /bigobj) - target_compile_definitions(resolve_with_sentinel PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_echo_server PRIVATE /bigobj) + target_compile_definitions(cpp20_echo_server PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(serialization examples/serialization.cpp) -target_compile_features(serialization PUBLIC cxx_std_20) -target_link_libraries(serialization common) -add_test(serialization serialization) +add_executable(cpp20_resolve_with_sentinel examples/cpp20_resolve_with_sentinel.cpp) +target_compile_features(cpp20_resolve_with_sentinel PUBLIC cxx_std_20) +target_link_libraries(cpp20_resolve_with_sentinel common) +#add_test(cpp20_resolve_with_sentinel cpp20_resolve_with_sentinel) if (MSVC) - target_compile_options(serialization PRIVATE /bigobj) - target_compile_definitions(serialization PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_resolve_with_sentinel PRIVATE /bigobj) + target_compile_definitions(cpp20_resolve_with_sentinel PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(subscriber examples/subscriber.cpp) -target_compile_features(subscriber PUBLIC cxx_std_20) -target_link_libraries(subscriber common) +add_executable(cpp20_serialization examples/cpp20_serialization.cpp) +target_compile_features(cpp20_serialization PUBLIC cxx_std_20) +target_link_libraries(cpp20_serialization common) +add_test(cpp20_serialization cpp20_serialization) if (MSVC) - target_compile_options(subscriber PRIVATE /bigobj) - target_compile_definitions(subscriber PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_serialization PRIVATE /bigobj) + target_compile_definitions(cpp20_serialization PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(intro_tls examples/intro_tls.cpp) -target_compile_features(intro_tls PUBLIC cxx_std_20) -add_test(intro_tls intro_tls) -target_link_libraries(intro_tls OpenSSL::Crypto OpenSSL::SSL) -target_link_libraries(intro_tls common) +add_executable(cpp20_subscriber examples/cpp20_subscriber.cpp) +target_compile_features(cpp20_subscriber PUBLIC cxx_std_20) +target_link_libraries(cpp20_subscriber common) if (MSVC) - target_compile_options(intro_tls PRIVATE /bigobj) - target_compile_definitions(intro_tls PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_subscriber PRIVATE /bigobj) + target_compile_definitions(cpp20_subscriber PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(low_level_async examples/low_level_async.cpp) -target_compile_features(low_level_async PUBLIC cxx_std_20) -add_test(low_level_async low_level_async) -target_link_libraries(low_level_async common) +add_executable(cpp20_intro_tls examples/cpp20_intro_tls.cpp) +target_compile_features(cpp20_intro_tls PUBLIC cxx_std_20) +add_test(cpp20_intro_tls cpp20_intro_tls) +target_link_libraries(cpp20_intro_tls OpenSSL::Crypto OpenSSL::SSL) +target_link_libraries(cpp20_intro_tls common) if (MSVC) - target_compile_options(low_level_async PRIVATE /bigobj) - target_compile_definitions(low_level_async PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp20_intro_tls PRIVATE /bigobj) + target_compile_definitions(cpp20_intro_tls PRIVATE _WIN32_WINNT=0x0601) +endif() + +add_executable(cpp20_low_level_async examples/cpp20_low_level_async.cpp) +target_compile_features(cpp20_low_level_async PUBLIC cxx_std_20) +add_test(cpp20_low_level_async cpp20_low_level_async) +target_link_libraries(cpp20_low_level_async common) +if (MSVC) + target_compile_options(cpp20_low_level_async PRIVATE /bigobj) + target_compile_definitions(cpp20_low_level_async PRIVATE _WIN32_WINNT=0x0601) endif() add_executable(echo_server_client benchmarks/cpp/asio/echo_server_client.cpp) @@ -175,12 +183,12 @@ if (MSVC) target_compile_definitions(echo_server_direct PRIVATE _WIN32_WINNT=0x0601) endif() -add_executable(low_level_sync examples/low_level_sync.cpp) -target_compile_features(low_level_sync PUBLIC cxx_std_17) -add_test(low_level_sync low_level_sync) +add_executable(cpp17_low_level_sync examples/cpp17_low_level_sync.cpp) +target_compile_features(cpp17_low_level_sync PUBLIC cxx_std_17) +add_test(cpp17_low_level_sync cpp17_low_level_sync) if (MSVC) - target_compile_options(low_level_sync PRIVATE /bigobj) - target_compile_definitions(low_level_sync PRIVATE _WIN32_WINNT=0x0601) + target_compile_options(cpp17_low_level_sync PRIVATE /bigobj) + target_compile_definitions(cpp17_low_level_sync PRIVATE _WIN32_WINNT=0x0601) endif() add_executable(test_conn_exec tests/conn_exec.cpp) diff --git a/README.md b/README.md index a3f99677..c9e08a23 100644 --- a/README.md +++ b/README.md @@ -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 intro.cpp and containers.cpp) +in a `std::map` (see cpp20_intro.cpp, cpp17_intro.cpp and containers.cpp) ```cpp auto async_main() -> net::awaitable @@ -51,30 +51,27 @@ completes, these functions play the following roles 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 and to - `aedis::connection::async_receive` when a server-push is received. - It will also trigger writes of pending requests when a reconnection - occurs. It remains suspended until the connection is lost. + `async_exec` when a response arrives, to + `aedis::connection::async_receive` when a server-push is received + and will trigger writes of pending requests when a reconnection + occurs. -By carefully choosing the flags `aedis::resp3::request::config` users -can also express their desire to not automatically cancel pending -requests when the connection is lost, giving the opportunity to -reconnect and call `async_run` again after a failover to trigger their -execution, perhaps in another Redis instance. Before we approach -long-lived connections in the next section users will find it helpful -to skim over the examples +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 -* intro.cpp: The Aedis hello-world program. Sends one command and quits the connection. -* intro_tls.cpp: Same as intro.cpp but over TLS. -* intro_sync.cpp: Shows how to use the connection class synchronously. -* containers.cpp: Shows how to send and receive STL containers and how to use transactions. -* serialization.cpp: Shows how to serialize types using Boost.Json. -* resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels. -* subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription. -* echo_server.cpp: A simple TCP echo server. -* chat_room.cpp: A command line chat built on Redis pubsub. -* low_level_sync.cpp: Sends a ping synchronously using the low-level API. -* low_level_async.cpp: Sends a ping asynchronously using the low-level API. +* 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. +* cpp20_subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription. +* 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. To avoid repetition code that is common to some examples has been grouped in common.hpp. The main function used in some async examples diff --git a/examples/cpp17_intro.cpp b/examples/cpp17_intro.cpp new file mode 100644 index 00000000..2210470c --- /dev/null +++ b/examples/cpp17_intro.cpp @@ -0,0 +1,93 @@ +/* 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 +#include +#include +#include + +namespace net = boost::asio; +namespace resp3 = aedis::resp3; +using aedis::resp3::request; +using aedis::adapt; +using aedis::operation; + +void log(boost::system::error_code const& ec, char const* prefix) +{ + std::clog << prefix << ec.message() << std::endl; +} + +auto main() -> int +{ + try { + // The request + resp3::request req; + req.push("HELLO", 3); + req.push("PING", "Hello world"); + req.push("QUIT"); + + // The response. + std::tuple resp; + + net::io_context ioc; + + // IO objects. + net::ip::tcp::resolver resv{ioc}; + aedis::connection conn{ioc}; + + // Resolve endpoints. + net::ip::tcp::resolver::results_type endpoints; + + // async_run callback. + auto on_run = [](auto ec) + { + if (ec) + return log(ec, "on_exec: "); + }; + + // async_exec callback. + auto on_exec = [&](auto ec, auto) + { + if (ec) { + conn.cancel(operation::run); + return log(ec, "on_exec: "); + } + + std::cout << "PING: " << std::get<1>(resp) << std::endl; + }; + + // Connect callback. + auto on_connect = [&](auto ec, auto) + { + if (ec) + return log(ec, "on_connect: "); + + conn.async_run(on_run); + conn.async_exec(req, adapt(resp), on_exec); + }; + + // Resolve callback. + auto on_resolve = [&](auto ec, auto const& addrs) + { + if (ec) + return log(ec, "on_resolve: "); + + endpoints = addrs; + net::async_connect(conn.next_layer(), endpoints, on_connect); + }; + + resv.async_resolve("127.0.0.1", "6379", on_resolve); + + ioc.run(); + return 0; + + } catch (std::exception const& e) { + std::cerr << "Error: " << e.what() << std::endl; + } + + return 1; +} + diff --git a/examples/intro_sync.cpp b/examples/cpp17_intro_sync.cpp similarity index 100% rename from examples/intro_sync.cpp rename to examples/cpp17_intro_sync.cpp diff --git a/examples/low_level_sync.cpp b/examples/cpp17_low_level_sync.cpp similarity index 100% rename from examples/low_level_sync.cpp rename to examples/cpp17_low_level_sync.cpp diff --git a/examples/chat_room.cpp b/examples/cpp20_chat_room.cpp similarity index 100% rename from examples/chat_room.cpp rename to examples/cpp20_chat_room.cpp diff --git a/examples/containers.cpp b/examples/cpp20_containers.cpp similarity index 100% rename from examples/containers.cpp rename to examples/cpp20_containers.cpp diff --git a/examples/echo_server.cpp b/examples/cpp20_echo_server.cpp similarity index 100% rename from examples/echo_server.cpp rename to examples/cpp20_echo_server.cpp diff --git a/examples/intro.cpp b/examples/cpp20_intro.cpp similarity index 100% rename from examples/intro.cpp rename to examples/cpp20_intro.cpp diff --git a/examples/intro_tls.cpp b/examples/cpp20_intro_tls.cpp similarity index 100% rename from examples/intro_tls.cpp rename to examples/cpp20_intro_tls.cpp diff --git a/examples/low_level_async.cpp b/examples/cpp20_low_level_async.cpp similarity index 100% rename from examples/low_level_async.cpp rename to examples/cpp20_low_level_async.cpp diff --git a/examples/resolve_with_sentinel.cpp b/examples/cpp20_resolve_with_sentinel.cpp similarity index 100% rename from examples/resolve_with_sentinel.cpp rename to examples/cpp20_resolve_with_sentinel.cpp diff --git a/examples/serialization.cpp b/examples/cpp20_serialization.cpp similarity index 100% rename from examples/serialization.cpp rename to examples/cpp20_serialization.cpp diff --git a/examples/subscriber.cpp b/examples/cpp20_subscriber.cpp similarity index 100% rename from examples/subscriber.cpp rename to examples/cpp20_subscriber.cpp diff --git a/tests/low_level.cpp b/tests/low_level.cpp index 9905eec9..33de70bf 100644 --- a/tests/low_level.cpp +++ b/tests/low_level.cpp @@ -37,6 +37,38 @@ using test_stream = boost::beast::test::stream; using aedis::adapter::adapt2; using node_type = aedis::resp3::node; using vec_node_type = std::vector; +using vec_type = std::vector; +using op_vec_type = std::optional>; + +// Set +using set_type = std::set; +using mset_type = std::multiset; +using uset_type = std::unordered_set; +using muset_type = std::unordered_multiset; + +// Array +using tuple_int_2 = std::tuple; +using array_type = std::array; +using array_type2 = std::array; + +// Map +using map_type = std::map; +using mmap_type = std::multimap; +using umap_type = std::unordered_map; +using mumap_type = std::unordered_multimap; +using op_map_type = std::optional>; +using tuple8_type = std::tuple; + +// Null +using op_type_01 = std::optional; +using op_type_02 = std::optional; +using op_type_03 = std::optional; +using op_type_04 = std::optional>; +using op_type_05 = std::optional>; +using op_type_06 = std::optional>; +using op_type_07 = std::optional>; +using op_type_08 = std::optional>; +using op_type_09 = std::optional>; //------------------------------------------------------------------- @@ -44,20 +76,18 @@ template struct expect { std::string in; Result expected; - std::string name; boost::system::error_code ec{}; }; template -auto make_expected(std::string in, Result expected, std::string name, boost::system::error_code ec = {}) +auto make_expected(std::string in, Result expected, boost::system::error_code ec = {}) { - return expect{in, expected, name, ec}; + return expect{in, expected, ec}; } template void test_sync(net::any_io_executor ex, expect e) { - std::cout << e.name << std::endl; std::string rbuffer; test_stream ts {ex}; ts.append(e.in); @@ -115,6 +145,30 @@ void test_async(net::any_io_executor ex, expect e) std::make_shared>(ex, e)->run(); } +auto make_blob() +{ + std::string str(100000, 'a'); + str[1000] = '\r'; + str[1001] = '\n'; + return str; + + return str; +} + +auto const blob = make_blob(); + +auto make_blob_string(std::string const& b) +{ + std::string wire; + wire += '$'; + wire += std::to_string(b.size()); + wire += "\r\n"; + wire += b; + wire += "\r\n"; + + return wire; +} + std::optional op_int_ok = 11; std::optional op_bool_ok = true; @@ -130,6 +184,117 @@ std::vector streamed_string_e1 std::vector streamed_string_e2 { {resp3::type::streamed_string, 0UL, 1UL, {}}, {resp3::type::streamed_string_part, 1UL, 1UL, {}} }; +std::vector const push_e1a + { {resp3::type::push, 4UL, 0UL, {}} + , {resp3::type::simple_string, 1UL, 1UL, "pubsub"} + , {resp3::type::simple_string, 1UL, 1UL, "message"} + , {resp3::type::simple_string, 1UL, 1UL, "some-channel"} + , {resp3::type::simple_string, 1UL, 1UL, "some message"} + }; + +std::vector const push_e1b + { {resp3::type::push, 0UL, 0UL, {}} }; + +std::vector const set_expected1a + { {resp3::type::set, 6UL, 0UL, {}} + , {resp3::type::simple_string, 1UL, 1UL, {"orange"}} + , {resp3::type::simple_string, 1UL, 1UL, {"apple"}} + , {resp3::type::simple_string, 1UL, 1UL, {"one"}} + , {resp3::type::simple_string, 1UL, 1UL, {"two"}} + , {resp3::type::simple_string, 1UL, 1UL, {"three"}} + , {resp3::type::simple_string, 1UL, 1UL, {"orange"}} + }; + +mset_type const set_e1f{"apple", "one", "orange", "orange", "three", "two"}; +uset_type const set_e1c{"apple", "one", "orange", "three", "two"}; +muset_type const set_e1g{"apple", "one", "orange", "orange", "three", "two"}; +vec_type const set_e1d = {"orange", "apple", "one", "two", "three", "orange"}; +op_vec_type const set_expected_1e = set_e1d; + +std::vector const array_e1a + { {resp3::type::array, 3UL, 0UL, {}} + , {resp3::type::blob_string, 1UL, 1UL, {"11"}} + , {resp3::type::blob_string, 1UL, 1UL, {"22"}} + , {resp3::type::blob_string, 1UL, 1UL, {"3"}} + }; + +std::vector const array_e1b{11, 22, 3}; +std::vector const array_e1c{"11", "22", "3"}; +std::vector const array_e1d{}; +std::vector const array_e1e{{resp3::type::array, 0UL, 0UL, {}}}; +array_type const array_e1f{11, 22, 3}; +std::list const array_e1g{11, 22, 3}; +std::deque const array_e1h{11, 22, 3}; + +std::vector const map_expected_1a +{ {resp3::type::map, 4UL, 0UL, {}} +, {resp3::type::blob_string, 1UL, 1UL, {"key1"}} +, {resp3::type::blob_string, 1UL, 1UL, {"value1"}} +, {resp3::type::blob_string, 1UL, 1UL, {"key2"}} +, {resp3::type::blob_string, 1UL, 1UL, {"value2"}} +, {resp3::type::blob_string, 1UL, 1UL, {"key3"}} +, {resp3::type::blob_string, 1UL, 1UL, {"value3"}} +, {resp3::type::blob_string, 1UL, 1UL, {"key3"}} +, {resp3::type::blob_string, 1UL, 1UL, {"value3"}} +}; + +map_type const map_expected_1b +{ {"key1", "value1"} +, {"key2", "value2"} +, {"key3", "value3"} +}; + +umap_type const map_e1g +{ {"key1", "value1"} +, {"key2", "value2"} +, {"key3", "value3"} +}; + +mmap_type const map_e1k +{ {"key1", "value1"} +, {"key2", "value2"} +, {"key3", "value3"} +, {"key3", "value3"} +}; + +mumap_type const map_e1l +{ {"key1", "value1"} +, {"key2", "value2"} +, {"key3", "value3"} +, {"key3", "value3"} +}; + +std::vector const map_expected_1c +{ "key1", "value1" +, "key2", "value2" +, "key3", "value3" +, "key3", "value3" +}; + +op_map_type const map_expected_1d = map_expected_1b; + +op_vec_type const map_expected_1e = map_expected_1c; + +tuple8_type const map_e1f +{ std::string{"key1"}, std::string{"value1"} +, std::string{"key2"}, std::string{"value2"} +, std::string{"key3"}, std::string{"value3"} +, std::string{"key3"}, std::string{"value3"} +}; + +std::vector const attr_e1a + { {resp3::type::attribute, 1UL, 0UL, {}} + , {resp3::type::simple_string, 1UL, 1UL, "key-popularity"} + , {resp3::type::map, 2UL, 1UL, {}} + , {resp3::type::blob_string, 1UL, 2UL, "a"} + , {resp3::type::doublean, 1UL, 2UL, "0.1923"} + , {resp3::type::blob_string, 1UL, 2UL, "b"} + , {resp3::type::doublean, 1UL, 2UL, "0.0012"} + }; + +std::vector const attr_e1b + { {resp3::type::attribute, 0UL, 0UL, {}} }; + #define S01 "#11\r\n" #define S02 "#f\r\n" #define S03 "#t\r\n" @@ -159,632 +324,147 @@ std::vector streamed_string_e2 { {resp3::type::streamed_string, 0UL, #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 NUMBER_TEST_CONDITIONS(test) \ - test(ex, make_expected(S01, std::optional{}, "bool.error", aedis::error::unexpected_bool_value)); \ - test(ex, make_expected(S02, bool{false}, "bool.bool (true)")); \ - test(ex, make_expected(S02, node_type{resp3::type::boolean, 1UL, 0UL, {"f"}}, "bool.node (false)")); \ - test(ex, make_expected(S03, bool{true}, "bool.bool (true)")); \ - test(ex, make_expected(S03, node_type{resp3::type::boolean, 1UL, 0UL, {"t"}}, "bool.node (true)")); \ - test(ex, make_expected(S03, op_bool_ok, "optional.int")); \ - test(ex, make_expected(S03, std::map{}, "bool.error", aedis::error::expects_resp3_map)); \ - test(ex, make_expected(S03, std::set{}, "bool.error", aedis::error::expects_resp3_set)); \ - test(ex, make_expected(S03, std::unordered_map{}, "bool.error", aedis::error::expects_resp3_map)); \ - test(ex, make_expected(S03, std::unordered_set{}, "bool.error", aedis::error::expects_resp3_set)); \ - test(ex, make_expected(S04, streamed_string_e2, "streamed_string.node.empty")); \ - test(ex, make_expected(S05, std::optional{}, "number.optional.int.error", aedis::error::expects_resp3_simple_type));; \ - test(ex, make_expected(S06, int{}, "streamed_string.string", aedis::error::not_a_number)); \ - test(ex, make_expected(S06, std::string{"Hello word"}, "streamed_string.string")); \ - test(ex, make_expected(S06, streamed_string_e1, "streamed_string.node")); \ - test(ex, make_expected(S07, std::string{}, "streamed_string.error", aedis::error::not_a_number)); \ - test(ex, make_expected(S08, std::tuple{11}, "number.tuple.int")); \ - test(ex, make_expected(S09, node_type{resp3::type::number, 1UL, 0UL, {"-3"}}, "number.node (negative)")); \ - test(ex, make_expected(S10, int{11}, "number.int")); \ - test(ex, make_expected(S10, op_int_ok, "number.optional.int")); \ - test(ex, make_expected(S10, std::list{}, "number.optional.int", aedis::error::expects_resp3_aggregate)); \ - test(ex, make_expected(S10, std::map{}, "number.optional.int", aedis::error::expects_resp3_map)); \ - test(ex, make_expected(S10, std::set{}, "number.optional.int", aedis::error::expects_resp3_set)); \ - test(ex, make_expected(S10, std::unordered_map{}, "number.optional.int", aedis::error::expects_resp3_map)); \ - test(ex, make_expected(S10, std::unordered_set{}, "number.optional.int", aedis::error::expects_resp3_set)); \ - test(ex, make_expected(S11, node_type{resp3::type::number, 1UL, 0UL, {"3"}}, "number.node (positive)")); \ - test(ex, make_expected(S12, int{0}, "number.int.error.null", aedis::error::resp3_null)); \ - -BOOST_AUTO_TEST_CASE(test_push) -{ - net::io_context ioc; - - std::vector e1a - { {resp3::type::push, 4UL, 0UL, {}} - , {resp3::type::simple_string, 1UL, 1UL, "pubsub"} - , {resp3::type::simple_string, 1UL, 1UL, "message"} - , {resp3::type::simple_string, 1UL, 1UL, "some-channel"} - , {resp3::type::simple_string, 1UL, 1UL, "some message"} - }; - - std::vector e1b { {resp3::type::push, 0UL, 0UL, {}} }; - - auto const in01 = expect>{S13, e1a, "push.node"}; - auto const in02 = expect>{S14, e1b, "push.node.empty"}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - - test_async(ex, in01); - test_async(ex, in02); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_map) -{ - net::io_context ioc; - using map_type = std::map; - using mmap_type = std::multimap; - using umap_type = std::unordered_map; - using mumap_type = std::unordered_multimap; - using vec_type = std::vector; - using op_map_type = std::optional>; - using op_vec_type = std::optional>; - using tuple_type = std::tuple; - - std::vector expected_1a - { {resp3::type::map, 4UL, 0UL, {}} - , {resp3::type::blob_string, 1UL, 1UL, {"key1"}} - , {resp3::type::blob_string, 1UL, 1UL, {"value1"}} - , {resp3::type::blob_string, 1UL, 1UL, {"key2"}} - , {resp3::type::blob_string, 1UL, 1UL, {"value2"}} - , {resp3::type::blob_string, 1UL, 1UL, {"key3"}} - , {resp3::type::blob_string, 1UL, 1UL, {"value3"}} - , {resp3::type::blob_string, 1UL, 1UL, {"key3"}} - , {resp3::type::blob_string, 1UL, 1UL, {"value3"}} - }; - - map_type expected_1b - { {"key1", "value1"} - , {"key2", "value2"} - , {"key3", "value3"} - }; - - umap_type e1g - { {"key1", "value1"} - , {"key2", "value2"} - , {"key3", "value3"} - }; - - mmap_type e1k - { {"key1", "value1"} - , {"key2", "value2"} - , {"key3", "value3"} - , {"key3", "value3"} - }; - - mumap_type e1l - { {"key1", "value1"} - , {"key2", "value2"} - , {"key3", "value3"} - , {"key3", "value3"} - }; - - std::vector expected_1c - { "key1", "value1" - , "key2", "value2" - , "key3", "value3" - , "key3", "value3" - }; - - op_map_type expected_1d; - expected_1d = expected_1b; - - op_vec_type expected_1e; - expected_1e = expected_1c; - - tuple_type e1f - { std::string{"key1"}, std::string{"value1"} - , std::string{"key2"}, std::string{"value2"} - , std::string{"key3"}, std::string{"value3"} - , std::string{"key3"}, std::string{"value3"} - }; - - auto const in00 = expect>{S16, expected_1a, "map.node"}; - auto const in01 = expect{"%0\r\n", map_type{}, "map.map.empty"}; - auto const in02 = expect{S16, expected_1b, "map.map"}; - auto const in03 = expect{S16, e1k, "map.multimap"}; - auto const in04 = expect{S16, e1g, "map.unordered_map"}; - auto const in05 = expect{S16, e1l, "map.unordered_multimap"}; - auto const in06 = expect{S16, expected_1c, "map.vector"}; - auto const in07 = expect{S16, expected_1d, "map.optional.map"}; - auto const in08 = expect{S16, expected_1e, "map.optional.vector"}; - auto const in09 = expect>{S17, std::tuple{expected_1d}, "map.transaction.optional.map"}; - auto const in10 = expect{"%11\r\n", int{}, "map.invalid.int", aedis::error::expects_resp3_simple_type}; - auto const in11 = expect{S16, e1f, "map.tuple"}; - auto const in12 = expect{S15, map_type{}, "map.error", aedis::error::expects_resp3_map}; - auto const in13 = expect{S12, map_type{}, "map.null", aedis::error::resp3_null}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in00); - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - test_sync(ex, in05); - test_sync(ex, in06); - test_sync(ex, in07); - test_sync(ex, in08); - test_sync(ex, in09); - test_sync(ex, in10); - test_sync(ex, in11); - test_sync(ex, in12); - test_sync(ex, in13); - - test_async(ex, in00); - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - test_async(ex, in05); - test_async(ex, in06); - test_async(ex, in07); - test_async(ex, in08); - test_async(ex, in09); - test_async(ex, in10); - test_async(ex, in11); - test_async(ex, in12); - test_async(ex, in13); - ioc.run(); -} - -void test_attribute(net::io_context& ioc) -{ - std::vector e1a - { {resp3::type::attribute, 1UL, 0UL, {}} - , {resp3::type::simple_string, 1UL, 1UL, "key-popularity"} - , {resp3::type::map, 2UL, 1UL, {}} - , {resp3::type::blob_string, 1UL, 2UL, "a"} - , {resp3::type::doublean, 1UL, 2UL, "0.1923"} - , {resp3::type::blob_string, 1UL, 2UL, "b"} - , {resp3::type::doublean, 1UL, 2UL, "0.0012"} - }; - - std::vector e1b; - - auto const in01 = expect>{S18, e1a, "attribute.node"}; - auto const in02 = expect>{S19, e1b, "attribute.node.empty"}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - - test_async(ex, in01); - test_async(ex, in02); -} - -BOOST_AUTO_TEST_CASE(test_array) -{ - using tuple_type = std::tuple; - using array_type = std::array; - using array_type2 = std::array; - - net::io_context ioc; - - std::vector e1a - { {resp3::type::array, 3UL, 0UL, {}} - , {resp3::type::blob_string, 1UL, 1UL, {"11"}} - , {resp3::type::blob_string, 1UL, 1UL, {"22"}} - , {resp3::type::blob_string, 1UL, 1UL, {"3"}} - }; - - std::vector const e1b{11, 22, 3}; - std::vector const e1c{"11", "22", "3"}; - std::vector const e1d{}; - std::vector const e1e{{resp3::type::array, 0UL, 0UL, {}}}; - array_type const e1f{11, 22, 3}; - std::list const e1g{11, 22, 3}; - std::deque const e1h{11, 22, 3}; - - auto const in01 = expect>{S20, e1a, "array.node"}; - auto const in02 = expect>{S20, e1b, "array.int"}; - auto const in03 = expect>{S23, e1e, "array.node.empty"}; - auto const in04 = expect>{S23, e1d, "array.string.empty"}; - auto const in05 = expect>{S20, e1c, "array.string"}; - auto const in06 = expect{S20, e1f, "array.array"}; - auto const in07 = expect>{S20, e1g, "array.list"}; - auto const in08 = expect>{S20, e1h, "array.deque"}; - auto const in09 = expect>{S12, std::vector{}, "array.vector", aedis::error::resp3_null}; - auto const in10 = expect>{S12, std::list{}, "array.list", aedis::error::resp3_null}; - auto const in11 = expect{S12, array_type{}, "array.null", aedis::error::resp3_null}; - auto const in12 = expect{S20, tuple_type{}, "array.list", aedis::error::incompatible_size}; - auto const in13 = expect{S21, array_type2{}, "array.nested", aedis::error::nested_aggregate_not_supported}; - auto const in14 = expect{S20, array_type2{}, "array.null", aedis::error::incompatible_size}; - auto const in15 = expect{S11, array_type2{}, "array.array", aedis::error::expects_resp3_aggregate}; - auto const in16 = expect{S22, vec_node_type{}, "array.depth.exceeds", aedis::error::exceeeds_max_nested_depth}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - test_sync(ex, in05); - test_sync(ex, in06); - test_sync(ex, in07); - test_sync(ex, in08); - test_sync(ex, in09); - test_sync(ex, in10); - test_sync(ex, in11); - test_sync(ex, in12); - test_sync(ex, in13); - test_sync(ex, in14); - test_sync(ex, in15); - test_sync(ex, in16); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - test_async(ex, in05); - test_async(ex, in06); - test_async(ex, in07); - test_async(ex, in08); - test_async(ex, in09); - test_async(ex, in10); - test_async(ex, in11); - test_async(ex, in12); - test_async(ex, in13); - test_async(ex, in14); - test_async(ex, in15); - test_async(ex, in16); - - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_set) -{ - net::io_context ioc; - using set_type = std::set; - using mset_type = std::multiset; - using uset_type = std::unordered_set; - using muset_type = std::unordered_multiset; - using vec_type = std::vector; - using op_vec_type = std::optional>; - - std::vector const expected1a - { {resp3::type::set, 6UL, 0UL, {}} - , {resp3::type::simple_string, 1UL, 1UL, {"orange"}} - , {resp3::type::simple_string, 1UL, 1UL, {"apple"}} - , {resp3::type::simple_string, 1UL, 1UL, {"one"}} - , {resp3::type::simple_string, 1UL, 1UL, {"two"}} - , {resp3::type::simple_string, 1UL, 1UL, {"three"}} - , {resp3::type::simple_string, 1UL, 1UL, {"orange"}} - }; - - mset_type const e1f{"apple", "one", "orange", "orange", "three", "two"}; - uset_type const e1c{"apple", "one", "orange", "three", "two"}; - muset_type const e1g{"apple", "one", "orange", "orange", "three", "two"}; - vec_type const e1d = {"orange", "apple", "one", "two", "three", "orange"}; - op_vec_type expected_1e; - expected_1e = e1d; - - auto const in00 = expect>{S25, expected1a, "set.node"}; - auto const in01 = expect>{S27, std::vector{ {resp3::type::set, 0UL, 0UL, {}} }, "set.node (empty)"}; - auto const in02 = expect{S25, set_type{"apple", "one", "orange", "three", "two"}, "set.set"}; - auto const in03 = expect{S25, e1f, "set.multiset"}; - auto const in04 = expect{S25, e1d, "set.vector "}; - auto const in05 = expect{S25, expected_1e, "set.vector "}; - auto const in06 = expect{S25, e1c, "set.unordered_set"}; - auto const in07 = expect{S25, e1g, "set.unordered_multiset"}; - auto const in08 = expect>{S26, std::tuple{e1c}, "set.tuple"}; - auto const in09 = expect{S24, set_type{}, "set.error", aedis::error::expects_resp3_set}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in00); - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - test_sync(ex, in05); - test_sync(ex, in06); - test_sync(ex, in07); - test_sync(ex, in08); - test_sync(ex, in09); - - test_async(ex, in00); - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - test_async(ex, in05); - test_async(ex, in06); - test_async(ex, in07); - test_async(ex, in08); - test_async(ex, in09); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_simple_error) -{ - net::io_context ioc; - auto const in01 = expect{S28, node_type{resp3::type::simple_error, 1UL, 0UL, {"Error"}}, "simple_error.node", aedis::error::resp3_simple_error}; - auto const in02 = expect{S29, node_type{resp3::type::simple_error, 1UL, 0UL, {""}}, "simple_error.node.empty", aedis::error::resp3_simple_error}; - auto const in03 = expect{S28, aedis::ignore{}, "simple_error.not.ignore.error", aedis::error::resp3_simple_error}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_blob_string) -{ - net::io_context ioc; - std::string str(100000, 'a'); - str[1000] = '\r'; - str[1001] = '\n'; - - std::string wire; - wire += '$'; - wire += std::to_string(str.size()); - wire += "\r\n"; - wire += str; - wire += "\r\n"; - - auto const in01 = expect{"$2\r\nhh\r\n", node_type{resp3::type::blob_string, 1UL, 0UL, {"hh"}}, "blob_string.node"}; - auto const in02 = expect{"$26\r\nhhaa\aaaa\raaaaa\r\naaaaaaaaaa\r\n", node_type{resp3::type::blob_string, 1UL, 0UL, {"hhaa\aaaa\raaaaa\r\naaaaaaaaaa"}}, "blob_string.node (with separator)"}; - auto const in03 = expect{"$0\r\n\r\n", node_type{resp3::type::blob_string, 1UL, 0UL, {}}, "blob_string.node.empty"}; - auto const in04 = expect{wire, node_type{resp3::type::blob_string, 1UL, 0UL, {str}}, "blob_string.node (long string)"}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_double) -{ - net::io_context ioc; - auto const in01 = expect{",1.23\r\n", node_type{resp3::type::doublean, 1UL, 0UL, {"1.23"}}, "double.node"}; - auto const in02 = expect{",inf\r\n", node_type{resp3::type::doublean, 1UL, 0UL, {"inf"}}, "double.node (inf)"}; - auto const in03 = expect{",-inf\r\n", node_type{resp3::type::doublean, 1UL, 0UL, {"-inf"}}, "double.node (-inf)"}; - auto const in04 = expect{",1.23\r\n", double{1.23}, "double.double"}; - auto const in05 = expect{",er\r\n", double{0}, "double.double", aedis::error::not_a_double}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - test_sync(ex, in05); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - test_async(ex, in05); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_blob_error) -{ - net::io_context ioc; - auto const in01 = expect{"!21\r\nSYNTAX invalid syntax\r\n", node_type{resp3::type::blob_error, 1UL, 0UL, {"SYNTAX invalid syntax"}}, "blob_error", aedis::error::resp3_blob_error}; - auto const in02 = expect{"!0\r\n\r\n", node_type{resp3::type::blob_error, 1UL, 0UL, {}}, "blob_error.empty", aedis::error::resp3_blob_error}; - auto const in03 = expect{"!3\r\nfoo\r\n", aedis::ignore{}, "blob_error.ignore.adapter.error", aedis::error::resp3_blob_error}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_verbatim_string) -{ - net::io_context ioc; - auto const in01 = expect{"=15\r\ntxt:Some string\r\n", node_type{resp3::type::verbatim_string, 1UL, 0UL, {"txt:Some string"}}, "verbatim_string"}; - auto const in02 = expect{"=0\r\n\r\n", node_type{resp3::type::verbatim_string, 1UL, 0UL, {}}, "verbatim_string.empty"}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - - test_async(ex, in01); - test_async(ex, in02); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_big_number) -{ - net::io_context ioc; - auto const in01 = expect{"(3492890328409238509324850943850943825024385\r\n", node_type{resp3::type::big_number, 1UL, 0UL, {"3492890328409238509324850943850943825024385"}}, "big_number.node"}; - auto const in02 = expect{"(\r\n", int{}, "big_number.error (empty field)", aedis::error::empty_field}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - - test_async(ex, in01); - test_async(ex, in02); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_simple_string) -{ - net::io_context ioc; - std::optional ok1, ok2; - ok1 = "OK"; - ok2 = ""; - - auto in00 = expect{"+OK\r\n", std::string{"OK"}, "simple_string.string"}; - auto in01 = expect{"+\r\n", std::string{""}, "simple_string.string.empty"}; - auto in02 = expect>{"+OK\r\n", std::optional{"OK"}, "simple_string.optional"}; - auto in03 = expect>{"+\r\n", std::optional{""}, "simple_string.optional.empty"}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in00); - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - - test_async(ex, in00); - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_resp3) -{ - using map_type = std::map; - std::string const wire_map = "%rt\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n"; - - std::string const wire_ssp = "$?\r\n;d\r\nHell\r\n;5\r\no wor\r\n;1\r\nd\r\n;0\r\n"; - - net::io_context ioc; - auto const in01 = expect{"s11\r\n", int{}, "number.error", aedis::error::invalid_data_type}; - auto const in02 = expect{":adf\r\n", int{11}, "number.int", aedis::error::not_a_number}; - auto const in03 = expect{wire_map, map_type{}, "map.error", aedis::error::not_a_number}; - auto const in04 = expect{wire_ssp, std::string{}, "streamed_string_part.error", aedis::error::not_a_number}; - auto const in05 = expect{"$l\r\nhh\r\n", std::string{}, "blob_string.error", aedis::error::not_a_number}; - auto const in06 = expect{":\r\n", int{}, "number.error (empty field)", aedis::error::empty_field}; - auto const in07 = expect>{"#\r\n", std::optional{}, "bool.error", aedis::error::empty_field}; - auto const in08 = expect{",\r\n", std::string{}, "double.error (empty field)", aedis::error::empty_field}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - test_sync(ex, in05); - test_sync(ex, in06); - test_sync(ex, in07); - test_sync(ex, in08); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - test_async(ex, in05); - test_async(ex, in06); - test_async(ex, in07); - test_async(ex, in08); - - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(test_null) -{ - net::io_context ioc; - using op_type_01 = std::optional; - using op_type_02 = std::optional; - using op_type_03 = std::optional; - using op_type_04 = std::optional>; - using op_type_05 = std::optional>; - using op_type_06 = std::optional>; - using op_type_07 = std::optional>; - using op_type_08 = std::optional>; - using op_type_09 = std::optional>; - - auto const in01 = expect{S12, op_type_01{}, "null.optional.bool"}; - auto const in02 = expect{S12, op_type_02{}, "null.optional.int"}; - auto const in03 = expect{S12, op_type_03{}, "null.optional.string"}; - auto const in04 = expect{S12, op_type_04{}, "null.optional.vector"}; - auto const in05 = expect{S12, op_type_05{}, "null.optional.list"}; - auto const in06 = expect{S12, op_type_06{}, "null.optional.map"}; - auto const in07 = expect{S12, op_type_07{}, "null.optional.unordered_map"}; - auto const in08 = expect{S12, op_type_08{}, "null.optional.set"}; - auto const in09 = expect{S12, op_type_09{}, "null.optional.unordered_set"}; - - auto ex = ioc.get_executor(); - - test_sync(ex, in01); - test_sync(ex, in02); - test_sync(ex, in03); - test_sync(ex, in04); - test_sync(ex, in05); - test_sync(ex, in06); - test_sync(ex, in07); - test_sync(ex, in08); - test_sync(ex, in09); - - test_async(ex, in01); - test_async(ex, in02); - test_async(ex, in03); - test_async(ex, in04); - test_async(ex, in05); - test_async(ex, in06); - test_async(ex, in07); - test_async(ex, in08); - test_async(ex, in09); - ioc.run(); -} - -BOOST_AUTO_TEST_CASE(ignore_adapter_simple_error) -{ - net::io_context ioc; - std::string rbuffer; - - boost::system::error_code ec; - - test_stream ts {ioc}; - ts.append("-Error\r\n"); - resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec); - BOOST_CHECK_EQUAL(ec, aedis::error::resp3_simple_error); - BOOST_TEST(!rbuffer.empty()); -} - -BOOST_AUTO_TEST_CASE(ignore_adapter_blob_error) -{ - net::io_context ioc; - std::string rbuffer; - boost::system::error_code ec; - - test_stream ts {ioc}; - ts.append("!21\r\nSYNTAX invalid syntax\r\n"); - resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec); - BOOST_CHECK_EQUAL(ec, aedis::error::resp3_blob_error); - BOOST_TEST(!rbuffer.empty()); -} - -BOOST_AUTO_TEST_CASE(ignore_adapter_no_error) -{ - net::io_context ioc; - std::string rbuffer; - boost::system::error_code ec; - test_stream ts {ioc}; - ts.append(":10\r\n"); - resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec); - BOOST_TEST(!ec); - BOOST_TEST(rbuffer.empty()); -} - -BOOST_AUTO_TEST_CASE(all_tests) + test(ex, make_expected(S01, std::optional{}, 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{}, aedis::error::expects_resp3_map)); \ + test(ex, make_expected(S03, std::set{}, aedis::error::expects_resp3_set)); \ + test(ex, make_expected(S03, std::unordered_map{}, aedis::error::expects_resp3_map)); \ + test(ex, make_expected(S03, std::unordered_set{}, 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{}, 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{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{}, aedis::error::expects_resp3_aggregate)); \ + test(ex, make_expected(S10, std::map{}, aedis::error::expects_resp3_map)); \ + test(ex, make_expected(S10, std::set{}, aedis::error::expects_resp3_set)); \ + test(ex, make_expected(S10, std::unordered_map{}, aedis::error::expects_resp3_map)); \ + test(ex, make_expected(S10, std::unordered_set{}, 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{}, aedis::error::resp3_null));\ + test(ex, make_expected(S12, std::vector{}, 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{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{set_e1c})); \ + test(ex, make_expected(S27, std::vector{ {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{"OK"}));\ + test(ex, make_expected(S43, std::string{"OK"}));\ + test(ex, make_expected(S44, std::optional{""}));\ + 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{}, 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(make_blob_string(blob), node_type{resp3::type::blob_string, 1UL, 0UL, {blob}}));\ + +BOOST_AUTO_TEST_CASE(parser) { net::io_context ioc; @@ -801,6 +481,46 @@ BOOST_AUTO_TEST_CASE(all_tests) ioc.run(); } +BOOST_AUTO_TEST_CASE(ignore_adapter_simple_error) +{ + net::io_context ioc; + std::string rbuffer; + + boost::system::error_code ec; + + test_stream ts {ioc}; + ts.append(S28); + resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec); + BOOST_CHECK_EQUAL(ec, aedis::error::resp3_simple_error); + BOOST_TEST(!rbuffer.empty()); +} + +BOOST_AUTO_TEST_CASE(ignore_adapter_blob_error) +{ + net::io_context ioc; + std::string rbuffer; + boost::system::error_code ec; + + test_stream ts {ioc}; + ts.append(S36); + resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec); + BOOST_CHECK_EQUAL(ec, aedis::error::resp3_blob_error); + BOOST_TEST(!rbuffer.empty()); +} + +BOOST_AUTO_TEST_CASE(ignore_adapter_no_error) +{ + net::io_context ioc; + std::string rbuffer; + boost::system::error_code ec; + + test_stream ts {ioc}; + ts.append(S10); + resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec); + BOOST_TEST(!ec); + BOOST_TEST(rbuffer.empty()); +} + //----------------------------------------------------------------------------------- void check_error(char const* name, aedis::error ev) {