2
0
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:
ruben
2020-02-14 17:37:21 +00:00
parent ab4715525b
commit 16347315fe
6 changed files with 262 additions and 202 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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_ */

View 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;

View 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_ */

View File

@@ -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());
}