mirror of
https://github.com/boostorg/mysql.git
synced 2026-01-28 07:22:26 +00:00
Fixed a problem that caused pfr_by_name<T> to fail compilation when T has no fields under older clang/libc++ versions. close #313 close #483
258 lines
7.0 KiB
C++
258 lines
7.0 KiB
C++
//
|
|
// Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
|
|
#ifndef BOOST_MYSQL_TEST_UNIT_INCLUDE_TEST_UNIT_ALGO_TEST_HPP
|
|
#define BOOST_MYSQL_TEST_UNIT_INCLUDE_TEST_UNIT_ALGO_TEST_HPP
|
|
|
|
#include <boost/mysql/character_set.hpp>
|
|
#include <boost/mysql/diagnostics.hpp>
|
|
#include <boost/mysql/error_code.hpp>
|
|
|
|
#include <boost/mysql/detail/next_action.hpp>
|
|
|
|
#include <boost/mysql/impl/internal/protocol/capabilities.hpp>
|
|
#include <boost/mysql/impl/internal/protocol/db_flavor.hpp>
|
|
#include <boost/mysql/impl/internal/sansio/connection_state_data.hpp>
|
|
|
|
#include <boost/config.hpp>
|
|
#include <boost/core/span.hpp>
|
|
#include <boost/optional/optional.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
#include "test_common/source_location.hpp"
|
|
|
|
namespace boost {
|
|
namespace mysql {
|
|
namespace test {
|
|
|
|
// A type-erased reference to an algorithm
|
|
class any_algo_ref
|
|
{
|
|
template <class Algo>
|
|
static detail::next_action do_resume(
|
|
void* self,
|
|
detail::connection_state_data& st,
|
|
diagnostics& diag,
|
|
error_code ec
|
|
)
|
|
{
|
|
return static_cast<Algo*>(self)->resume(st, diag, ec);
|
|
}
|
|
|
|
using fn_t = detail::next_action (*)(void*, detail::connection_state_data&, diagnostics&, error_code);
|
|
|
|
void* algo_{};
|
|
fn_t fn_{};
|
|
|
|
public:
|
|
template <class Algo, class = typename std::enable_if<!std::is_same<Algo, any_algo_ref>::value>::type>
|
|
any_algo_ref(Algo& algo) noexcept : algo_(&algo), fn_(&do_resume<Algo>)
|
|
{
|
|
}
|
|
|
|
detail::next_action resume(detail::connection_state_data& st, diagnostics& diag, error_code ec)
|
|
{
|
|
return fn_(algo_, st, diag, ec);
|
|
}
|
|
};
|
|
|
|
class BOOST_ATTRIBUTE_NODISCARD algo_test
|
|
{
|
|
struct step_t
|
|
{
|
|
detail::next_action_type type;
|
|
std::vector<std::uint8_t> bytes;
|
|
error_code result;
|
|
bool check;
|
|
};
|
|
|
|
std::vector<step_t> steps_;
|
|
|
|
// Monitor connection_state_data for relevant changes
|
|
struct expected_state_changes_t
|
|
{
|
|
boost::optional<detail::connection_status> status;
|
|
boost::optional<detail::db_flavor> flavor;
|
|
boost::optional<detail::capabilities> current_capabilities;
|
|
boost::optional<std::uint32_t> connection_id;
|
|
boost::optional<bool> tls_supported;
|
|
boost::optional<bool> tls_active;
|
|
boost::optional<bool> backslash_escapes;
|
|
boost::optional<character_set> current_charset;
|
|
} state_changes_;
|
|
|
|
class state_checker;
|
|
|
|
static void handle_read(detail::connection_state_data& st, const step_t& op);
|
|
|
|
detail::next_action run_algo_until_step(
|
|
any_algo_ref algo,
|
|
detail::connection_state_data& st,
|
|
diagnostics& diag,
|
|
std::size_t num_steps_to_run
|
|
) const;
|
|
|
|
algo_test& add_step(
|
|
detail::next_action_type act_type,
|
|
std::vector<std::uint8_t> bytes,
|
|
error_code ec,
|
|
bool check = true
|
|
);
|
|
|
|
void check_impl(
|
|
any_algo_ref algo,
|
|
detail::connection_state_data& st,
|
|
error_code expected_ec,
|
|
const diagnostics& expected_diag,
|
|
source_location loc
|
|
) const;
|
|
|
|
std::size_t num_steps() const { return steps_.size(); }
|
|
|
|
void check_network_errors_impl(
|
|
any_algo_ref algo,
|
|
detail::connection_state_data& st,
|
|
std::size_t step_number,
|
|
source_location loc
|
|
) const;
|
|
|
|
public:
|
|
algo_test() = default;
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_write(std::vector<std::uint8_t> bytes, error_code result = {})
|
|
{
|
|
return add_step(detail::next_action_type::write, std::move(bytes), result);
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_any_write(error_code result = {})
|
|
{
|
|
return add_step(detail::next_action_type::write, {}, result, false);
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_read(std::vector<std::uint8_t> result_bytes)
|
|
{
|
|
return add_step(detail::next_action_type::read, std::move(result_bytes), error_code());
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_read(error_code result) { return add_step(detail::next_action_type::read, {}, result); }
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_ssl_handshake(error_code result = {})
|
|
{
|
|
return add_step(detail::next_action_type::ssl_handshake, {}, result);
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_ssl_shutdown(error_code result = {})
|
|
{
|
|
return add_step(detail::next_action_type::ssl_shutdown, {}, result);
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& expect_close(error_code result = {})
|
|
{
|
|
return add_step(detail::next_action_type::close, {}, result);
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_status(detail::connection_status expected)
|
|
{
|
|
state_changes_.status = expected;
|
|
return *this;
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_capabilities(detail::capabilities expected)
|
|
{
|
|
state_changes_.current_capabilities = expected;
|
|
return *this;
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_connection_id(std::uint32_t expected)
|
|
{
|
|
state_changes_.connection_id = expected;
|
|
return *this;
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_flavor(detail::db_flavor expected)
|
|
{
|
|
state_changes_.flavor = expected;
|
|
return *this;
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_tls_active(bool expected)
|
|
{
|
|
state_changes_.tls_active = expected;
|
|
return *this;
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_current_charset(character_set expected)
|
|
{
|
|
state_changes_.current_charset = expected;
|
|
return *this;
|
|
}
|
|
|
|
BOOST_ATTRIBUTE_NODISCARD
|
|
algo_test& will_set_backslash_escapes(bool expected)
|
|
{
|
|
state_changes_.backslash_escapes = expected;
|
|
return *this;
|
|
}
|
|
|
|
template <class AlgoFixture>
|
|
void check(
|
|
AlgoFixture& fix,
|
|
error_code expected_ec = {},
|
|
const diagnostics& expected_diag = {},
|
|
source_location loc = BOOST_MYSQL_CURRENT_LOCATION
|
|
) const
|
|
{
|
|
check_impl(fix.algo, fix.st, expected_ec, expected_diag, loc);
|
|
}
|
|
|
|
template <class AlgoFixture>
|
|
void check_network_errors(source_location loc = BOOST_MYSQL_CURRENT_LOCATION) const
|
|
{
|
|
for (std::size_t i = 0; i < num_steps(); ++i)
|
|
{
|
|
AlgoFixture fix;
|
|
check_network_errors_impl(fix.algo, fix.st, i, loc);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct algo_fixture_base
|
|
{
|
|
static constexpr std::size_t default_max_buffsize = 4u * 1024u * 1024u;
|
|
|
|
detail::connection_state_data st;
|
|
|
|
algo_fixture_base(std::size_t max_buffer_size = default_max_buffsize)
|
|
: st(max_buffer_size, max_buffer_size)
|
|
{
|
|
st.status = detail::connection_status::ready;
|
|
st.write_buffer.push_back(0xff); // Check that we clear the write buffer at each step
|
|
}
|
|
};
|
|
|
|
} // namespace test
|
|
} // namespace mysql
|
|
} // namespace boost
|
|
|
|
#endif
|