391 lines
14 KiB
C++
391 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License, version 2.0,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is also distributed with certain software (including
|
|
* but not limited to OpenSSL) that is licensed under separate terms,
|
|
* as designated in a particular file or component or in included license
|
|
* documentation. The authors of MySQL hereby grant you an additional
|
|
* permission to link the program and your derivative works with the
|
|
* separately licensed software that they have included with MySQL.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License, version 2.0, for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "unittest/gunit/xplugin/xcl/protocol_t.h"
|
|
|
|
namespace xcl {
|
|
namespace test {
|
|
|
|
using Msg_types = ::testing::Types<
|
|
::Mysqlx::Session::AuthenticateStart,
|
|
::Mysqlx::Session::AuthenticateContinue, ::Mysqlx::Session::Reset,
|
|
::Mysqlx::Session::Close, ::Mysqlx::Sql::StmtExecute, ::Mysqlx::Crud::Find,
|
|
::Mysqlx::Crud::Insert, ::Mysqlx::Crud::Update, ::Mysqlx::Crud::Delete,
|
|
::Mysqlx::Crud::CreateView, ::Mysqlx::Crud::ModifyView,
|
|
::Mysqlx::Crud::DropView, ::Mysqlx::Expect::Open, ::Mysqlx::Expect::Close,
|
|
::Mysqlx::Connection::CapabilitiesGet,
|
|
::Mysqlx::Connection::CapabilitiesSet, ::Mysqlx::Connection::Close,
|
|
::Mysqlx::Prepare::Prepare, ::Mysqlx::Prepare::Execute,
|
|
::Mysqlx::Prepare::Deallocate>;
|
|
|
|
TYPED_TEST_CASE(Xcl_protocol_impl_tests_with_msg, Msg_types);
|
|
|
|
TYPED_TEST(Xcl_protocol_impl_tests_with_msg,
|
|
connection_send_successful_send_handler_consumed_msg_was_ignored) {
|
|
this->setup_required_fields_in_message();
|
|
|
|
const auto id = this->m_sut->add_send_message_handler(
|
|
this->m_mock_handlers.get_mock_lambda_send_message_handler());
|
|
|
|
{
|
|
InSequence s;
|
|
EXPECT_CALL(this->m_mock_handlers,
|
|
send_message_handler(_, TestFixture::Msg_descriptor::get_id(),
|
|
Ref(*this->m_message.get())))
|
|
.WillOnce(Return(Handler_result::Consumed));
|
|
|
|
EXPECT_CALL(*this->m_mock_connection, write(_, _))
|
|
.WillOnce(
|
|
Invoke([this](const uchar *data, const std::size_t size) -> XError {
|
|
return this->assert_write_size(data, size);
|
|
}));
|
|
}
|
|
|
|
auto error = this->m_sut->send(*this->m_message.get());
|
|
|
|
ASSERT_FALSE(error);
|
|
|
|
this->m_sut->remove_send_message_handler(id);
|
|
}
|
|
|
|
TYPED_TEST(Xcl_protocol_impl_tests_with_msg,
|
|
connection_send_successful_send_handler_ignored_msg) {
|
|
this->setup_required_fields_in_message();
|
|
|
|
const auto id = this->m_sut->add_send_message_handler(
|
|
this->m_mock_handlers.get_mock_lambda_send_message_handler());
|
|
|
|
{
|
|
InSequence s;
|
|
EXPECT_CALL(this->m_mock_handlers,
|
|
send_message_handler(_, TestFixture::Msg_descriptor::get_id(),
|
|
Ref(*this->m_message.get())))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
|
|
EXPECT_CALL(*this->m_mock_connection, write(_, _))
|
|
.WillOnce(
|
|
Invoke([this](const uchar *data, const std::size_t size) -> XError {
|
|
return this->assert_write_size(data, size);
|
|
}));
|
|
}
|
|
|
|
auto error = this->m_sut->send(*this->m_message.get());
|
|
|
|
ASSERT_FALSE(error);
|
|
|
|
this->m_sut->remove_send_message_handler(id);
|
|
}
|
|
|
|
TYPED_TEST(Xcl_protocol_impl_tests_with_msg,
|
|
connection_send_successful_multiple_send_handlers_called) {
|
|
const auto first_handler_didnt_consume = Handler_result::Continue;
|
|
|
|
this->assert_multiple_handlers(first_handler_didnt_consume);
|
|
}
|
|
|
|
TYPED_TEST(Xcl_protocol_impl_tests_with_msg,
|
|
connection_send_successful_first_handler_doesnt_blok_others) {
|
|
const auto first_handler_consumed = Handler_result::Consumed;
|
|
|
|
this->assert_multiple_handlers(first_handler_consumed);
|
|
}
|
|
|
|
TYPED_TEST(Xcl_protocol_impl_tests_with_msg, connection_send_successful) {
|
|
this->setup_required_fields_in_message();
|
|
|
|
{
|
|
InSequence s;
|
|
|
|
EXPECT_CALL(*this->m_mock_connection, write(_, _))
|
|
.WillOnce(
|
|
Invoke([this](const uchar *data, const std::size_t size) -> XError {
|
|
return this->assert_write_size(data, size);
|
|
}));
|
|
}
|
|
|
|
auto error = this->m_sut->send(*this->m_message.get());
|
|
|
|
ASSERT_FALSE(error);
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_fails_at_header) {
|
|
const int64 expected_error_code = 3000;
|
|
const int32 expected_payload_size = 10;
|
|
XProtocol::Server_message_type_id out_id;
|
|
XError out_error;
|
|
|
|
expect_read_header(::Mysqlx::ServerMessages::OK, expected_payload_size,
|
|
expected_error_code);
|
|
auto result = m_sut->recv_single_message(&out_id, &out_error);
|
|
ASSERT_FALSE(result.get());
|
|
ASSERT_EQ(expected_error_code, out_error.error());
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_fails_at_payload) {
|
|
const int expected_error_code = 3000;
|
|
const int32 expected_payload_size = 10;
|
|
XProtocol::Server_message_type_id out_id;
|
|
XError out_error;
|
|
|
|
expect_read_header(::Mysqlx::ServerMessages::OK, expected_payload_size);
|
|
|
|
EXPECT_CALL(*m_mock_connection, read(_, expected_payload_size))
|
|
.WillOnce(Return(XError(expected_error_code, "")));
|
|
auto result = m_sut->recv_single_message(&out_id, &out_error);
|
|
ASSERT_FALSE(result.get());
|
|
ASSERT_EQ(expected_error_code, out_error.error());
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_large_message) {
|
|
std::string message_payload(1024 * 64, 'x');
|
|
int offset = 0;
|
|
expect_read_header(::Mysqlx::ServerMessages::OK,
|
|
static_cast<int32>(message_payload.size()));
|
|
|
|
{
|
|
InSequence s;
|
|
const int k_internal_buffer = 4 * 1024;
|
|
size_t count_data_in_reads = 0;
|
|
|
|
while (count_data_in_reads < message_payload.size()) {
|
|
EXPECT_CALL(*m_mock_connection, read(_, k_internal_buffer))
|
|
.WillOnce(Invoke(
|
|
[&offset, message_payload](
|
|
uchar *data, const std::size_t data_length MY_ATTRIBUTE(
|
|
(unused))) -> XError {
|
|
auto i_start = message_payload.begin() + offset;
|
|
std::copy(i_start, i_start + data_length, data);
|
|
offset += data_length;
|
|
return {};
|
|
}));
|
|
count_data_in_reads += k_internal_buffer;
|
|
}
|
|
}
|
|
XProtocol::Server_message_type_id out_id;
|
|
XError out_error;
|
|
auto result = m_sut->recv_single_message(&out_id, &out_error);
|
|
ASSERT_TRUE(result.get());
|
|
ASSERT_EQ(0, out_error.error());
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_unknown_msg_type) {
|
|
const int32 invalid_message_id = 255;
|
|
const int32 expected_payload_size = 10;
|
|
XProtocol::Server_message_type_id out_id;
|
|
XError out_error;
|
|
|
|
expect_read_header(invalid_message_id, expected_payload_size);
|
|
|
|
EXPECT_CALL(*m_mock_connection, read(_, expected_payload_size))
|
|
.WillOnce(Return(XError()));
|
|
auto result = m_sut->recv_single_message(&out_id, &out_error);
|
|
ASSERT_FALSE(result.get());
|
|
ASSERT_EQ(CR_MALFORMED_PACKET, out_error.error());
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_in_single_read_op) {
|
|
const int32 expected_payload_size = 0;
|
|
const std::string expected_message_name = "Mysqlx.Ok";
|
|
XProtocol::Server_message_type_id out_id;
|
|
XError out_error;
|
|
|
|
expect_read_header(::Mysqlx::ServerMessages::OK, expected_payload_size);
|
|
|
|
auto result = m_sut->recv_single_message(&out_id, &out_error);
|
|
ASSERT_TRUE(result.get());
|
|
ASSERT_EQ(expected_message_name, result->GetTypeName());
|
|
ASSERT_FALSE(out_error);
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_in_two_read_op) {
|
|
using Auth_continue = ::Mysqlx::Session::AuthenticateContinue;
|
|
const auto message = Server_message<Auth_continue>::make_required();
|
|
|
|
assert_read_message(message);
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_in_two_read_op_call_one_handler) {
|
|
using Auth_continue_desc =
|
|
Server_message<::Mysqlx::Session::AuthenticateContinue>;
|
|
|
|
const auto message = Auth_continue_desc::make_required();
|
|
|
|
m_sut->add_received_message_handler(
|
|
m_mock_handlers.get_mock_lambda_received_message_handler());
|
|
EXPECT_CALL(m_mock_handlers,
|
|
received_message_handler(_, Auth_continue_desc::get_id(),
|
|
Cmp_msg(message)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
|
|
assert_read_message(message);
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_in_two_read_op_call_multiple_handlers) {
|
|
using Auth_continue_desc =
|
|
Server_message<::Mysqlx::Session::AuthenticateContinue>;
|
|
|
|
const auto message = Auth_continue_desc::make_required();
|
|
StrictMock<Mock_handlers> mock_handlers[2];
|
|
|
|
m_sut->add_received_message_handler(
|
|
m_mock_handlers.get_mock_lambda_received_message_handler());
|
|
m_sut->add_received_message_handler(
|
|
mock_handlers[0].get_mock_lambda_received_message_handler());
|
|
m_sut->add_received_message_handler(
|
|
mock_handlers[1].get_mock_lambda_received_message_handler());
|
|
|
|
// The call sequence must be opposite to push sequence
|
|
{
|
|
InSequence s;
|
|
EXPECT_CALL(mock_handlers[1],
|
|
received_message_handler(_, Auth_continue_desc::get_id(),
|
|
Cmp_msg(message)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
EXPECT_CALL(mock_handlers[0],
|
|
received_message_handler(_, Auth_continue_desc::get_id(),
|
|
Cmp_msg(message)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
EXPECT_CALL(m_mock_handlers,
|
|
received_message_handler(_, Auth_continue_desc::get_id(),
|
|
Cmp_msg(message)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
}
|
|
|
|
assert_read_message(message);
|
|
}
|
|
|
|
TEST_F(
|
|
Xcl_protocol_impl_tests,
|
|
recv_in_two_read_op_call_first_handler_consumes_next_msg_on_all_handlers) {
|
|
using Auth_continue_desc =
|
|
Server_message<::Mysqlx::Session::AuthenticateContinue>;
|
|
using Error_desc = Server_message<::Mysqlx::Error>;
|
|
|
|
const auto msg_continue = Auth_continue_desc::make_required();
|
|
const auto msg_error = Error_desc::make_required();
|
|
StrictMock<Mock_handlers> mock_handlers[2];
|
|
|
|
m_sut->add_received_message_handler(
|
|
m_mock_handlers.get_mock_lambda_received_message_handler());
|
|
m_sut->add_received_message_handler(
|
|
mock_handlers[0].get_mock_lambda_received_message_handler());
|
|
m_sut->add_received_message_handler(
|
|
mock_handlers[1].get_mock_lambda_received_message_handler());
|
|
|
|
// The call sequence must be opposite to push sequence
|
|
{
|
|
InSequence s1;
|
|
// Get first transmitted message
|
|
// and consume it inside the handler
|
|
EXPECT_CALL(mock_handlers[1],
|
|
received_message_handler(_, Auth_continue_desc::get_id(),
|
|
Cmp_msg(msg_continue)))
|
|
.WillOnce(Return(Handler_result::Consumed));
|
|
|
|
// Message was consumed, thus read_single_message
|
|
// is going to read next one from XConnection.
|
|
// We are expecting that next message is going to
|
|
// processed on all handlers without consuming it !
|
|
EXPECT_CALL(
|
|
mock_handlers[1],
|
|
received_message_handler(_, Error_desc::get_id(), Cmp_msg(msg_error)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
EXPECT_CALL(
|
|
mock_handlers[0],
|
|
received_message_handler(_, Error_desc::get_id(), Cmp_msg(msg_error)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
EXPECT_CALL(
|
|
m_mock_handlers,
|
|
received_message_handler(_, Error_desc::get_id(), Cmp_msg(msg_error)))
|
|
.WillOnce(Return(Handler_result::Continue));
|
|
}
|
|
|
|
{
|
|
InSequence s2;
|
|
expect_read_message(msg_continue); // Message to be read at first read op
|
|
assert_read_message(msg_error); // Message to be read at second read op
|
|
}
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_ok_fails_on_other_msg) {
|
|
using Auth_continue_desc =
|
|
Server_message<::Mysqlx::Session::AuthenticateContinue>;
|
|
|
|
const auto msg_continue = Auth_continue_desc::make_required();
|
|
|
|
expect_read_message(msg_continue);
|
|
auto error = m_sut->recv_ok();
|
|
|
|
ASSERT_EQ(CR_MALFORMED_PACKET, error.error());
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_ok_fails_error_msg) {
|
|
using Error_desc = Server_message<::Mysqlx::Error>;
|
|
|
|
const uint32 expected_error_code = 23332;
|
|
const char *expected_msg = "expected error message";
|
|
const char *expected_sql_state = "expected sql state";
|
|
|
|
auto msg_error = Error_desc::make_required();
|
|
|
|
msg_error.set_code(expected_error_code);
|
|
msg_error.set_msg(expected_msg);
|
|
msg_error.set_sql_state(expected_sql_state);
|
|
msg_error.set_severity(::Mysqlx::Error::FATAL);
|
|
|
|
expect_read_message(msg_error);
|
|
XError error = m_sut->recv_ok();
|
|
|
|
ASSERT_EQ(expected_error_code, error.error());
|
|
ASSERT_STREQ(expected_msg, error.what());
|
|
ASSERT_STREQ(expected_sql_state, error.sql_state());
|
|
ASSERT_TRUE(error.is_fatal());
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_ok) {
|
|
using Ok_desc = Server_message<::Mysqlx::Ok>;
|
|
|
|
auto msg_ok = Ok_desc::make_required();
|
|
|
|
expect_read_message_without_payload(msg_ok);
|
|
auto error = m_sut->recv_ok();
|
|
|
|
ASSERT_FALSE(error);
|
|
}
|
|
|
|
TEST_F(Xcl_protocol_impl_tests, recv_resultset) {
|
|
Mock_query_result *query_result = new Mock_query_result();
|
|
static XQuery_result::Metadata metadata;
|
|
|
|
EXPECT_CALL(m_mock_factory, create_result_raw(_, _, _))
|
|
.WillOnce(Return(query_result));
|
|
|
|
auto result = m_sut->recv_resultset();
|
|
|
|
ASSERT_EQ(query_result, result.get());
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace xcl
|