mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Makes flat_tree a proper container (#383)
Removes flat_tree::get_view()
Adds flat_tree{iterator, reverse_iterator, begin, end, rbegin, rend, data, operator[], at, front, back, size, empty}
close #362
This commit is contained in:
committed by
GitHub
parent
89e44dc017
commit
18ee72830b
@@ -122,7 +122,7 @@ auto receiver(std::shared_ptr<connection> conn) -> asio::awaitable<void>
|
||||
|
||||
// The response must be consumed without suspending the
|
||||
// coroutine i.e. without the use of async operations.
|
||||
for (auto const& elem : resp.value().get_view())
|
||||
for (auto const& elem : resp.value())
|
||||
std::cout << elem.value << "\n";
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -133,7 +133,7 @@ auto receiver(std::shared_ptr<connection> conn) -> asio::awaitable<void>
|
||||
|
||||
// The response must be consumed without suspending the
|
||||
// coroutine i.e. without the use of async operations.
|
||||
for (auto const& elem : resp.value().get_view())
|
||||
for (auto const& elem : resp.value())
|
||||
std::cout << elem.value << "\n";
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -72,7 +72,7 @@ auto receiver(std::shared_ptr<connection> conn) -> awaitable<void>
|
||||
|
||||
// The response must be consumed without suspending the
|
||||
// coroutine i.e. without the use of async operations.
|
||||
for (auto const& elem : resp.value().get_view())
|
||||
for (auto const& elem : resp.value())
|
||||
std::cout << elem.value << "\n";
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -72,7 +72,7 @@ auto receiver(std::shared_ptr<connection> conn) -> asio::awaitable<void>
|
||||
|
||||
// The response must be consumed without suspending the
|
||||
// coroutine i.e. without the use of async operations.
|
||||
for (auto const& elem : resp.value().get_view())
|
||||
for (auto const& elem : resp.value())
|
||||
std::cout << elem.value << "\n";
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
#include <boost/redis/resp3/tree.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
namespace boost::redis::resp3 {
|
||||
@@ -228,15 +230,19 @@ void flat_tree::notify_done()
|
||||
data_tmp_offset_ = data_.size;
|
||||
}
|
||||
|
||||
const node_view& flat_tree::at(std::size_t i) const
|
||||
{
|
||||
if (i >= size())
|
||||
BOOST_THROW_EXCEPTION(std::out_of_range("flat_tree::at"));
|
||||
return view_tree_[i];
|
||||
}
|
||||
|
||||
bool operator==(flat_tree const& a, flat_tree const& b)
|
||||
{
|
||||
// data is already taken into account by comparing the nodes.
|
||||
// Only committed nodes should be taken into account.
|
||||
auto a_nodes = a.get_view();
|
||||
auto b_nodes = b.get_view();
|
||||
return a_nodes.size() == b_nodes.size() &&
|
||||
std::equal(a_nodes.begin(), a_nodes.end(), b_nodes.begin()) &&
|
||||
a.total_msgs_ == b.total_msgs_;
|
||||
return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()) &&
|
||||
a.get_total_msgs() == b.get_total_msgs();
|
||||
}
|
||||
|
||||
} // namespace boost::redis::resp3
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <boost/core/span.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
namespace boost::redis {
|
||||
@@ -45,8 +46,12 @@ struct flat_buffer {
|
||||
* to obtain how many responses this object contains.
|
||||
*
|
||||
* Objects are typically created by the user and passed to @ref connection::async_exec
|
||||
* to be used as response containers. Call @ref get_view to access the actual RESP3 nodes.
|
||||
* Once populated, `flat_tree` can't be modified, except for @ref clear and assignment.
|
||||
* to be used as response containers. Once populated, they can be used as a const range
|
||||
* of @ref resp3::node_view objects. The usual random access range methods (like @ref at, @ref size or
|
||||
* @ref front) are provided. Once populated, `flat_tree` can't be modified,
|
||||
* except for @ref clear and assignment.
|
||||
*
|
||||
* `flat_tree` models `std::ranges::contiguous_range`.
|
||||
*
|
||||
* A `flat_tree` is conceptually similar to a pair of `std::vector` objects, one holding
|
||||
* @ref resp3::node_view objects, and another owning the the string data that these views
|
||||
@@ -54,6 +59,22 @@ struct flat_buffer {
|
||||
*/
|
||||
class flat_tree {
|
||||
public:
|
||||
/**
|
||||
* @brief The type of the iterators returned by @ref begin and @ref end.
|
||||
*
|
||||
* It is guaranteed to be a contiguous iterator. While this is currently a pointer,
|
||||
* users shouldn't rely on this fact, as the exact implementation may change between releases.
|
||||
*/
|
||||
using iterator = const node_view*;
|
||||
|
||||
/**
|
||||
* @brief The type of the iterators returned by @ref rbegin and @ref rend.
|
||||
*
|
||||
* As with @ref iterator, users should treat this type as an unspecified
|
||||
* contiguous iterator type rather than assuming a specific type.
|
||||
*/
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*
|
||||
@@ -70,7 +91,7 @@ public:
|
||||
* Constructs a tree by taking ownership of the nodes in `other`.
|
||||
*
|
||||
* @par Object lifetimes
|
||||
* References to the nodes and strings in `other` remain valid.
|
||||
* Iterators, pointers and references to the nodes and strings in `other` remain valid.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
@@ -95,8 +116,8 @@ public:
|
||||
* `other` is left in a valid but unspecified state.
|
||||
*
|
||||
* @par Object lifetimes
|
||||
* References to the nodes and strings in `other` remain valid.
|
||||
* References to the nodes and strings in `*this` are invalidated.
|
||||
* Iterators, pointers and references to the nodes and strings in `other` remain valid.
|
||||
* Iterators, pointers and references to the nodes and strings in `*this` are invalidated.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
@@ -110,16 +131,137 @@ public:
|
||||
* After the copy, `*this` and `other` have independent lifetimes (usual copy semantics).
|
||||
*
|
||||
* @par Object lifetimes
|
||||
* References to the nodes and strings in `*this` are invalidated.
|
||||
* Iterators, pointers and references to the nodes and strings in `*this` are invalidated.
|
||||
*
|
||||
* @par Exception safety
|
||||
* Basic guarantee. Memory allocations might throw.
|
||||
*/
|
||||
flat_tree& operator=(const flat_tree& other);
|
||||
|
||||
friend bool operator==(flat_tree const&, flat_tree const&);
|
||||
/**
|
||||
* @brief Returns an iterator to the first element of the node range.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns An iterator to the first node.
|
||||
*/
|
||||
iterator begin() const noexcept { return data(); }
|
||||
|
||||
friend bool operator!=(flat_tree const&, flat_tree const&);
|
||||
/**
|
||||
* @brief Returns an iterator past the last element in the node range.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns An iterator past the last element in the node range.
|
||||
*/
|
||||
iterator end() const noexcept { return data() + size(); }
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first element of the reversed node range.
|
||||
*
|
||||
* Allows iterating the range of nodes in reverse order.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns An iterator to the first node of the reversed range.
|
||||
*/
|
||||
reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator past the last element of the reversed node range.
|
||||
*
|
||||
* Allows iterating the range of nodes in reverse order.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns An iterator past the last element of the reversed node range.
|
||||
*/
|
||||
reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the node at the specified position (checked access).
|
||||
*
|
||||
* @par Exception safety
|
||||
* Strong guarantee. Throws `std::out_of_range` if `i >= size()`.
|
||||
*
|
||||
* @param i Position of the node to return.
|
||||
* @returns A reference to the node at position `i`.
|
||||
*/
|
||||
const node_view& at(std::size_t i) const;
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the node at the specified position (unchecked access).
|
||||
*
|
||||
* @par Precondition
|
||||
* `i < size()`.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @param i Position of the node to return.
|
||||
* @returns A reference to the node at position `i`.
|
||||
*/
|
||||
const node_view& operator[](std::size_t i) const noexcept { return get_view()[i]; }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the first node.
|
||||
*
|
||||
* @par Precondition
|
||||
* `!empty()`.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns A reference to the first node.
|
||||
*/
|
||||
const node_view& front() const noexcept { return get_view().front(); }
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the last node.
|
||||
*
|
||||
* @par Precondition
|
||||
* `!empty()`.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns A reference to the last node.
|
||||
*/
|
||||
const node_view& back() const noexcept { return get_view().back(); }
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying node storage.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns A pointer to the underlying node array.
|
||||
*/
|
||||
const node_view* data() const noexcept { return view_tree_.data(); }
|
||||
|
||||
/**
|
||||
* @brief Checks whether the tree is empty.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns `true` if the tree contains no nodes, `false` otherwise.
|
||||
*/
|
||||
bool empty() const noexcept { return size() == 0u; }
|
||||
|
||||
/**
|
||||
* @brief Returns the number of nodes in the tree.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns The number of nodes.
|
||||
*/
|
||||
std::size_t size() const noexcept { return node_tmp_offset_; }
|
||||
|
||||
/** @brief Reserves capacity for incoming data.
|
||||
*
|
||||
@@ -142,7 +284,7 @@ public:
|
||||
/** @brief Clears the tree so it contains no nodes.
|
||||
*
|
||||
* Calling this function removes every node, making
|
||||
* @ref get_view return empty and @ref get_total_msgs
|
||||
* the range contain no nodes, and @ref get_total_msgs
|
||||
* return zero. It does not modify the object's capacity.
|
||||
*
|
||||
* To re-use a `flat_tree` for several requests,
|
||||
@@ -189,17 +331,6 @@ public:
|
||||
*/
|
||||
auto data_capacity() const noexcept -> std::size_t { return data_.capacity; }
|
||||
|
||||
/** @brief Returns a vector with the nodes in the tree.
|
||||
*
|
||||
* This is the main way to access the contents of the tree.
|
||||
*
|
||||
* @par Exception safety
|
||||
* No-throw guarantee.
|
||||
*
|
||||
* @returns The nodes in the tree.
|
||||
*/
|
||||
span<const node_view> get_view() const noexcept { return {view_tree_.data(), node_tmp_offset_}; }
|
||||
|
||||
/** @brief Returns the number of memory reallocations that took place in the data buffer.
|
||||
*
|
||||
* This function returns how many reallocations in the data buffer were performed and
|
||||
@@ -226,6 +357,7 @@ public:
|
||||
private:
|
||||
template <class> friend class adapter::detail::general_aggregate;
|
||||
|
||||
span<const node_view> get_view() const noexcept { return {data(), size()}; }
|
||||
void notify_init();
|
||||
void notify_done();
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(exec_generic_flat_response)
|
||||
BOOST_TEST_REQUIRE(finished);
|
||||
|
||||
BOOST_TEST(resp.has_value());
|
||||
BOOST_TEST(resp->get_view().front().value == "PONG");
|
||||
BOOST_TEST(resp.value().front().value == "PONG");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -682,15 +682,15 @@ struct test_pubsub_state_restoration_impl {
|
||||
{
|
||||
// Checks for the expected subscriptions and patterns after restoration
|
||||
std::set<std::string_view> seen_channels, seen_patterns;
|
||||
for (auto it = resp_push.get_view().begin(); it != resp_push.get_view().end();) {
|
||||
for (auto it = resp_push.begin(); it != resp_push.end();) {
|
||||
// The root element should be a push
|
||||
BOOST_TEST_EQ(it->data_type, type::push);
|
||||
BOOST_TEST_GE(it->aggregate_size, 2u);
|
||||
BOOST_TEST(++it != resp_push.get_view().end());
|
||||
BOOST_TEST(++it != resp_push.end());
|
||||
|
||||
// The next element should be the message type
|
||||
std::string_view msg_type = it->value;
|
||||
BOOST_TEST(++it != resp_push.get_view().end());
|
||||
BOOST_TEST(++it != resp_push.end());
|
||||
|
||||
// The next element is the channel or pattern
|
||||
if (msg_type == "subscribe")
|
||||
@@ -699,7 +699,7 @@ struct test_pubsub_state_restoration_impl {
|
||||
seen_patterns.insert(it->value);
|
||||
|
||||
// Skip the rest of the nodes
|
||||
while (it != resp_push.get_view().end() && it->depth != 0u)
|
||||
while (it != resp_push.end() && it->depth != 0u)
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,19 +11,29 @@
|
||||
#include <boost/redis/resp3/type.hpp>
|
||||
|
||||
#include <boost/assert/source_location.hpp>
|
||||
#include <boost/config.hpp> // for a safe #include <version>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
|
||||
#include "print_node.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#if (__cpp_lib_ranges >= 201911L) && (__cpp_lib_concepts >= 202002L)
|
||||
#define BOOST_REDIS_TEST_RANGE_CONCEPTS
|
||||
#include <ranges>
|
||||
#endif
|
||||
|
||||
using boost::redis::adapter::adapt2;
|
||||
using boost::redis::adapter::result;
|
||||
using boost::redis::resp3::tree;
|
||||
@@ -70,11 +80,7 @@ void check_nodes(
|
||||
boost::span<const node_view> expected,
|
||||
boost::source_location loc = BOOST_CURRENT_LOCATION)
|
||||
{
|
||||
if (!BOOST_TEST_ALL_EQ(
|
||||
tree.get_view().begin(),
|
||||
tree.get_view().end(),
|
||||
expected.begin(),
|
||||
expected.end()))
|
||||
if (!BOOST_TEST_ALL_EQ(tree.begin(), tree.end(), expected.begin(), expected.end()))
|
||||
std::cerr << "Called from " << loc << std::endl;
|
||||
}
|
||||
|
||||
@@ -1090,6 +1096,233 @@ void test_move_assign_tmp()
|
||||
BOOST_TEST_EQ(t.get_total_msgs(), 2u);
|
||||
}
|
||||
|
||||
// --- Iterators ---
|
||||
// We can obtain iterators using begin() and end() and use them to iterate
|
||||
void test_iterators()
|
||||
{
|
||||
// Setup
|
||||
flat_tree t;
|
||||
add_nodes(t, "+node1\r\n");
|
||||
add_nodes(t, ":200\r\n");
|
||||
constexpr node_view node1{type::simple_string, 1u, 0u, "node1"};
|
||||
constexpr node_view node2{type::number, 1u, 0u, "200"};
|
||||
|
||||
// These methods are const
|
||||
const auto& tconst = t;
|
||||
auto it = tconst.begin();
|
||||
auto end = tconst.end();
|
||||
|
||||
// Iteration using iterators
|
||||
BOOST_TEST_NE(it, end);
|
||||
BOOST_TEST_EQ(*it, node1);
|
||||
BOOST_TEST_NE(++it, end);
|
||||
BOOST_TEST_EQ(*it, node2);
|
||||
BOOST_TEST_EQ(++it, end);
|
||||
|
||||
// Iteration using range for
|
||||
std::vector<node_view> nodes;
|
||||
for (const auto& n : t)
|
||||
nodes.push_back(n);
|
||||
constexpr std::array expected_nodes{node1, node2};
|
||||
BOOST_TEST_ALL_EQ(nodes.begin(), nodes.end(), expected_nodes.begin(), expected_nodes.end());
|
||||
}
|
||||
|
||||
// Empty ranges don't cause trouble
|
||||
void test_iterators_empty()
|
||||
{
|
||||
flat_tree t;
|
||||
BOOST_TEST_EQ(t.begin(), t.end());
|
||||
}
|
||||
|
||||
// Tmp area is not included in the range
|
||||
// More or less tested with the add_nodes tests
|
||||
void test_iterators_tmp()
|
||||
{
|
||||
parser p;
|
||||
flat_tree t;
|
||||
BOOST_TEST_NOT(parse_checked(t, p, "*2\r\n+hello\r\n"));
|
||||
BOOST_TEST_EQ(t.begin(), t.end());
|
||||
}
|
||||
|
||||
// The iterator should be contiguous
|
||||
#ifdef BOOST_REDIS_TEST_RANGE_CONCEPTS
|
||||
static_assert(std::contiguous_iterator<flat_tree::iterator>);
|
||||
#endif
|
||||
|
||||
// --- Reverse iterators ---
|
||||
// We can obtain iterators using rbegin() and rend() and use them to iterate
|
||||
void test_reverse_iterators()
|
||||
{
|
||||
// Setup
|
||||
flat_tree t;
|
||||
add_nodes(t, "+node1\r\n");
|
||||
add_nodes(t, ":200\r\n");
|
||||
|
||||
// These methods are const
|
||||
const auto& tconst = t;
|
||||
|
||||
constexpr node_view expected_nodes[] = {
|
||||
{type::number, 1u, 0u, "200" },
|
||||
{type::simple_string, 1u, 0u, "node1"},
|
||||
};
|
||||
BOOST_TEST_ALL_EQ(
|
||||
tconst.rbegin(),
|
||||
tconst.rend(),
|
||||
std::begin(expected_nodes),
|
||||
std::end(expected_nodes));
|
||||
}
|
||||
|
||||
// Empty ranges don't cause trouble
|
||||
void test_reverse_iterators_empty()
|
||||
{
|
||||
flat_tree t;
|
||||
BOOST_TEST(t.rbegin() == t.rend());
|
||||
}
|
||||
|
||||
// Tmp area is not included in the range
|
||||
void test_reverse_iterators_tmp()
|
||||
{
|
||||
parser p;
|
||||
flat_tree t;
|
||||
|
||||
// Add one full message and a partial one
|
||||
add_nodes(t, "*1\r\n+node1\r\n");
|
||||
BOOST_TEST_NOT(parse_checked(t, p, "*2\r\n+hello\r\n"));
|
||||
|
||||
// Only the full message appears in the reversed range
|
||||
constexpr node_view expected_nodes[] = {
|
||||
{type::simple_string, 1u, 1u, "node1"},
|
||||
{type::array, 1u, 0u, "" },
|
||||
};
|
||||
BOOST_TEST_ALL_EQ(t.rbegin(), t.rend(), std::begin(expected_nodes), std::end(expected_nodes));
|
||||
}
|
||||
|
||||
// --- at ---
|
||||
void test_at()
|
||||
{
|
||||
parser p;
|
||||
flat_tree t;
|
||||
|
||||
// Add one full message and a partial one
|
||||
add_nodes(t, "*1\r\n+node1\r\n");
|
||||
BOOST_TEST_NOT(parse_checked(t, p, "*2\r\n+hello\r\n"));
|
||||
|
||||
// Nodes in the range can be accessed with at()
|
||||
constexpr node_view n0{type::array, 1u, 0u, ""};
|
||||
constexpr node_view n1{type::simple_string, 1u, 1u, "node1"};
|
||||
BOOST_TEST_EQ(t.at(0u), n0);
|
||||
BOOST_TEST_EQ(t.at(1u), n1);
|
||||
|
||||
// Nodes in the tmp area are not considered in range
|
||||
BOOST_TEST_THROWS(t.at(2u), std::out_of_range);
|
||||
BOOST_TEST_THROWS(t.at(3u), std::out_of_range);
|
||||
|
||||
// Indices out of range throw
|
||||
BOOST_TEST_THROWS(t.at(4u), std::out_of_range);
|
||||
BOOST_TEST_THROWS(t.at(5u), std::out_of_range);
|
||||
BOOST_TEST_THROWS(t.at((std::numeric_limits<std::size_t>::max)()), std::out_of_range);
|
||||
}
|
||||
|
||||
// Empty ranges don't cause trouble
|
||||
void test_at_empty()
|
||||
{
|
||||
flat_tree t;
|
||||
BOOST_TEST_THROWS(t.at(0u), std::out_of_range);
|
||||
BOOST_TEST_THROWS(t.at(2u), std::out_of_range);
|
||||
BOOST_TEST_THROWS(t.at((std::numeric_limits<std::size_t>::max)()), std::out_of_range);
|
||||
}
|
||||
|
||||
// --- operator[], front, back ---
|
||||
void test_unchecked_access()
|
||||
{
|
||||
flat_tree t;
|
||||
add_nodes(t, "*2\r\n+node1\r\n+node2\r\n");
|
||||
|
||||
constexpr node_view n0{type::array, 2u, 0u, ""};
|
||||
constexpr node_view n1{type::simple_string, 1u, 1u, "node1"};
|
||||
constexpr node_view n2{type::simple_string, 1u, 1u, "node2"};
|
||||
|
||||
// operator []
|
||||
BOOST_TEST_EQ(t[0u], n0);
|
||||
BOOST_TEST_EQ(t[1u], n1);
|
||||
BOOST_TEST_EQ(t[2u], n2);
|
||||
|
||||
// Front and back
|
||||
BOOST_TEST_EQ(t.front(), n0);
|
||||
BOOST_TEST_EQ(t.back(), n2);
|
||||
}
|
||||
|
||||
// --- data ---
|
||||
void test_data()
|
||||
{
|
||||
flat_tree t;
|
||||
add_nodes(t, "*1\r\n+node1\r\n");
|
||||
|
||||
constexpr node_view expected_nodes[] = {
|
||||
{type::array, 1u, 0u, "" },
|
||||
{type::simple_string, 1u, 1u, "node1"},
|
||||
};
|
||||
|
||||
BOOST_TEST_NE(t.data(), nullptr);
|
||||
BOOST_TEST_ALL_EQ(t.data(), t.data() + 2u, std::begin(expected_nodes), std::end(expected_nodes));
|
||||
}
|
||||
|
||||
// Empty ranges don't cause trouble
|
||||
void test_data_empty()
|
||||
{
|
||||
flat_tree t;
|
||||
BOOST_TEST_EQ(t.data(), nullptr);
|
||||
}
|
||||
|
||||
// --- size and empty ---
|
||||
void test_size()
|
||||
{
|
||||
flat_tree t;
|
||||
add_nodes(t, "*1\r\n+node1\r\n");
|
||||
|
||||
BOOST_TEST_EQ(t.size(), 2u);
|
||||
BOOST_TEST_NOT(t.empty());
|
||||
}
|
||||
|
||||
void test_size_empty()
|
||||
{
|
||||
flat_tree t;
|
||||
|
||||
BOOST_TEST_EQ(t.size(), 0u);
|
||||
BOOST_TEST(t.empty());
|
||||
}
|
||||
|
||||
// Tmp area not taken into account
|
||||
void test_size_tmp()
|
||||
{
|
||||
parser p;
|
||||
flat_tree t;
|
||||
|
||||
// Add one full message and a partial one
|
||||
add_nodes(t, "*1\r\n+node1\r\n");
|
||||
BOOST_TEST_NOT(parse_checked(t, p, "*2\r\n+hello\r\n"));
|
||||
|
||||
BOOST_TEST_EQ(t.size(), 2u);
|
||||
BOOST_TEST_NOT(t.empty());
|
||||
}
|
||||
|
||||
void test_size_tmp_only()
|
||||
{
|
||||
parser p;
|
||||
flat_tree t;
|
||||
|
||||
// Add one partial message
|
||||
BOOST_TEST_NOT(parse_checked(t, p, "*2\r\n+hello\r\n"));
|
||||
|
||||
BOOST_TEST_EQ(t.size(), 0u);
|
||||
BOOST_TEST(t.empty());
|
||||
}
|
||||
|
||||
// The range should model contiguous range
|
||||
#ifdef BOOST_REDIS_TEST_RANGE_CONCEPTS
|
||||
static_assert(std::ranges::contiguous_range<flat_tree>);
|
||||
#endif
|
||||
|
||||
// --- Comparison ---
|
||||
void test_comparison_different()
|
||||
{
|
||||
@@ -1310,6 +1543,27 @@ int main()
|
||||
test_move_assign_both_empty();
|
||||
test_move_assign_tmp();
|
||||
|
||||
test_iterators();
|
||||
test_iterators_empty();
|
||||
test_iterators_tmp();
|
||||
|
||||
test_reverse_iterators();
|
||||
test_reverse_iterators_empty();
|
||||
test_reverse_iterators_tmp();
|
||||
|
||||
test_at();
|
||||
test_at_empty();
|
||||
|
||||
test_unchecked_access();
|
||||
|
||||
test_data();
|
||||
test_data_empty();
|
||||
|
||||
test_size();
|
||||
test_size_empty();
|
||||
test_size_tmp();
|
||||
test_size_tmp_only();
|
||||
|
||||
test_comparison_different();
|
||||
test_comparison_different_node_types();
|
||||
test_comparison_equal();
|
||||
|
||||
@@ -36,11 +36,7 @@ void test_success()
|
||||
std::vector<node_view> expected_nodes{
|
||||
{type::simple_string, 1u, 0u, "hello"},
|
||||
};
|
||||
BOOST_TEST_ALL_EQ(
|
||||
resp->get_view().begin(),
|
||||
resp->get_view().end(),
|
||||
expected_nodes.begin(),
|
||||
expected_nodes.end());
|
||||
BOOST_TEST_ALL_EQ(resp->begin(), resp->end(), expected_nodes.begin(), expected_nodes.end());
|
||||
}
|
||||
|
||||
// If an error of any kind appears, we set the overall result to error
|
||||
|
||||
Reference in New Issue
Block a user