summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWaley Chen <waleycz@gmail.com>2016-08-04 14:58:43 -0400
committerWaley Chen <waleycz@gmail.com>2016-08-04 14:58:43 -0400
commit207f47f85a5972c989f57126fec16399c81212fe (patch)
tree214ae644d05a9e930cad336e5c2b8866c233206d
parent8e56e445cb9faee8a46af8866bf6d859b7ca8f2f (diff)
downloadmongo-207f47f85a5972c989f57126fec16399c81212fe.tar.gz
SERVER-23913 Implement TransportLayer test suite and mock
-rw-r--r--src/mongo/base/error_codes.err20
-rw-r--r--src/mongo/transport/SConscript11
-rw-r--r--src/mongo/transport/service_entry_point_mock_test.cpp24
-rw-r--r--src/mongo/transport/service_entry_point_test_suite.cpp2
-rw-r--r--src/mongo/transport/service_entry_point_test_suite.h2
-rw-r--r--src/mongo/transport/session.cpp15
-rw-r--r--src/mongo/transport/session.h18
-rw-r--r--src/mongo/transport/ticket.cpp24
-rw-r--r--src/mongo/transport/ticket.h35
-rw-r--r--src/mongo/transport/ticket_impl.h1
-rw-r--r--src/mongo/transport/transport_layer.cpp47
-rw-r--r--src/mongo/transport/transport_layer.h6
-rw-r--r--src/mongo/transport/transport_layer_legacy.cpp8
-rw-r--r--src/mongo/transport/transport_layer_legacy.h3
-rw-r--r--src/mongo/transport/transport_layer_manager.cpp2
-rw-r--r--src/mongo/transport/transport_layer_manager.h3
-rw-r--r--src/mongo/transport/transport_layer_mock.cpp108
-rw-r--r--src/mongo/transport/transport_layer_mock.h52
-rw-r--r--src/mongo/transport/transport_layer_mock_test.cpp270
19 files changed, 593 insertions, 58 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 7c8f58de4fd..cee5d694d4d 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -170,15 +170,17 @@ error_code("InvalidPipelineOperator", 168)
error_code("CommandOnShardedViewNotSupportedOnMongod", 169)
error_code("TooManyMatchingDocuments", 170)
error_code("CannotIndexParallelArrays", 171)
-error_code("TransportSessionNotFound", 172)
-error_code("QueryPlanKilled", 173)
-error_code("FileOpenFailed", 174)
-error_code("ZoneNotFound", 175)
-error_code("RangeOverlapConflict", 176)
-error_code("WindowsPdhError", 177)
-error_code("BadPerfCounterPath", 178)
-error_code("AmbiguousIndexKeyPattern", 179)
-error_code("InvalidViewDefinition", 180);
+error_code("TransportSessionClosed", 172)
+error_code("TransportSessionNotFound", 173)
+error_code("TransportSessionUnknown", 174)
+error_code("QueryPlanKilled", 175)
+error_code("FileOpenFailed", 176)
+error_code("ZoneNotFound", 177)
+error_code("RangeOverlapConflict", 178)
+error_code("WindowsPdhError", 179)
+error_code("BadPerfCounterPath", 180)
+error_code("AmbiguousIndexKeyPattern", 181)
+error_code("InvalidViewDefinition", 182);
# Non-sequential error codes (for compatibility only)
error_code("SocketException", 9001)
diff --git a/src/mongo/transport/SConscript b/src/mongo/transport/SConscript
index cbc2d3b9432..38ef3410619 100644
--- a/src/mongo/transport/SConscript
+++ b/src/mongo/transport/SConscript
@@ -17,6 +17,7 @@ env.Library(
source=[
'session.cpp',
'ticket.cpp',
+ 'transport_layer.cpp',
],
LIBDEPS=[
'$BUILD_DIR/mongo/unittest/unittest',
@@ -89,3 +90,13 @@ env.CppUnitTest(
'service_entry_point_test_suite',
],
)
+
+env.CppUnitTest(
+ target='transport_layer_mock_test',
+ source=[
+ 'transport_layer_mock_test.cpp',
+ ],
+ LIBDEPS=[
+ 'transport_layer_mock',
+ ],
+)
diff --git a/src/mongo/transport/service_entry_point_mock_test.cpp b/src/mongo/transport/service_entry_point_mock_test.cpp
index c996c446b37..66806111980 100644
--- a/src/mongo/transport/service_entry_point_mock_test.cpp
+++ b/src/mongo/transport/service_entry_point_mock_test.cpp
@@ -46,19 +46,33 @@ std::unique_ptr<ServiceEntryPoint> mockSEPFactory(transport::TransportLayer* tl)
} // namespace
-TEST_F(ServiceEntryPointTestSuite, ServiceEntryPointMockTest) {
+TEST_F(ServiceEntryPointTestSuite, NoLifeCycleTest) {
setServiceEntryPoint(&mockSEPFactory);
-
- // Lifecycle tests
noLifeCycleTest();
+}
+
+TEST_F(ServiceEntryPointTestSuite, HalfLifeCycleTest) {
+ setServiceEntryPoint(&mockSEPFactory);
halfLifeCycleTest();
+}
+
+TEST_F(ServiceEntryPointTestSuite, FullLifeCycleTest) {
+ setServiceEntryPoint(&mockSEPFactory);
fullLifeCycleTest();
+}
- // Concurrent session tests
+TEST_F(ServiceEntryPointTestSuite, InterruptingSessionTest) {
+ setServiceEntryPoint(&mockSEPFactory);
interruptingSessionTest();
+}
- // Stress tests
+TEST_F(ServiceEntryPointTestSuite, BurstStressTest) {
+ setServiceEntryPoint(&mockSEPFactory);
burstStressTest();
+}
+
+TEST_F(ServiceEntryPointTestSuite, LongSessionStressTest) {
+ setServiceEntryPoint(&mockSEPFactory);
longSessionStressTest();
}
diff --git a/src/mongo/transport/service_entry_point_test_suite.cpp b/src/mongo/transport/service_entry_point_test_suite.cpp
index e2a9c7f336c..efd43fd9a9b 100644
--- a/src/mongo/transport/service_entry_point_test_suite.cpp
+++ b/src/mongo/transport/service_entry_point_test_suite.cpp
@@ -158,7 +158,7 @@ TransportLayer::Stats ServiceEntryPointTestSuite::MockTLHarness::sessionStats()
return Stats();
}
-void ServiceEntryPointTestSuite::MockTLHarness::end(const Session& session) {
+void ServiceEntryPointTestSuite::MockTLHarness::end(Session& session) {
return _end(session);
}
diff --git a/src/mongo/transport/service_entry_point_test_suite.h b/src/mongo/transport/service_entry_point_test_suite.h
index 828fee5fe3c..623f0533267 100644
--- a/src/mongo/transport/service_entry_point_test_suite.h
+++ b/src/mongo/transport/service_entry_point_test_suite.h
@@ -132,7 +132,7 @@ private:
std::string getX509SubjectName(const transport::Session& session) override;
void registerTags(const transport::Session& session) override;
Stats sessionStats() override;
- void end(const transport::Session& session) override;
+ void end(transport::Session& session) override;
void endAllSessions(
transport::Session::TagMask tags = transport::Session::kEmptyTagMask) override;
Status start() override;
diff --git a/src/mongo/transport/session.cpp b/src/mongo/transport/session.cpp
index ebf237f8bd4..b341cd97630 100644
--- a/src/mongo/transport/session.cpp
+++ b/src/mongo/transport/session.cpp
@@ -42,15 +42,19 @@ AtomicUInt64 sessionIdCounter(0);
} // namespace
+const Status Session::ClosedStatus =
+ Status(ErrorCodes::TransportSessionClosed, "Session is closed.");
+
Session::Session(HostAndPort remote, HostAndPort local, TransportLayer* tl)
- : _id(sessionIdCounter.addAndFetch(1)),
+ : _ended(false),
+ _id(sessionIdCounter.addAndFetch(1)),
_remote(std::move(remote)),
_local(std::move(local)),
_tags(kEmptyTagMask),
_tl(tl) {}
Session::~Session() {
- if (_tl != nullptr) {
+ if (_tl != nullptr && !_ended) {
_tl->end(*this);
}
}
@@ -95,5 +99,12 @@ std::string Session::getX509SubjectName() const {
return _tl->getX509SubjectName(*this);
}
+void Session::end() {
+ if (!_ended) {
+ _ended = true;
+ _tl->end(*this);
+ }
+}
+
} // namespace transport
} // namespace mongo
diff --git a/src/mongo/transport/session.h b/src/mongo/transport/session.h
index b340b0fecf8..8551f607037 100644
--- a/src/mongo/transport/session.h
+++ b/src/mongo/transport/session.h
@@ -33,6 +33,7 @@
#include "mongo/transport/ticket.h"
#include "mongo/util/net/hostandport.h"
#include "mongo/util/net/message.h"
+#include "mongo/util/time_support.h"
namespace mongo {
namespace transport {
@@ -56,6 +57,9 @@ public:
* Tags for groups of connections.
*/
using TagMask = uint32_t;
+
+ static const Status ClosedStatus;
+
static constexpr TagMask kEmptyTagMask = 0;
static constexpr TagMask kKeepOpen = 1;
@@ -136,7 +140,21 @@ public:
return _tl;
}
+ /*
+ * End the session.
+ */
+ void end();
+
+ /*
+ * Return true if the session ended, false otherwise.
+ */
+ bool ended() const {
+ return _ended;
+ }
+
private:
+ bool _ended = false;
+
Id _id;
HostAndPort _remote;
diff --git a/src/mongo/transport/ticket.cpp b/src/mongo/transport/ticket.cpp
index 77a290087dd..f7ee14778c4 100644
--- a/src/mongo/transport/ticket.cpp
+++ b/src/mongo/transport/ticket.cpp
@@ -28,6 +28,7 @@
#include "mongo/platform/basic.h"
+#include "mongo/base/status.h"
#include "mongo/transport/ticket.h"
#include "mongo/transport/ticket_impl.h"
#include "mongo/transport/transport_layer.h"
@@ -37,9 +38,16 @@ namespace transport {
const Date_t Ticket::kNoExpirationDate{Date_t::max()};
+Status Ticket::ExpiredStatus = Status(ErrorCodes::ExceededTimeLimit, "Ticket has expired.");
+
+Status Ticket::SessionClosedStatus =
+ Status(ErrorCodes::TransportSessionClosed, "Ticket's Session is closed.");
+
Ticket::Ticket(TransportLayer* tl, std::unique_ptr<TicketImpl> ticket)
: _tl(tl), _ticket(std::move(ticket)) {}
+Ticket::Ticket(Status status) : _status(status) {}
+
Ticket::~Ticket() = default;
Ticket::Ticket(Ticket&&) = default;
@@ -53,5 +61,21 @@ void Ticket::asyncWait(TicketCallback cb)&& {
return _tl->asyncWait(std::move(*this), std::move(cb));
}
+bool Ticket::valid() {
+ return _status == Status::OK() && !expired();
+}
+
+Status Ticket::status() const {
+ return _status;
+}
+
+bool Ticket::expired() {
+ bool expired = expiration() <= Date_t::now();
+ if (_status == Status::OK() && expired) {
+ _status = Status(ErrorCodes::ExceededTimeLimit, "Ticket has expired.");
+ }
+ return expired;
+}
+
} // namespace transport
} // namespace mongo
diff --git a/src/mongo/transport/ticket.h b/src/mongo/transport/ticket.h
index dd8927085aa..63b064d104b 100644
--- a/src/mongo/transport/ticket.h
+++ b/src/mongo/transport/ticket.h
@@ -31,14 +31,15 @@
#include <memory>
#include "mongo/base/disallow_copying.h"
+#include "mongo/base/status.h"
#include "mongo/stdx/functional.h"
-#include "mongo/transport/session_id.h"
#include "mongo/transport/ticket_impl.h"
#include "mongo/util/time_support.h"
namespace mongo {
namespace transport {
+class Session;
class TransportLayer;
/**
@@ -59,7 +60,16 @@ public:
*/
static const Date_t kNoExpirationDate;
+ static Status ExpiredStatus;
+ static Status SessionClosedStatus;
+
Ticket(TransportLayer* tl, std::unique_ptr<TicketImpl> ticket);
+
+ /**
+ * An invalid ticket and a Status for why it's invalid.
+ */
+ Ticket(Status status);
+
~Ticket();
/**
@@ -96,7 +106,27 @@ public:
*/
void asyncWait(TicketCallback cb) &&;
-protected:
+ /*
+ * Return's the Status of the ticket.
+ */
+ Status status() const;
+
+ /*
+ * Returns true if the ticket has expired, false otherwise.
+ *
+ * If the ticket has expired, changes the Status of the ticket to TicketExpired but
+ * only if the current Status is Status::OK().
+ */
+ bool expired();
+
+ /*
+ * Return true if the ticket is usable, false otherwise.
+ *
+ * If the ticket has expired, changes the Status of the ticket to TicketExpired but
+ * only if the current Status is Status::OK().
+ */
+ bool valid();
+
/**
* Return a non-owning pointer to the underlying TicketImpl type
*/
@@ -106,6 +136,7 @@ protected:
private:
TransportLayer* _tl;
+ Status _status = Status::OK();
std::unique_ptr<TicketImpl> _ticket;
};
diff --git a/src/mongo/transport/ticket_impl.h b/src/mongo/transport/ticket_impl.h
index 4cbbc0a22d0..22adf9414e7 100644
--- a/src/mongo/transport/ticket_impl.h
+++ b/src/mongo/transport/ticket_impl.h
@@ -30,6 +30,7 @@
#include "mongo/base/disallow_copying.h"
#include "mongo/transport/session_id.h"
+#include "mongo/util/net/message.h"
#include "mongo/util/time_support.h"
namespace mongo {
diff --git a/src/mongo/transport/transport_layer.cpp b/src/mongo/transport/transport_layer.cpp
new file mode 100644
index 00000000000..877070191b4
--- /dev/null
+++ b/src/mongo/transport/transport_layer.cpp
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/base/status.h"
+#include "mongo/transport/transport_layer.h"
+
+namespace mongo {
+namespace transport {
+
+const Status TransportLayer::SessionUnknownStatus =
+ Status(ErrorCodes::TransportSessionUnknown, "TransportLayer does not own the Session.");
+
+const Status TransportLayer::ShutdownStatus =
+ Status(ErrorCodes::ShutdownInProgress, "TransportLayer is in shutdown.");
+
+const Status TransportLayer::TicketSessionUnknownStatus = Status(
+ ErrorCodes::TransportSessionUnknown, "TransportLayer does not own the Ticket's Session.");
+
+} // namespace transport
+} // namespace mongo
diff --git a/src/mongo/transport/transport_layer.h b/src/mongo/transport/transport_layer.h
index dde6248eaf5..44995fc375b 100644
--- a/src/mongo/transport/transport_layer.h
+++ b/src/mongo/transport/transport_layer.h
@@ -57,6 +57,10 @@ class TransportLayer {
MONGO_DISALLOW_COPYING(TransportLayer);
public:
+ static const Status SessionUnknownStatus;
+ static const Status ShutdownStatus;
+ static const Status TicketSessionUnknownStatus;
+
/**
* Stats for sessions open in the Transport Layer.
*/
@@ -171,7 +175,7 @@ public:
*
* This method is idempotent and synchronous.
*/
- virtual void end(const Session& session) = 0;
+ virtual void end(Session& session) = 0;
/**
* End all active sessions in the TransportLayer. Tickets that have already been started via
diff --git a/src/mongo/transport/transport_layer_legacy.cpp b/src/mongo/transport/transport_layer_legacy.cpp
index b45d6a6ad07..8d1f95b6a23 100644
--- a/src/mongo/transport/transport_layer_legacy.cpp
+++ b/src/mongo/transport/transport_layer_legacy.cpp
@@ -163,7 +163,7 @@ void TransportLayerLegacy::asyncWait(Ticket&& ticket, TicketCallback callback) {
MONGO_UNREACHABLE;
}
-void TransportLayerLegacy::end(const Session& session) {
+void TransportLayerLegacy::end(Session& session) {
stdx::lock_guard<stdx::mutex> lk(_connectionsMutex);
auto conn = _connections.find(session.id());
if (conn != _connections.end()) {
@@ -225,11 +225,11 @@ void TransportLayerLegacy::shutdown() {
Status TransportLayerLegacy::_runTicket(Ticket ticket) {
if (!_running.load()) {
- return {ErrorCodes::ShutdownInProgress, "TransportLayer in shutdown"};
+ return TransportLayer::ShutdownStatus;
}
if (ticket.expiration() < Date_t::now()) {
- return {ErrorCodes::ExceededTimeLimit, "Ticket has expired"};
+ return Ticket::ExpiredStatus;
}
AbstractMessagingPort* amp;
@@ -239,7 +239,7 @@ Status TransportLayerLegacy::_runTicket(Ticket ticket) {
auto conn = _connections.find(ticket.sessionId());
if (conn == _connections.end()) {
- return {ErrorCodes::TransportSessionNotFound, "No such session in TransportLayer"};
+ return TransportLayer::TicketSessionUnknownStatus;
}
// "check out" the port
diff --git a/src/mongo/transport/transport_layer_legacy.h b/src/mongo/transport/transport_layer_legacy.h
index ec17c76ca1e..1ef193b4754 100644
--- a/src/mongo/transport/transport_layer_legacy.h
+++ b/src/mongo/transport/transport_layer_legacy.h
@@ -83,8 +83,9 @@ public:
Stats sessionStats() override;
- void end(const Session& session) override;
+ void end(Session& session) override;
void endAllSessions(transport::Session::TagMask tags = Session::kKeepOpen) override;
+
void shutdown() override;
private:
diff --git a/src/mongo/transport/transport_layer_manager.cpp b/src/mongo/transport/transport_layer_manager.cpp
index c64023b2067..7ba1797a21f 100644
--- a/src/mongo/transport/transport_layer_manager.cpp
+++ b/src/mongo/transport/transport_layer_manager.cpp
@@ -100,7 +100,7 @@ void TransportLayerManager::registerTags(const Session& session) {
session.getTransportLayer()->registerTags(session);
}
-void TransportLayerManager::end(const Session& session) {
+void TransportLayerManager::end(Session& session) {
session.getTransportLayer()->end(session);
}
diff --git a/src/mongo/transport/transport_layer_manager.h b/src/mongo/transport/transport_layer_manager.h
index 391bdf4ebea..aeed86edcfe 100644
--- a/src/mongo/transport/transport_layer_manager.h
+++ b/src/mongo/transport/transport_layer_manager.h
@@ -69,11 +69,10 @@ public:
Stats sessionStats() override;
- void end(const Session& session) override;
+ void end(Session& session) override;
void endAllSessions(Session::TagMask tags = Session::kEmptyTagMask) override;
Status start() override;
-
void shutdown() override;
Status addAndStartTransportLayer(std::unique_ptr<TransportLayer> tl);
diff --git a/src/mongo/transport/transport_layer_mock.cpp b/src/mongo/transport/transport_layer_mock.cpp
index c550570b401..3f71b5d16e0 100644
--- a/src/mongo/transport/transport_layer_mock.cpp
+++ b/src/mongo/transport/transport_layer_mock.cpp
@@ -28,42 +28,82 @@
#include "mongo/platform/basic.h"
-#include <memory>
+#include "mongo/transport/transport_layer_mock.h"
#include "mongo/base/status.h"
#include "mongo/stdx/memory.h"
#include "mongo/transport/session.h"
#include "mongo/transport/ticket.h"
#include "mongo/transport/ticket_impl.h"
-#include "mongo/transport/transport_layer_mock.h"
+#include "mongo/transport/transport_layer.h"
+#include "mongo/util/net/message.h"
#include "mongo/util/time_support.h"
-#include "mongo/stdx/memory.h"
-
namespace mongo {
namespace transport {
-Session::Id TransportLayerMock::MockTicket::sessionId() const {
- return Session::Id{};
+TransportLayerMock::TicketMock::TicketMock(const Session* session,
+ Message* message,
+ Date_t expiration)
+ : _session(session), _message(message), _expiration(expiration) {}
+
+TransportLayerMock::TicketMock::TicketMock(const Session* session, Date_t expiration)
+ : _session(session), _expiration(expiration) {}
+
+Session::Id TransportLayerMock::TicketMock::sessionId() const {
+ return _session->id();
}
-Date_t TransportLayerMock::MockTicket::expiration() const {
- return Date_t::now();
+Date_t TransportLayerMock::TicketMock::expiration() const {
+ return _expiration;
}
+boost::optional<Message*> TransportLayerMock::TicketMock::msg() const {
+ return _message;
+}
+
+TransportLayerMock::TransportLayerMock() : _shutdown(false) {}
+
Ticket TransportLayerMock::sourceMessage(const Session& session,
Message* message,
Date_t expiration) {
- return Ticket(this, stdx::make_unique<MockTicket>());
+ if (inShutdown()) {
+ return Ticket(TransportLayer::ShutdownStatus);
+ } else if (!owns(session.id())) {
+ return Ticket(TransportLayer::SessionUnknownStatus);
+ } else if (session.ended()) {
+ return Ticket(Session::ClosedStatus);
+ }
+
+ return Ticket(this,
+ stdx::make_unique<TransportLayerMock::TicketMock>(&session, message, expiration));
}
Ticket TransportLayerMock::sinkMessage(const Session& session,
const Message& message,
Date_t expiration) {
- return Ticket(this, stdx::make_unique<MockTicket>());
+ if (inShutdown()) {
+ return Ticket(TransportLayer::ShutdownStatus);
+ } else if (!owns(session.id())) {
+ return Ticket(TransportLayer::SessionUnknownStatus);
+ } else if (session.ended()) {
+ return Ticket(Session::ClosedStatus);
+ }
+
+ return Ticket(this, stdx::make_unique<TransportLayerMock::TicketMock>(&session, expiration));
}
Status TransportLayerMock::wait(Ticket&& ticket) {
+ if (inShutdown()) {
+ return ShutdownStatus;
+ } else if (!ticket.valid()) {
+ return ticket.status();
+ } else if (!owns(ticket.sessionId())) {
+ return TicketSessionUnknownStatus;
+ } else if (get(ticket.sessionId())->ended()) {
+ return Ticket::SessionClosedStatus;
+ }
+
return Status::OK();
}
@@ -81,15 +121,57 @@ TransportLayer::Stats TransportLayerMock::sessionStats() {
void TransportLayerMock::registerTags(const Session& session) {}
-void TransportLayerMock::end(const Session& session) {}
+Session* TransportLayerMock::createSession() {
+ std::unique_ptr<Session> session =
+ stdx::make_unique<Session>(HostAndPort(), HostAndPort(), this);
+ Session::Id sessionId = session->id();
+
+ _sessions[sessionId] = std::move(session);
+
+ return _sessions[sessionId].get();
+}
+
+Session* TransportLayerMock::get(Session::Id id) {
+ if (!owns(id))
+ return nullptr;
+
+ return _sessions[id].get();
+}
-void TransportLayerMock::endAllSessions(Session::TagMask tags) {}
+bool TransportLayerMock::owns(Session::Id id) {
+ return _sessions.count(id) > 0;
+}
+
+void TransportLayerMock::end(Session& session) {
+ session.end();
+}
+
+void TransportLayerMock::endAllSessions(Session::TagMask tags) {
+ auto it = _sessions.begin();
+ while (it != _sessions.end()) {
+ end(*it->second.get());
+ it++;
+ }
+}
Status TransportLayerMock::start() {
return Status::OK();
}
-void TransportLayerMock::shutdown() {}
+void TransportLayerMock::shutdown() {
+ if (!inShutdown()) {
+ _shutdown = true;
+ endAllSessions();
+ }
+}
+
+bool TransportLayerMock::inShutdown() const {
+ return _shutdown;
+}
+
+TransportLayerMock::~TransportLayerMock() {
+ shutdown();
+}
} // namespace transport
} // namespace mongo
diff --git a/src/mongo/transport/transport_layer_mock.h b/src/mongo/transport/transport_layer_mock.h
index 400d6b02258..f519713e9bc 100644
--- a/src/mongo/transport/transport_layer_mock.h
+++ b/src/mongo/transport/transport_layer_mock.h
@@ -28,6 +28,8 @@
#pragma once
+#include <unordered_map>
+
#include "mongo/base/status.h"
#include "mongo/transport/session.h"
#include "mongo/transport/ticket.h"
@@ -46,7 +48,33 @@ class TransportLayerMock : public TransportLayer {
MONGO_DISALLOW_COPYING(TransportLayerMock);
public:
- TransportLayerMock() = default;
+ class TicketMock : public TicketImpl {
+ public:
+ // Source constructor
+ TicketMock(const Session* session,
+ Message* message,
+ Date_t expiration = Ticket::kNoExpirationDate);
+
+ // Sink constructor
+ TicketMock(const Session* session, Date_t expiration = Ticket::kNoExpirationDate);
+
+ TicketMock(TicketMock&&) = default;
+ TicketMock& operator=(TicketMock&&) = default;
+
+ SessionId sessionId() const override;
+
+ Date_t expiration() const override;
+
+ boost::optional<Message*> msg() const;
+
+ private:
+ const Session* _session;
+ boost::optional<Message*> _message;
+ Date_t _expiration;
+ };
+
+ TransportLayerMock();
+ ~TransportLayerMock();
Ticket sourceMessage(const Session& session,
Message* message,
@@ -63,27 +91,19 @@ public:
Stats sessionStats() override;
- void end(const Session& session) override;
+ Session* createSession();
+ Session* get(Session::Id id);
+ bool owns(Session::Id id);
+ void end(Session& session) override;
void endAllSessions(Session::TagMask tags = Session::kEmptyTagMask) override;
Status start() override;
void shutdown() override;
+ bool inShutdown() const;
private:
- /**
- * A class for Tickets issued from the TransportLayerMock. These will
- * route to the appropriate TransportLayer when run with wait() or
- * asyncWait().
- */
- class MockTicket : public TicketImpl {
- MONGO_DISALLOW_COPYING(MockTicket);
-
- public:
- MockTicket() = default;
-
- Session::Id sessionId() const override;
- Date_t expiration() const override;
- };
+ std::unordered_map<Session::Id, std::unique_ptr<Session>> _sessions;
+ bool _shutdown;
};
} // namespace transport
diff --git a/src/mongo/transport/transport_layer_mock_test.cpp b/src/mongo/transport/transport_layer_mock_test.cpp
new file mode 100644
index 00000000000..22a1da12d88
--- /dev/null
+++ b/src/mongo/transport/transport_layer_mock_test.cpp
@@ -0,0 +1,270 @@
+/**
+ * Copyright (C) 2016 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/stdx/memory.h"
+#include "mongo/transport/session.h"
+#include "mongo/transport/transport_layer.h"
+#include "mongo/transport/transport_layer_mock.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace transport {
+
+class TransportLayerMockTest : public mongo::unittest::Test {
+public:
+ void setUp() {
+ _transportLayer = stdx::make_unique<TransportLayerMock>();
+ }
+
+ TransportLayerMock* tl() {
+ return _transportLayer.get();
+ }
+
+private:
+ std::unique_ptr<TransportLayerMock> _transportLayer;
+};
+
+// sinkMessage() generates a valid Ticket
+TEST_F(TransportLayerMockTest, SinkMessageGeneratesTicket) {
+ Message* msg = nullptr;
+ Session* session = tl()->createSession();
+
+ // call sinkMessage() with no expiration
+ Ticket ticket = tl()->sinkMessage(*session, *msg);
+ ASSERT(ticket.valid());
+ ASSERT_OK(ticket.status());
+ ASSERT_EQUALS(ticket.sessionId(), session->id());
+ ASSERT_EQUALS(ticket.expiration(), Ticket::kNoExpirationDate);
+
+ // call sinkMessage() with an expiration
+ Date_t expiration = Date_t::now() + Hours(1);
+ ticket = tl()->sinkMessage(*session, *msg, expiration);
+ ASSERT(ticket.valid());
+ ASSERT_OK(ticket.status());
+ ASSERT_EQUALS(ticket.sessionId(), session->id());
+ ASSERT_EQUALS(ticket.expiration(), expiration);
+}
+
+// sinkMessage() generates an invalid Ticket if the Session is closed
+TEST_F(TransportLayerMockTest, SinkMessageSessionClosed) {
+ Message* msg = nullptr;
+ Session* session = tl()->createSession();
+
+ tl()->end(*session);
+
+ Ticket ticket = tl()->sinkMessage(*session, *msg);
+ ASSERT_FALSE(ticket.valid());
+ ASSERT_EQUALS(ticket.status().code(), ErrorCodes::TransportSessionClosed);
+}
+
+// sinkMessage() generates an invalid Ticket if the TransportLayer does not own the Session
+TEST_F(TransportLayerMockTest, SinkMessageSessionUnknown) {
+ Message* msg = nullptr;
+
+ std::unique_ptr<TransportLayerMock> anotherTL = stdx::make_unique<TransportLayerMock>();
+ Session* session = anotherTL->createSession();
+
+ Ticket ticket = tl()->sinkMessage(*session, *msg);
+ ASSERT_FALSE(ticket.valid());
+ ASSERT_EQUALS(ticket.status().code(), ErrorCodes::TransportSessionUnknown);
+}
+
+// sinkMessage() generates an invalid Ticket if the TransportLayer is in shutdown
+TEST_F(TransportLayerMockTest, SinkMessageTLShutdown) {
+ Message* msg = nullptr;
+ Session* session = tl()->createSession();
+
+ tl()->shutdown();
+
+ Ticket ticket = tl()->sinkMessage(*session, *msg);
+ ASSERT_FALSE(ticket.valid());
+ ASSERT_EQUALS(ticket.status().code(), ErrorCodes::ShutdownInProgress);
+}
+
+// sourceMessage() generates a valid ticket
+TEST_F(TransportLayerMockTest, SourceMessageGeneratesTicket) {
+ Message* msg = nullptr;
+ Session* session = tl()->createSession();
+
+ // call sourceMessage() with no expiration
+ Ticket ticket = tl()->sourceMessage(*session, msg);
+ ASSERT(ticket.valid());
+ ASSERT_OK(ticket.status());
+ ASSERT_EQUALS(ticket.sessionId(), session->id());
+ ASSERT_EQUALS(*static_cast<TransportLayerMock::TicketMock*>(ticket.impl())->msg(), msg);
+ ASSERT_EQUALS(ticket.expiration(), Ticket::kNoExpirationDate);
+
+ // call sourceMessage() with an expiration
+ Date_t expiration = Date_t::now() + Hours(1);
+ ticket = tl()->sourceMessage(*session, msg, expiration);
+ ASSERT(ticket.valid());
+ ASSERT_OK(ticket.status());
+ ASSERT_EQUALS(ticket.sessionId(), session->id());
+ ASSERT_EQUALS(*static_cast<TransportLayerMock::TicketMock*>(ticket.impl())->msg(), msg);
+ ASSERT_EQUALS(ticket.expiration(), expiration);
+}
+
+// sourceMessage() generates an invalid ticket if the Session is closed
+TEST_F(TransportLayerMockTest, SourceMessageSessionClosed) {
+ Message* msg = nullptr;
+ Session* session = tl()->createSession();
+
+ tl()->end(*session);
+
+ Ticket ticket = tl()->sourceMessage(*session, msg);
+ ASSERT_FALSE(ticket.valid());
+ ASSERT_EQUALS(ticket.status().code(), ErrorCodes::TransportSessionClosed);
+}
+
+// sourceMessage() generates an invalid ticket if the TransportLayer does not own the Session
+TEST_F(TransportLayerMockTest, SourceMessageSessionUnknown) {
+ Message* msg = nullptr;
+
+ std::unique_ptr<TransportLayerMock> anotherTL = stdx::make_unique<TransportLayerMock>();
+ Session* session = anotherTL->createSession();
+
+ Ticket ticket = tl()->sourceMessage(*session, msg);
+ ASSERT_FALSE(ticket.valid());
+ ASSERT_EQUALS(ticket.status().code(), ErrorCodes::TransportSessionUnknown);
+}
+
+// sourceMessage() generates an invalid ticket if the TransportLayer is in shutdown
+TEST_F(TransportLayerMockTest, SourceMessageTLShutdown) {
+ Message* msg = nullptr;
+ Session* session = tl()->createSession();
+
+ tl()->shutdown();
+
+ Ticket ticket = tl()->sourceMessage(*session, msg);
+ ASSERT_FALSE(ticket.valid());
+ ASSERT_EQUALS(ticket.status().code(), ErrorCodes::ShutdownInProgress);
+}
+
+// wait() returns an OK status
+TEST_F(TransportLayerMockTest, Wait) {
+ Session* session = tl()->createSession();
+ Ticket ticket = Ticket(tl(), stdx::make_unique<TransportLayerMock::TicketMock>(session));
+
+ Status status = tl()->wait(std::move(ticket));
+ ASSERT_OK(status);
+}
+
+// wait() returns an TicketExpired error status if the Ticket expired
+TEST_F(TransportLayerMockTest, WaitExpiredTicket) {
+ Session* session = tl()->createSession();
+ Ticket expiredTicket =
+ Ticket(tl(), stdx::make_unique<TransportLayerMock::TicketMock>(session, Date_t::now()));
+
+ Status status = tl()->wait(std::move(expiredTicket));
+ ASSERT_EQUALS(status.code(), ErrorCodes::ExceededTimeLimit);
+}
+
+// wait() returns the invalid Ticket's Status
+TEST_F(TransportLayerMockTest, WaitInvalidTicket) {
+ Ticket invalidTicket = Ticket(Status(ErrorCodes::UnknownError, ""));
+ ASSERT_FALSE(invalidTicket.valid());
+
+ Status status = tl()->wait(std::move(invalidTicket));
+ ASSERT_EQUALS(status.code(), ErrorCodes::UnknownError);
+}
+
+// wait() returns a SessionClosed error status if the Ticket's Session is closed
+TEST_F(TransportLayerMockTest, WaitSessionClosed) {
+ Session* session = tl()->createSession();
+ Ticket ticket = Ticket(tl(), stdx::make_unique<TransportLayerMock::TicketMock>(session));
+
+ tl()->end(*session);
+
+ Status status = tl()->wait(std::move(ticket));
+ ASSERT_EQUALS(status.code(), ErrorCodes::TransportSessionClosed);
+}
+
+// wait() returns a SessionUnknown error status if the TransportLayer does not own the Ticket's
+// Session
+TEST_F(TransportLayerMockTest, WaitSessionUnknown) {
+ std::unique_ptr<TransportLayerMock> anotherTL = stdx::make_unique<TransportLayerMock>();
+ Session* session = anotherTL->createSession();
+ Ticket ticket = Ticket(tl(), stdx::make_unique<TransportLayerMock::TicketMock>(session));
+
+ Status status = tl()->wait(std::move(ticket));
+ ASSERT_EQUALS(status.code(), ErrorCodes::TransportSessionUnknown);
+}
+
+// wait() returns a ShutdownInProgress status if the TransportLayer is in shutdown
+TEST_F(TransportLayerMockTest, WaitTLShutdown) {
+ Session* session = tl()->createSession();
+ Ticket ticket = Ticket(tl(), stdx::make_unique<TransportLayerMock::TicketMock>(session));
+
+ tl()->shutdown();
+
+ Status status = tl()->wait(std::move(ticket));
+ ASSERT_EQUALS(status.code(), ErrorCodes::ShutdownInProgress);
+}
+
+// end() closes the session
+TEST_F(TransportLayerMockTest, EndSession) {
+ Session* session = tl()->createSession();
+ tl()->end(*session);
+ ASSERT(session->ended());
+}
+
+std::vector<Session*> createSessions(TransportLayerMock* tl) {
+ int numSessions = 10;
+ std::vector<Session*> sessions;
+ for (int i = 0; i < numSessions; i++) {
+ Session* session = tl->createSession();
+ sessions.push_back(session);
+ }
+ return sessions;
+}
+
+void assertEnded(std::vector<Session*> sessions) {
+ for (auto session : sessions) {
+ ASSERT(session->ended());
+ }
+}
+
+// endAllSessions() ends all sessions
+TEST_F(TransportLayerMockTest, EndAllSessions) {
+ std::vector<Session*> sessions = createSessions(tl());
+ tl()->endAllSessions();
+ assertEnded(sessions);
+}
+
+// shutdown() ends all sessions and shuts down
+TEST_F(TransportLayerMockTest, Shutdown) {
+ std::vector<Session*> sessions = createSessions(tl());
+ tl()->shutdown();
+ assertEnded(sessions);
+ ASSERT(tl()->inShutdown());
+}
+
+} // namespace transport
+} // namespace mongo