/* * Copyright (C) 2018 Codership Oy * * This file is part of wsrep-lib. * * Wsrep-lib is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * Wsrep-lib 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 for more details. * * You should have received a copy of the GNU General Public License * along with wsrep-lib. If not, see . */ #ifndef WSREP_MOCK_CLIENT_CONTEXT_HPP #define WSREP_MOCK_CLIENT_CONTEXT_HPP #include "wsrep/client_state.hpp" #include "wsrep/mutex.hpp" #include "wsrep/compiler.hpp" #include "wsrep/client_service.hpp" #include "wsrep/condition_variable.hpp" #include "test_utils.hpp" namespace wsrep { class mock_client_state : public wsrep::client_state { public: mock_client_state(wsrep::server_state& server_state, wsrep::client_service& client_service, const wsrep::client_id& id, enum wsrep::client_state::mode mode) : wsrep::client_state(mutex_, cond_, server_state, client_service, id, mode) , mutex_() , cond_() { } ~mock_client_state() WSREP_OVERRIDE { if (transaction().active()) { (void)client_service().bf_rollback(); } } private: wsrep::default_mutex mutex_; wsrep::default_condition_variable cond_; public: private: }; class mock_client_service : public wsrep::client_service { public: mock_client_service(wsrep::mock_client_state* client_state) : wsrep::client_service() , is_autocommit_() , do_2pc_() // , fail_next_applying_() // , fail_next_toi_() , bf_abort_during_wait_() , bf_abort_during_fragment_removal_() , error_during_prepare_data_() , killed_before_certify_() , sync_point_enabled_() , sync_point_action_() , bytes_generated_() , client_state_(client_state) , will_replay_called_() , replays_() , unordered_replays_() , aborts_() { } mock_client_service(const mock_client_service&) = delete; mock_client_service& operator=(const mock_client_service&) = delete; int bf_rollback() WSREP_OVERRIDE; bool interrupted(wsrep::unique_lock&) const WSREP_OVERRIDE { return killed_before_certify_; } void emergency_shutdown() WSREP_OVERRIDE { ++aborts_; } int remove_fragments() WSREP_OVERRIDE { if (bf_abort_during_fragment_removal_) { client_state_->before_rollback(); client_state_->after_rollback(); return 1; } else { return 0; } } void will_replay() WSREP_OVERRIDE { will_replay_called_ = true; } void signal_replayed() WSREP_OVERRIDE { } enum wsrep::provider::status replay() WSREP_OVERRIDE; enum wsrep::provider::status replay_unordered() WSREP_OVERRIDE { unordered_replays_++; return wsrep::provider::success; } void wait_for_replayers( wsrep::unique_lock& lock) WSREP_OVERRIDE { lock.unlock(); if (bf_abort_during_wait_) { wsrep_test::bf_abort_unordered(*client_state_); } lock.lock(); } int prepare_data_for_replication() WSREP_OVERRIDE { if (error_during_prepare_data_) { return 1; } static const char buf[1] = { 1 }; wsrep::const_buffer data = wsrep::const_buffer(buf, 1); return client_state_->append_data(data); } void cleanup_transaction() WSREP_OVERRIDE { } size_t bytes_generated() const WSREP_OVERRIDE { return bytes_generated_; } bool statement_allowed_for_streaming() const WSREP_OVERRIDE { return true; } int prepare_fragment_for_replication(wsrep::mutable_buffer& buffer, size_t& position) WSREP_OVERRIDE { if (error_during_prepare_data_) { return 1; } static const char buf[1] = { 1 }; buffer.push_back(&buf[0], &buf[1]); wsrep::const_buffer data(buffer.data(), buffer.size()); position = buffer.size(); return client_state_->append_data(data); } void store_globals() WSREP_OVERRIDE { } void reset_globals() WSREP_OVERRIDE { } enum wsrep::provider::status commit_by_xid() WSREP_OVERRIDE { return wsrep::provider::success; } bool is_explicit_xa() WSREP_OVERRIDE { return false; } bool is_prepared_xa() WSREP_OVERRIDE { return false; } bool is_xa_rollback() WSREP_OVERRIDE { return false; } void debug_sync(const char* sync_point) WSREP_OVERRIDE { if (sync_point_enabled_ == sync_point) { switch (sync_point_action_) { case spa_bf_abort_unordered: wsrep_test::bf_abort_unordered(*client_state_); break; case spa_bf_abort_ordered: wsrep_test::bf_abort_ordered(*client_state_); break; } } } void debug_crash(const char*) WSREP_OVERRIDE { // Not going to do this while unit testing } // // Knobs to tune the behavior // bool is_autocommit_; bool do_2pc_; // bool fail_next_applying_; // bool fail_next_toi_; bool bf_abort_during_wait_; bool bf_abort_during_fragment_removal_; bool error_during_prepare_data_; bool killed_before_certify_; std::string sync_point_enabled_; enum sync_point_action { spa_bf_abort_unordered, spa_bf_abort_ordered } sync_point_action_; size_t bytes_generated_; // // Verifying the state // bool will_replay_called() const { return will_replay_called_; } size_t replays() const { return replays_; } size_t unordered_replays() const { return unordered_replays_; } size_t aborts() const { return aborts_; } private: wsrep::mock_client_state* client_state_; bool will_replay_called_; size_t replays_; size_t unordered_replays_; size_t aborts_; }; class mock_client : public mock_client_state , public mock_client_service { public: mock_client(wsrep::server_state& server_state, const wsrep::client_id& id, enum wsrep::client_state::mode mode) : mock_client_state(server_state, *this, id, mode) , mock_client_service(static_cast(this)) { } int after_row() { bytes_generated_++; return wsrep::client_state::after_row(); } }; } #endif // WSREP_MOCK_CLIENT_CONTEXT_HPP