LogCabin
Client/MockClientImpl.cc
Go to the documentation of this file.
00001 /* Copyright (c) 2012 Stanford University
00002  * Copyright (c) 2014-2015 Diego Ongaro
00003  *
00004  * Permission to use, copy, modify, and distribute this software for any
00005  * purpose with or without fee is hereby granted, provided that the above
00006  * copyright notice and this permission notice appear in all copies.
00007  *
00008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
00009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
00011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00015  */
00016 
00017 #include "Core/Debug.h"
00018 #include "Core/ProtoBuf.h"
00019 #include "Core/STLUtil.h"
00020 #include "Client/MockClientImpl.h"
00021 #include "Tree/ProtoBuf.h"
00022 
00023 namespace LogCabin {
00024 namespace Client {
00025 
00026 namespace PC = LogCabin::Protocol::Client;
00027 
00028 namespace {
00029 
00030 template<typename T>
00031 struct RAIISwap {
00032     RAIISwap(T& a, T& b)
00033         : a(a)
00034         , b(b)
00035     {
00036         std::swap(a, b);
00037     }
00038     ~RAIISwap()
00039     {
00040         std::swap(a, b);
00041     }
00042     T& a;
00043     T& b;
00044 };
00045 
00046 typedef RAIISwap<std::shared_ptr<TestingCallbacks>> CallbackSwap;
00047 
00048 /**
00049  * This class intercepts LeaderRPC calls from ClientImpl.
00050  * It's used to mock out the Tree RPCs by processing them directly.
00051  */
00052 class TreeLeaderRPC : public LeaderRPCBase {
00053   public:
00054     explicit TreeLeaderRPC(std::shared_ptr<TestingCallbacks> callbacks)
00055         : mutex()
00056         , callbacks(callbacks)
00057         , tree()
00058     {
00059     }
00060     Status call(OpCode opCode,
00061               const google::protobuf::Message& request,
00062               google::protobuf::Message& response,
00063               TimePoint timeout) {
00064         std::lock_guard<std::recursive_mutex> lockGuard(mutex);
00065         if (opCode == OpCode::STATE_MACHINE_QUERY) {
00066             PC::StateMachineQuery::Request qrequest;
00067             qrequest.CopyFrom(request);
00068             auto& qresponse =
00069                 static_cast<PC::StateMachineQuery::Response&>(response);
00070             auto localCallbacks = std::make_shared<TestingCallbacks>();
00071             // set to noop for recursive calls, then restore
00072             CallbackSwap s(callbacks, localCallbacks);
00073             if (localCallbacks->stateMachineQuery(qrequest, qresponse))
00074                 return Status::OK;
00075             qresponse.Clear();
00076             if (timeout < Clock::now())
00077                 return Status::TIMEOUT;
00078             if (qrequest.has_tree()) {
00079                 LogCabin::Tree::ProtoBuf::readOnlyTreeRPC(
00080                     tree, qrequest.tree(), *qresponse.mutable_tree());
00081                 return Status::OK;
00082             }
00083         } else if (opCode == OpCode::STATE_MACHINE_COMMAND) {
00084             PC::StateMachineCommand::Request crequest;
00085             crequest.CopyFrom(request);
00086             auto& cresponse =
00087                 static_cast<PC::StateMachineCommand::Response&>(response);
00088             auto localCallbacks = std::make_shared<TestingCallbacks>();
00089             // set to noop for recursive calls, then restore
00090             CallbackSwap s(callbacks, localCallbacks);
00091             if (localCallbacks->stateMachineCommand(crequest, cresponse))
00092                 return Status::OK;
00093             cresponse.Clear();
00094             if (timeout < Clock::now())
00095                 return Status::TIMEOUT;
00096             if (crequest.has_tree()) {
00097                 LogCabin::Tree::ProtoBuf::readWriteTreeRPC(
00098                     tree, crequest.tree(), *cresponse.mutable_tree());
00099                 return Status::OK;
00100             } else if (crequest.has_open_session()) {
00101                 cresponse.mutable_open_session()->
00102                     set_client_id(1);
00103                 return Status::OK;
00104             } else if (crequest.has_close_session()) {
00105                 return Status::OK;
00106             }
00107         }
00108         PANIC("Unexpected request: %d %s",
00109               opCode,
00110               Core::ProtoBuf::dumpString(request).c_str());
00111     }
00112 
00113     class Call : public LeaderRPCBase::Call {
00114       public:
00115         explicit Call(TreeLeaderRPC& leaderRPC)
00116             : leaderRPC(leaderRPC)
00117             , opCode()
00118             , request()
00119         {
00120         }
00121         void start(OpCode _opCode,
00122                    const google::protobuf::Message& _request,
00123                    TimePoint _timeout) {
00124             opCode = _opCode;
00125             request.reset(_request.New());
00126             request->CopyFrom(_request);
00127         }
00128         void cancel() {
00129             // no op
00130         }
00131         Status wait(google::protobuf::Message& response,
00132                     TimePoint timeout) {
00133             leaderRPC.call(opCode, *request, response, timeout);
00134             return Status::OK;
00135         }
00136         TreeLeaderRPC& leaderRPC;
00137         OpCode opCode;
00138         std::unique_ptr<google::protobuf::Message> request;
00139 
00140         // Call is not copyable.
00141         Call(const Call&) = delete;
00142         Call& operator=(const Call&) = delete;
00143     };
00144 
00145     std::unique_ptr<LeaderRPCBase::Call> makeCall() {
00146         return std::unique_ptr<LeaderRPCBase::Call>(new Call(*this));
00147     }
00148 
00149     /**
00150      * Prevents concurrent access to callbacks and tree. It's recursive so that
00151      * you can call the client library from within MockCallbacks, if you're
00152      * that insane.
00153      */
00154     std::recursive_mutex mutex;
00155     std::shared_ptr<TestingCallbacks> callbacks;
00156     LogCabin::Tree::Tree tree;
00157 };
00158 } // anonymous namespace
00159 
00160 MockClientImpl::MockClientImpl(std::shared_ptr<TestingCallbacks> callbacks)
00161 {
00162     leaderRPC.reset(new TreeLeaderRPC(callbacks));
00163 }
00164 
00165 MockClientImpl::~MockClientImpl()
00166 {
00167 }
00168 
00169 void
00170 MockClientImpl::initDerived()
00171 {
00172 }
00173 
00174 std::pair<uint64_t, Configuration>
00175 MockClientImpl::getConfiguration()
00176 {
00177     return {0, {}};
00178 }
00179 
00180 ConfigurationResult
00181 MockClientImpl::setConfiguration(uint64_t oldId,
00182                                  const Configuration& newConfiguration)
00183 {
00184     ConfigurationResult result;
00185     result.status = ConfigurationResult::BAD;
00186     result.badServers = newConfiguration;
00187     return result;
00188 }
00189 
00190 } // namespace LogCabin::Client
00191 } // namespace LogCabin
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines