mirror of
https://github.com/boostorg/mysql.git
synced 2026-02-13 12:32:18 +00:00
Now network tests use an interface
Made network tests simpler and much more flexible
This commit is contained in:
@@ -56,6 +56,7 @@ add_test(
|
||||
add_executable(
|
||||
mysql_integrationtests
|
||||
integration/metadata_validator.cpp
|
||||
integration/network_functions.cpp
|
||||
integration/handshake.cpp
|
||||
integration/query.cpp
|
||||
integration/prepare_statement.cpp
|
||||
|
||||
@@ -19,112 +19,68 @@ using mysql::tcp_prepared_statement;
|
||||
namespace
|
||||
{
|
||||
|
||||
struct ExecuteStatementTest : public NetworkTest<> {};
|
||||
|
||||
// Iterator version
|
||||
using listit = std::forward_list<value>::const_iterator;
|
||||
struct ExecuteStatementIteratorTraits
|
||||
{
|
||||
static tcp_resultset sync_errc(const tcp_prepared_statement& stmt, listit first, listit last,
|
||||
error_code& errc, error_info& info)
|
||||
{
|
||||
return stmt.execute(first, last, errc, info);
|
||||
}
|
||||
|
||||
static tcp_resultset sync_exc(const tcp_prepared_statement& stmt, listit first, listit last)
|
||||
{
|
||||
return stmt.execute(first, last);
|
||||
}
|
||||
|
||||
template <typename CompletionToken>
|
||||
static auto async(const tcp_prepared_statement& stmt, listit first, listit last, CompletionToken&& token)
|
||||
{
|
||||
return stmt.async_execute(first, last, std::forward<CompletionToken>(token));
|
||||
}
|
||||
};
|
||||
|
||||
struct ExecuteStatementIteratorTest : public NetworkTest<ExecuteStatementIteratorTraits> {};
|
||||
|
||||
TEST_P(ExecuteStatementIteratorTest, OkNoParams)
|
||||
TEST_P(ExecuteStatementTest, Iterator_OkNoParams)
|
||||
{
|
||||
std::forward_list<value> params;
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
|
||||
auto result = GetParam().fun(stmt, params.begin(), params.end()); // execute
|
||||
auto result = GetParam()->execute_statement(stmt, params.begin(), params.end()); // execute
|
||||
result.validate_no_error();
|
||||
EXPECT_TRUE(result.value.valid());
|
||||
}
|
||||
|
||||
TEST_P(ExecuteStatementIteratorTest, OkWithParams)
|
||||
TEST_P(ExecuteStatementTest, Iterator_OkWithParams)
|
||||
{
|
||||
std::forward_list<value> params { value("item"), value(42) };
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||
auto result = GetParam().fun(stmt, params.begin(), params.end());
|
||||
auto result = GetParam()->execute_statement(stmt, params.begin(), params.end());
|
||||
result.validate_no_error();
|
||||
EXPECT_TRUE(result.value.valid());
|
||||
}
|
||||
|
||||
TEST_P(ExecuteStatementIteratorTest, MismatchedNumParams)
|
||||
TEST_P(ExecuteStatementTest, Iterator_MismatchedNumParams)
|
||||
{
|
||||
std::forward_list<value> params { value("item") };
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||
auto result = GetParam().fun(stmt, params.begin(), params.end());
|
||||
auto result = GetParam()->execute_statement(stmt, params.begin(), params.end());
|
||||
result.validate_error(Error::wrong_num_params, {"param", "2", "1", "statement", "execute"});
|
||||
EXPECT_FALSE(result.value.valid());
|
||||
}
|
||||
|
||||
// TODO: is there any way of making server return an error here?
|
||||
|
||||
MYSQL_NETWORK_TEST_SUITE(ExecuteStatementIteratorTest);
|
||||
|
||||
// Container version
|
||||
struct ExecuteStatementContainerTraits
|
||||
{
|
||||
static tcp_resultset sync_errc(const tcp_prepared_statement& stmt, const std::vector<value>& v,
|
||||
error_code& errc, error_info& info)
|
||||
{
|
||||
return stmt.execute(v, errc, info);
|
||||
}
|
||||
|
||||
static tcp_resultset sync_exc(const tcp_prepared_statement& stmt, const std::vector<value>& v)
|
||||
{
|
||||
return stmt.execute(v);
|
||||
}
|
||||
|
||||
template <typename CompletionToken>
|
||||
static auto async(const tcp_prepared_statement& stmt, const std::vector<value>& v, CompletionToken&& token)
|
||||
{
|
||||
return stmt.async_execute(v, std::forward<CompletionToken>(token));
|
||||
}
|
||||
};
|
||||
|
||||
struct ExecuteStatementContainerTest : public NetworkTest<ExecuteStatementContainerTraits> {};
|
||||
|
||||
TEST_P(ExecuteStatementContainerTest, OkNoParams)
|
||||
TEST_P(ExecuteStatementTest, Container_OkNoParams)
|
||||
{
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
|
||||
auto result = GetParam().fun(stmt, std::vector<value>()); // execute
|
||||
auto result = GetParam()->execute_statement(stmt, std::vector<value>()); // execute
|
||||
result.validate_no_error();
|
||||
EXPECT_TRUE(result.value.valid());
|
||||
}
|
||||
|
||||
TEST_P(ExecuteStatementContainerTest, OkWithParams)
|
||||
TEST_P(ExecuteStatementTest, Container_OkWithParams)
|
||||
{
|
||||
std::vector<value> params { value("item"), value(42) };
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||
auto result = GetParam().fun(stmt, params);
|
||||
auto result = GetParam()->execute_statement(stmt, params);
|
||||
result.validate_no_error();
|
||||
EXPECT_TRUE(result.value.valid());
|
||||
}
|
||||
|
||||
TEST_P(ExecuteStatementContainerTest, MismatchedNumParams)
|
||||
TEST_P(ExecuteStatementTest, Container_MismatchedNumParams)
|
||||
{
|
||||
std::vector<value> params { value("item") };
|
||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||
auto result = GetParam().fun(stmt, params);
|
||||
auto result = GetParam()->execute_statement(stmt, params);
|
||||
result.validate_error(Error::wrong_num_params, {"param", "2", "1", "statement", "execute"});
|
||||
EXPECT_FALSE(result.value.valid());
|
||||
}
|
||||
|
||||
MYSQL_NETWORK_TEST_SUITE(ExecuteStatementContainerTest);
|
||||
MYSQL_NETWORK_TEST_SUITE(ExecuteStatementTest);
|
||||
|
||||
// Other containers
|
||||
struct ExecuteStatementOtherContainersTest : IntegTestAfterHandshake {};
|
||||
|
||||
TEST_F(ExecuteStatementOtherContainersTest, NoParams_CanUseNoStatementParamsVariable)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <functional>
|
||||
#include "test_common.hpp"
|
||||
#include "metadata_validator.hpp"
|
||||
#include "network_functions.hpp"
|
||||
|
||||
namespace mysql
|
||||
{
|
||||
@@ -187,128 +188,13 @@ struct IntegTestAfterHandshake : IntegTest
|
||||
IntegTestAfterHandshake() { handshake(); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct NetworkResult
|
||||
template <typename BaseType = IntegTestAfterHandshake>
|
||||
struct NetworkTest : public BaseType,
|
||||
public testing::WithParamInterface<network_functions*>
|
||||
{
|
||||
error_code errc;
|
||||
error_info info;
|
||||
T value;
|
||||
|
||||
void validate_no_error() const
|
||||
{
|
||||
ASSERT_EQ(errc, error_code());
|
||||
EXPECT_EQ(info, error_info());
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
error_code expected_errc,
|
||||
const std::vector<std::string>& expected_msg
|
||||
) const
|
||||
{
|
||||
EXPECT_EQ(errc, expected_errc);
|
||||
validate_string_contains(info.message(), expected_msg);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
Error expected_errc,
|
||||
const std::vector<std::string>& expected_msg
|
||||
) const
|
||||
{
|
||||
validate_error(detail::make_error_code(expected_errc), expected_msg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct NetworkFunction : named_param
|
||||
{
|
||||
std::string name;
|
||||
std::function<NetworkResult<R>(Args...)> fun;
|
||||
|
||||
template <typename Callable>
|
||||
NetworkFunction(std::string name, Callable&& cb):
|
||||
name(std::move(name)), fun(std::forward<Callable>(cb)) {}
|
||||
};
|
||||
|
||||
template <typename TraitsType>
|
||||
class get_network_function_type
|
||||
{
|
||||
template <typename R, typename... Args>
|
||||
static NetworkFunction<R, Args...> helper(R(*)(Args...));
|
||||
public:
|
||||
using type = decltype(helper(&TraitsType::sync_exc));
|
||||
};
|
||||
|
||||
template <typename TraitsType>
|
||||
using traits_network_function = typename get_network_function_type<TraitsType>::type;
|
||||
|
||||
template <typename... T>
|
||||
struct printer
|
||||
{
|
||||
static_assert(std::is_same_v<T..., int>);
|
||||
};
|
||||
|
||||
template <typename TraitsType, typename R, typename... Args>
|
||||
auto make_network_functions_impl(R(*)(Args...))
|
||||
{
|
||||
using NetResultType = NetworkResult<R>;
|
||||
using NetFunType = NetworkFunction<R, Args...>;
|
||||
auto sync_errc = [](Args... args) {
|
||||
NetResultType res;
|
||||
res.errc = detail::make_error_code(Error::no);
|
||||
res.info.set_message("Error info not cleared correctly");
|
||||
res.value = TraitsType::sync_errc(std::forward<Args>(args)..., res.errc, res.info);
|
||||
return res;
|
||||
};
|
||||
auto sync_exc = [](Args... args) {
|
||||
NetResultType res;
|
||||
try
|
||||
{
|
||||
res.value = TraitsType::sync_exc(std::forward<Args>(args)...);
|
||||
}
|
||||
catch (const boost::system::system_error& err)
|
||||
{
|
||||
res.errc = err.code();
|
||||
res.info.set_message(err.what());
|
||||
}
|
||||
return res;
|
||||
};
|
||||
auto async = [](Args... args) {
|
||||
std::promise<NetResultType> prom;
|
||||
TraitsType::async(std::forward<Args>(args)..., [&prom](error_code errc, error_info info, auto retval) {
|
||||
prom.set_value(NetResultType{errc, std::move(info), std::move(retval)});
|
||||
});
|
||||
return prom.get_future().get();
|
||||
};
|
||||
return std::vector<NetFunType>{
|
||||
NetFunType("sync_errc", sync_errc),
|
||||
NetFunType("sync_exc", sync_exc),
|
||||
NetFunType("async", async)
|
||||
};
|
||||
}
|
||||
|
||||
template <typename TraitsType>
|
||||
std::vector<traits_network_function<TraitsType>>
|
||||
make_network_functions()
|
||||
{
|
||||
return make_network_functions_impl<TraitsType>(&TraitsType::sync_exc);
|
||||
}
|
||||
|
||||
template <typename TraitsType, typename BaseTest=IntegTestAfterHandshake>
|
||||
struct NetworkTest :
|
||||
public BaseTest,
|
||||
public testing::WithParamInterface<traits_network_function<TraitsType>>
|
||||
{
|
||||
using traits_type = TraitsType;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#define MYSQL_NETWORK_TEST_SUITE(TestSuiteName) \
|
||||
INSTANTIATE_TEST_SUITE_P(Default, TestSuiteName, testing::ValuesIn( \
|
||||
make_network_functions<TestSuiteName::traits_type>() \
|
||||
), test_name_generator)
|
||||
|
||||
|
||||
|
||||
#endif /* TEST_INTEGRATION_INTEGRATION_TEST_COMMON_HPP_ */
|
||||
|
||||
158
test/integration/network_functions.cpp
Normal file
158
test/integration/network_functions.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "network_functions.hpp"
|
||||
#include <future>
|
||||
|
||||
using namespace mysql::test;
|
||||
using mysql::tcp_prepared_statement;
|
||||
using mysql::tcp_resultset;
|
||||
using mysql::tcp_connection;
|
||||
using mysql::error_info;
|
||||
using mysql::error_code;
|
||||
using mysql::detail::make_error_code;
|
||||
using mysql::Error;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename Callable>
|
||||
auto sync_errc_impl(Callable&& cb) {
|
||||
using R = decltype(cb(std::declval<error_code&>(), std::declval<error_info&>()));
|
||||
network_result<R> res;
|
||||
res.err = make_error_code(Error::no);
|
||||
res.info.set_message("Error info not cleared correctly");
|
||||
res.value = cb(res.err, res.info);
|
||||
return res;
|
||||
}
|
||||
|
||||
class sync_errc : public network_functions
|
||||
{
|
||||
public:
|
||||
network_result<tcp_prepared_statement> prepare_statement(
|
||||
tcp_connection& conn,
|
||||
std::string_view statement
|
||||
) override
|
||||
{
|
||||
return sync_errc_impl([&conn, statement](error_code& err, error_info& info) {
|
||||
return conn.prepare_statement(statement, err, info);
|
||||
});
|
||||
}
|
||||
network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement& stmt,
|
||||
value_list_it params_first,
|
||||
value_list_it params_last
|
||||
) override
|
||||
{
|
||||
return sync_errc_impl([=, &stmt](error_code& err, error_info& info) {
|
||||
return stmt.execute(params_first, params_last, err, info);
|
||||
});
|
||||
}
|
||||
network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement& stmt,
|
||||
const std::vector<mysql::value>& values
|
||||
) override
|
||||
{
|
||||
return sync_errc_impl([&stmt, &values](error_code& err, error_info& info) {
|
||||
return stmt.execute(values, err, info);
|
||||
});
|
||||
}
|
||||
};
|
||||
sync_errc sync_errc_obj;
|
||||
|
||||
template <typename Callable>
|
||||
auto sync_exc_impl(Callable&& cb) {
|
||||
using R = decltype(cb());
|
||||
network_result<R> res;
|
||||
try
|
||||
{
|
||||
res.value = cb();
|
||||
}
|
||||
catch (const boost::system::system_error& err)
|
||||
{
|
||||
res.err = err.code();
|
||||
res.info.set_message(err.what());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
class sync_exc : public network_functions
|
||||
{
|
||||
public:
|
||||
network_result<tcp_prepared_statement> prepare_statement(
|
||||
tcp_connection& conn,
|
||||
std::string_view statement
|
||||
) override
|
||||
{
|
||||
return sync_exc_impl([&conn, statement] {
|
||||
return conn.prepare_statement(statement);
|
||||
});
|
||||
}
|
||||
network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement& stmt,
|
||||
value_list_it params_first,
|
||||
value_list_it params_last
|
||||
) override
|
||||
{
|
||||
return sync_exc_impl([&]{
|
||||
return stmt.execute(params_first, params_last);
|
||||
});
|
||||
}
|
||||
network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement& stmt,
|
||||
const std::vector<mysql::value>& values
|
||||
) override
|
||||
{
|
||||
return sync_exc_impl([&stmt, &values] {
|
||||
return stmt.execute(values);
|
||||
});
|
||||
}
|
||||
};
|
||||
sync_exc sync_exc_obj;
|
||||
|
||||
template <typename R, typename Callable>
|
||||
network_result<R> async_impl(Callable&& cb) {
|
||||
std::promise<network_result<R>> prom;
|
||||
cb([&prom](error_code errc, error_info info, auto retval) {
|
||||
prom.set_value(network_result<R>{errc, std::move(info), std::move(retval)});
|
||||
});
|
||||
return prom.get_future().get();
|
||||
}
|
||||
|
||||
class async : public network_functions
|
||||
{
|
||||
public:
|
||||
network_result<tcp_prepared_statement> prepare_statement(
|
||||
tcp_connection& conn,
|
||||
std::string_view statement
|
||||
) override
|
||||
{
|
||||
return async_impl<tcp_prepared_statement>([&conn, statement](auto&& token) {
|
||||
return conn.async_prepare_statement(statement, std::forward<decltype(token)>(token));
|
||||
});
|
||||
}
|
||||
network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement& stmt,
|
||||
value_list_it params_first,
|
||||
value_list_it params_last
|
||||
) override
|
||||
{
|
||||
return async_impl<tcp_resultset>([&](auto&& token) {
|
||||
return stmt.async_execute(params_first, params_last, std::forward<decltype(token)>(token));
|
||||
});
|
||||
}
|
||||
network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement& stmt,
|
||||
const std::vector<mysql::value>& values
|
||||
) override
|
||||
{
|
||||
return async_impl<tcp_resultset>([&](auto&& token) {
|
||||
return stmt.async_execute(values, std::forward<decltype(token)>(token));
|
||||
});
|
||||
}
|
||||
};
|
||||
async async_obj;
|
||||
|
||||
}
|
||||
|
||||
// Visible stuff
|
||||
mysql::test::network_functions* mysql::test::sync_errc_network_functions = &sync_errc_obj;
|
||||
mysql::test::network_functions* mysql::test::sync_exc_network_functions = &sync_exc_obj;
|
||||
mysql::test::network_functions* mysql::test::async_network_functions = &async_obj;
|
||||
79
test/integration/network_functions.hpp
Normal file
79
test/integration/network_functions.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#ifndef TEST_INTEGRATION_NETWORK_FUNCTIONS_HPP_
|
||||
#define TEST_INTEGRATION_NETWORK_FUNCTIONS_HPP_
|
||||
|
||||
#include "mysql/connection.hpp"
|
||||
#include "test_common.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
#include <forward_list>
|
||||
|
||||
namespace mysql
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct network_result
|
||||
{
|
||||
error_code err;
|
||||
error_info info;
|
||||
T value;
|
||||
|
||||
void validate_no_error() const
|
||||
{
|
||||
ASSERT_EQ(err, error_code());
|
||||
EXPECT_EQ(info, error_info());
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
error_code expected_errc,
|
||||
const std::vector<std::string>& expected_msg
|
||||
) const
|
||||
{
|
||||
EXPECT_EQ(err, expected_errc);
|
||||
validate_string_contains(info.message(), expected_msg);
|
||||
}
|
||||
|
||||
void validate_error(
|
||||
Error expected_errc,
|
||||
const std::vector<std::string>& expected_msg
|
||||
) const
|
||||
{
|
||||
validate_error(detail::make_error_code(expected_errc), expected_msg);
|
||||
}
|
||||
};
|
||||
|
||||
using value_list_it = std::forward_list<value>::const_iterator;
|
||||
|
||||
class network_functions
|
||||
{
|
||||
public:
|
||||
virtual ~network_functions() = default;
|
||||
virtual network_result<tcp_prepared_statement> prepare_statement(
|
||||
tcp_connection&, std::string_view statement) = 0;
|
||||
virtual network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement&, value_list_it params_first, value_list_it params_last) = 0;
|
||||
virtual network_result<tcp_resultset> execute_statement(
|
||||
tcp_prepared_statement&, const std::vector<value>&) = 0;
|
||||
};
|
||||
|
||||
extern network_functions* sync_errc_network_functions;
|
||||
extern network_functions* sync_exc_network_functions;
|
||||
extern network_functions* async_network_functions;
|
||||
|
||||
inline network_functions* all_network_functions [] = {
|
||||
sync_errc_network_functions,
|
||||
sync_exc_network_functions,
|
||||
async_network_functions
|
||||
};
|
||||
|
||||
#define MYSQL_NETWORK_TEST_SUITE(TestSuiteName) \
|
||||
INSTANTIATE_TEST_SUITE_P(Default, TestSuiteName, testing::ValuesIn( \
|
||||
all_network_functions \
|
||||
), test_name_generator)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* TEST_INTEGRATION_NETWORK_FUNCTIONS_HPP_ */
|
||||
@@ -17,34 +17,14 @@ using mysql::tcp_connection;
|
||||
namespace
|
||||
{
|
||||
|
||||
struct PrepareStatementTraits
|
||||
{
|
||||
static tcp_prepared_statement sync_errc(tcp_connection& conn, std::string_view statement,
|
||||
error_code& err, error_info& info)
|
||||
{
|
||||
return conn.prepare_statement(statement, err, info);
|
||||
}
|
||||
|
||||
static tcp_prepared_statement sync_exc(tcp_connection& conn, std::string_view statement)
|
||||
{
|
||||
return conn.prepare_statement(statement);
|
||||
}
|
||||
|
||||
template <typename CompletionToken>
|
||||
static auto async(tcp_connection& conn, std::string_view statement, CompletionToken&& token)
|
||||
{
|
||||
return conn.async_prepare_statement(statement, std::forward<CompletionToken>(token));
|
||||
}
|
||||
};
|
||||
|
||||
struct PrepareStatementTest : public NetworkTest<PrepareStatementTraits>
|
||||
struct PrepareStatementTest : public NetworkTest<>
|
||||
{
|
||||
};
|
||||
|
||||
// sync errc
|
||||
TEST_P(PrepareStatementTest, OkNoParams)
|
||||
{
|
||||
auto stmt = GetParam().fun(conn, "SELECT * FROM empty_table");
|
||||
auto stmt = GetParam()->prepare_statement(conn, "SELECT * FROM empty_table");
|
||||
stmt.validate_no_error();
|
||||
ASSERT_TRUE(stmt.value.valid());
|
||||
EXPECT_GT(stmt.value.id(), 0);
|
||||
@@ -53,7 +33,7 @@ TEST_P(PrepareStatementTest, OkNoParams)
|
||||
|
||||
TEST_P(PrepareStatementTest, OkWithParams)
|
||||
{
|
||||
auto stmt = GetParam().fun(conn, "SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||
auto stmt = GetParam()->prepare_statement(conn, "SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||
stmt.validate_no_error();
|
||||
ASSERT_TRUE(stmt.value.valid());
|
||||
EXPECT_GT(stmt.value.id(), 0);
|
||||
@@ -62,7 +42,7 @@ TEST_P(PrepareStatementTest, OkWithParams)
|
||||
|
||||
TEST_P(PrepareStatementTest, Error)
|
||||
{
|
||||
auto stmt = GetParam().fun(conn, "SELECT * FROM bad_table WHERE id IN (?, ?)");
|
||||
auto stmt = GetParam()->prepare_statement(conn, "SELECT * FROM bad_table WHERE id IN (?, ?)");
|
||||
stmt.validate_error(Error::no_such_table, {"table", "doesn't exist", "bad_table"});
|
||||
EXPECT_FALSE(stmt.value.valid());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user