/* Copyright (c) 2015, 2018, 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 #include #include "plugin/x/ngs/include/ngs/error_code.h" #include "plugin/x/ngs/include/ngs/protocol/protocol_protobuf.h" #include "plugin/x/src/expect/expect.h" #include "plugin/x/src/expect/expect_stack.h" #include "plugin/x/src/xpl_error.h" static const int EXPECT_NO_ERROR = 1; namespace xpl { namespace test { static ngs::Error_code success; static ngs::Error_code simulate_instruction(Expectation_stack &xs, int8_t mid, ngs::Error_code fail) { ngs::Error_code err = xs.pre_client_stmt(mid); if (!err) { // execution would come here err = fail; xs.post_client_stmt(mid, fail); } return err; } static ngs::Error_code simulate_close(Expectation_stack &xs) { ngs::Error_code err = xs.pre_client_stmt(Mysqlx::ClientMessages::EXPECT_CLOSE); if (!err) { err = xs.close(); xs.post_client_stmt(Mysqlx::ClientMessages::EXPECT_CLOSE, err); } return err; } static ngs::Error_code simulate_open(Expectation_stack &xs, const Mysqlx::Expect::Open &open) { ngs::Error_code err = xs.pre_client_stmt(Mysqlx::ClientMessages::EXPECT_OPEN); if (!err) { err = xs.open(open); xs.post_client_stmt(Mysqlx::ClientMessages::EXPECT_OPEN, err); } return err; } static Mysqlx::Expect::Open Inherit() { Mysqlx::Expect::Open open; open.set_op(Mysqlx::Expect::Open::EXPECT_CTX_COPY_PREV); return open; } static Mysqlx::Expect::Open Noerror() { Mysqlx::Expect::Open open; open.set_op(Mysqlx::Expect::Open::EXPECT_CTX_EMPTY); Mysqlx::Expect::Open::Condition *cond = open.mutable_cond()->Add(); cond->set_condition_key(EXPECT_NO_ERROR); cond->set_op(Mysqlx::Expect::Open::Condition::EXPECT_OP_SET); return open; } static Mysqlx::Expect::Open Plain() { Mysqlx::Expect::Open open; open.set_op(Mysqlx::Expect::Open::EXPECT_CTX_EMPTY); return open; } static Mysqlx::Expect::Open Inherit_and_clear_noerror() { Mysqlx::Expect::Open open = Inherit(); Mysqlx::Expect::Open::Condition *cond = open.mutable_cond()->Add(); cond->set_condition_key(EXPECT_NO_ERROR); cond->set_op(Mysqlx::Expect::Open::Condition::EXPECT_OP_UNSET); return open; } static Mysqlx::Expect::Open Inherit_and_add_noerror() { Mysqlx::Expect::Open open = Inherit(); Mysqlx::Expect::Open::Condition *cond = open.mutable_cond()->Add(); cond->set_condition_key(EXPECT_NO_ERROR); cond->set_op(Mysqlx::Expect::Open::Condition::EXPECT_OP_SET); return open; } #define EXPECT_ok_cmd() \ ASSERT_EQ(success, simulate_instruction(xs, 1, ngs::Error_code())) #define EXPECT_error_cmd() \ ASSERT_EQ(ngs::Error_code(1234, "whatever"), \ simulate_instruction(xs, 2, ngs::Error_code(1234, "whatever"))) #define EXPECT_fail(error_code, exp) \ ASSERT_EQ(ngs::Error_code(error_code, "Expectation failed: " exp), \ simulate_instruction(xs, 3, ngs::Error_code())) #define EXPECT_open_ok(msg) ASSERT_EQ(success, simulate_open(xs, msg)) #define EXPECT_open_fail(msg, error_code, exp) \ ASSERT_EQ(ngs::Error_code(error_code, "Expectation failed: " exp), \ simulate_open(xs, msg)) #define EXPECT_close_ok() ASSERT_EQ(success, simulate_close(xs)) #define EXPECT_close_fail(error_code, exp) \ ASSERT_EQ(ngs::Error_code(error_code, "Expectation failed: " exp), \ simulate_close(xs)) TEST(expect, plain) { Expectation_stack xs; // open expect block EXPECT_open_ok(Plain()); // ok command 1 EXPECT_ok_cmd(); EXPECT_ok_cmd(); // error command 2 EXPECT_error_cmd(); // subsequent cmds succeed normally EXPECT_ok_cmd(); EXPECT_error_cmd(); // now close the block EXPECT_close_ok(); EXPECT_ok_cmd(); // close too much should fail EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); } TEST(expect, noerror) { Expectation_stack xs; // open expect block EXPECT_open_ok(Noerror()); // ok command 1 EXPECT_ok_cmd(); // error command 2 EXPECT_error_cmd(); // now everything fails EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); // now close the block EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); // now commands should succeed again EXPECT_ok_cmd(); } TEST(expect, noerror_in_noerror) { Expectation_stack xs; // fail in the inner block EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_ok_cmd(); EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); // fail in the outer block EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); { EXPECT_open_fail(Noerror(), ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_ok_cmd(); EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); // fail in inner block again EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_ok_cmd(); } TEST(expect, plain_in_noerror) { Expectation_stack xs; // fail in the inner block EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_ok_cmd(); EXPECT_close_ok(); } EXPECT_close_ok(); EXPECT_ok_cmd(); EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); // fail in the outer block EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); { EXPECT_open_fail(Plain(), ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_ok_cmd(); EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); // fail in inner block again EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_add_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_clear_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_ok_cmd(); EXPECT_close_ok(); } EXPECT_close_ok(); EXPECT_ok_cmd(); } TEST(expect, noerror_in_plain) { Expectation_stack xs; // fail in the inner block EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); { EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_ok_cmd(); EXPECT_close_ok(); EXPECT_ok_cmd(); EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); // fail in the outer block EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); EXPECT_error_cmd(); { EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); EXPECT_close_ok(); } EXPECT_ok_cmd(); EXPECT_close_ok(); EXPECT_ok_cmd(); EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open"), xs.close()); // fail in inner block again EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_ok_cmd(); EXPECT_close_ok(); } EXPECT_close_ok(); EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_add_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_ok_cmd(); EXPECT_close_ok(); EXPECT_ok_cmd(); } TEST(expect, nested_inheriting) { Expectation_stack xs; EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_add_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_open_ok(Noerror()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_clear_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_ok_cmd(); EXPECT_close_ok(); } EXPECT_close_ok(); EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_add_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); EXPECT_close_fail(ER_X_EXPECT_NO_ERROR_FAILED, "no_error"); } EXPECT_close_ok(); EXPECT_open_ok(Plain()); EXPECT_ok_cmd(); { EXPECT_open_ok(Inherit_and_clear_noerror()); EXPECT_ok_cmd(); EXPECT_error_cmd(); EXPECT_ok_cmd(); EXPECT_close_ok(); } EXPECT_close_ok(); } TEST(expect, invalid) { { Expectation exp; EXPECT_EQ( ngs::Error_code(ER_X_EXPECT_BAD_CONDITION, "Unknown condition key"), exp.set(1234, "1")); } { Expectation exp; EXPECT_EQ(ngs::Error_code(), exp.set(1, "")); EXPECT_EQ(true, exp.fail_on_error()); EXPECT_EQ(ngs::Error_code(), exp.set(1, "1")); EXPECT_EQ(true, exp.fail_on_error()); EXPECT_EQ(ngs::Error_code(), exp.set(1, "0")); EXPECT_FALSE(exp.fail_on_error()); EXPECT_EQ(ngs::Error_code(ER_X_EXPECT_BAD_CONDITION_VALUE, "Invalid value 'bla' for expectation no_error"), exp.set(1, "bla")); EXPECT_FALSE(exp.fail_on_error()); } } class Expect_surprise : public Expect_condition { public: Expect_surprise(const uint32_t k, const std::string &v) : Expect_condition(k, ""), m_surprise_value(v) {} Expect_condition_ptr clone() override { return Expect_condition_ptr{new Expect_surprise(key(), m_surprise_value)}; } ngs::Error_code check_if_error() override { const std::string valid_value = "true"; if (m_surprise_value == valid_value) return {}; return ngs::Error_code(ER_X_EXPECT_NO_ERROR_FAILED, ""); } void set(const std::string &flag) { m_surprise_value = flag; } const std::string &value() override { return m_surprise_value; } private: std::string m_surprise_value; }; TEST(expect, condition) { Expectation expect; Expect_surprise *surp = new Expect_surprise(1234, "false"); ASSERT_FALSE(expect.check_conditions()); expect.add_condition(std::unique_ptr{surp}); ASSERT_TRUE(expect.check_conditions()); surp->set("true"); ASSERT_FALSE(expect.check_conditions()); surp->set("false"); { Expectation copy(expect); ASSERT_TRUE(expect.check_conditions()); ASSERT_TRUE(copy.check_conditions()); expect.unset(1234, ""); ASSERT_FALSE(expect.check_conditions()); ASSERT_TRUE(copy.check_conditions()); } } TEST(expect, condition_unset) { const uint32_t expect_key = 1234u; Expectation expect; Expect_surprise *surp[4]; ASSERT_FALSE(expect.check_conditions()); for (int i = 0; i < 4; ++i) { surp[i] = new Expect_surprise(expect_key, "true"); expect.add_condition(std::unique_ptr{surp[i]}); } ASSERT_FALSE(expect.check_conditions()); surp[0]->set("false"); ASSERT_TRUE(expect.check_conditions()); const auto cond_value = surp[0]->value(); expect.unset(surp[0]->key(), cond_value); ASSERT_FALSE(expect.check_conditions()); surp[1]->set("false"); ASSERT_TRUE(expect.check_conditions()); expect.unset(expect_key, ""); ASSERT_FALSE(expect.check_conditions()); } } // namespace test } // namespace xpl