Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1592)

Unified Diff: util/mach/mach_message_server_test.cc

Issue 544393002: Add MachMessageServer and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: util/mach/mach_message_server_test.cc
diff --git a/util/mach/mach_message_server_test.cc b/util/mach/mach_message_server_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0b811ca2a9085ec4b692cb8b093d2aa4a689ae21
--- /dev/null
+++ b/util/mach/mach_message_server_test.cc
@@ -0,0 +1,386 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/mach_message_server.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "gtest/gtest.h"
+#include "util/file/fd_io.h"
+#include "util/test/errors.h"
+#include "util/test/mac/mach_errors.h"
+#include "util/test/mac/mach_multiprocess.h"
+
+namespace {
+
+using namespace crashpad;
+using namespace crashpad::test;
+
+class TestMachMessageServer : public MachMessageServerInterface,
+ public MachMultiprocess {
+ public:
+ // Variations on the MachMessageServer test.
+ enum TestType {
Robert Sesek 2014/09/08 16:28:47 No tests for the error reply scenario? What about
+ // The client sends one message to the server, which will wait indefinitely
+ // in blocking mode for it.
+ kBasic = 0,
+
+ // The server waits in nonblocking mode and the client sends nothing, so
+ // the server should return immediately without processing any message.
+ kNonblockingNoMessage,
+
+ // The server waits in blocking mode for one message, but with a timeout.
+ // The client sends no message, so the server returns after the timeout.
+ kTimeoutNoMessage,
+
+ // The client sends one message to the server and then signals the server
+ // that it’s safe to start waiting for it in nonblocking mode. The message
+ // is in the server’s queue, so it’s able to receive it when it begins
+ // listening in nonblocking mode.
+ kNonblocking,
+
+ // The client sends one message to the server, which will wait in blocking
+ // mode for it up to a specific timeout.
+ kTimeout,
+
+ // The server waits for as many messages as it can receive in blocking mode
+ // with a timeout. The client sends several messages, and the server
+ // processes them all.
+ kPersistentTenMessages,
+
+ // The client sends several messages to the server and then signals the
+ // server that it’s safe to start waiting for them in nonblocking mode. The
+ // server then listens for them in nonblocking persistent mode, and receives
+ // all of them because they’ve been queued up. The client doesn’t wait for
+ // the replies until after it’s put all of its requests into the server’s
+ // queue.
+ //
+ // This test is sensitive to the length of the IPC queue limit. Mach ports
+ // normally have a queue length limit of MACH_PORT_QLIMIT_DEFAULT (which
+ // is MACH_PORT_QLIMIT_BASIC, or 5). The number of messages sent for this
+ // test must be below this, because the server does not begin dequeueing
+ // request messages until the client has finished sending them.
+ kPersistentNonblockingFourMessages,
+ };
+
+ explicit TestMachMessageServer(TestType test_type)
+ : MachMessageServerInterface(),
+ MachMultiprocess(),
+ test_type_(test_type) {
+ }
+
+ // Runs the test, verifying that the number or requests and replies begins
+ // and remains equal. Returns the number of round-trip messages processed.
+ uint32_t Test() {
+ EXPECT_EQ(requests_, replies_);
+ uint32_t start = requests_;
+
+ Run();
+
+ EXPECT_EQ(requests_, replies_);
+ return requests_ - start;
+ }
+
+ // MachMessageServerInterface:
+
+ virtual bool MachMessageServerFunction(
+ mach_msg_header_t* in,
+ mach_msg_header_t* out,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ switch (test_type_) {
+ case kNonblockingNoMessage:
+ case kTimeoutNoMessage:
+ // These test types should not result in any messages being processed.
+ ADD_FAILURE();
+ return false;
+
+ default:
+ break;
+ }
+
+ const ReceiveRequestMessage* request =
+ reinterpret_cast<ReceiveRequestMessage*>(in);
+ EXPECT_EQ(RemotePort(), request->header.msgh_remote_port);
+ EXPECT_EQ(sizeof(SendRequestMessage), request->header.msgh_size);
+ EXPECT_EQ(requests_, request->number);
+ ++requests_;
+
+ SendReplyMessage* reply =
+ reinterpret_cast<SendReplyMessage*>(out);
+ reply->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ reply->header.msgh_remote_port = request->header.msgh_remote_port;
+ reply->header.msgh_size = sizeof(SendReplyMessage);
+ reply->header.msgh_local_port = MACH_PORT_NULL;
+ reply->number = replies_++;
+
+ return true;
+ }
+
+ virtual mach_msg_size_t MachMessageServerRequestSize() override {
+ return sizeof(SendRequestMessage);
+ }
+
+ virtual mach_msg_size_t MachMessageServerReplySize() override {
+ return sizeof(SendReplyMessage);
+ }
+
+ private:
+ struct SendRequestMessage {
+ mach_msg_header_t header;
+ uint32_t number;
+ };
+
+ struct ReceiveRequestMessage : public SendRequestMessage {
+ mach_msg_trailer_t trailer;
+ };
+
+ struct SendReplyMessage {
+ mach_msg_header_t header;
+ uint32_t number;
+ };
+
+ struct ReceiveReplyMessage : public SendReplyMessage {
+ mach_msg_trailer_t trailer;
+ };
+
+ // MachMultiprocess:
+
+ virtual void MachMultiprocessParent() override {
+ switch (test_type_) {
+ case kNonblocking:
+ case kPersistentNonblockingFourMessages: {
+ // Wait until the child is done sending what it’s going to send.
+ char c;
+ ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
+ EXPECT_EQ(1, rv) << ErrnoMessage("read");
+ EXPECT_EQ('\0', c);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ bool persistent = false;
+ bool nonblocking = false;
+ mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE;
+ kern_return_t expect = KERN_SUCCESS;
+
+ switch (test_type_) {
+ case kNonblockingNoMessage:
+ nonblocking = true;
+ expect = MACH_RCV_TIMED_OUT;
+ break;
+ case kTimeoutNoMessage:
+ timeout = 10; // milliseconds
+ expect = MACH_RCV_TIMED_OUT;
+ break;
+ case kNonblocking:
+ nonblocking = true;
+ break;
+ case kTimeout:
+ timeout = 10; // milliseconds
+ break;
+ case kPersistentTenMessages:
+ persistent = true;
+ timeout = 10; // milliseconds
+ expect = MACH_RCV_TIMED_OUT;
+ break;
+ case kPersistentNonblockingFourMessages:
+ persistent = true;
+ nonblocking = true;
+ expect = MACH_RCV_TIMED_OUT;
+ break;
+ default:
+ break;
+ }
+
+ kern_return_t kr;
+ ASSERT_EQ(expect, (kr = MachMessageServer(this,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ persistent,
+ nonblocking,
+ timeout)))
+ << MachErrorMessage(kr, "MachMessageServer");
+ }
+
+ virtual void MachMultiprocessChild() override {
Robert Sesek 2014/09/08 16:28:47 The test is pretty comprehensive, which is good, b
+ size_t count = 1;
+ switch (test_type_) {
+ case kNonblockingNoMessage:
+ case kTimeoutNoMessage:
+ count = 0;
+ break;
+ case kPersistentTenMessages:
+ count = 10;
+ break;
+ case kPersistentNonblockingFourMessages:
+ count = 4;
+ break;
+ default:
+ break;
+ }
+
+ for (size_t index = 0; index < count; ++index) {
+ if (test_type_ == kPersistentNonblockingFourMessages) {
+ // For this test, all of the messages need to go into the queue before
+ // the parent is allowed to start processing them, so the replies won’t
+ // be processed until all of the requests are sent.
+ ChildSendRequest();
+ } else {
+ ChildSendRequestAndWaitForReply();
+ }
+ if (testing::Test::HasFatalFailure()) {
+ return;
+ }
+ }
+
+ if (test_type_ == kPersistentNonblockingFourMessages) {
+ // Now that all of the requests have been sent, let the parent know that
+ // it’s safe to begin processing them, and then wait for the replies.
+ ChildNotifyParentViaPipe();
+ if (testing::Test::HasFatalFailure()) {
+ return;
+ }
+
+ for (size_t index = 0; index < count; ++index) {
+ ChildWaitForReply();
+ if (testing::Test::HasFatalFailure()) {
+ return;
+ }
+ }
+ }
+ }
+
+ // In the child process, sends a request message to the server.
+ void ChildSendRequest() {
+ SendRequestMessage request = {};
+ request.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
+ request.header.msgh_size = sizeof(request);
+ request.header.msgh_remote_port = RemotePort();
+ request.header.msgh_local_port = LocalPort();
+ request.number = requests_++;
+
+ kern_return_t kr = mach_msg(&request.header,
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ request.header.msgh_size,
+ 0,
+ MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
+ }
+
+ // In the child process, waits for a reply message from the server.
+ void ChildWaitForReply() {
+ ReceiveReplyMessage reply = {};
+ kern_return_t kr = mach_msg(&reply.header,
+ MACH_RCV_MSG,
+ 0,
+ sizeof(reply),
+ LocalPort(),
+ MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
+
+ ASSERT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
+ reply.header.msgh_remote_port);
+ ASSERT_EQ(sizeof(SendReplyMessage), reply.header.msgh_size);
+ ASSERT_EQ(replies_, reply.number);
+ ++replies_;
+ }
+
+ // For test types where the child needs to notify the server in the parent
+ // that the child is ready, this method will send a byte via the POSIX pipe.
+ // The parent will be waiting in a read() on this pipe, and will proceed to
+ // running MachMessageServer() once it’s received.
+ void ChildNotifyParentViaPipe() {
+ char c = '\0';
+ ssize_t rv = WriteFD(WritePipeFD(), &c, 1);
+ ASSERT_EQ(1, rv) << ErrnoMessage("write");
+ }
+
+ // In the child process, sends a request message to the server and then
+ // receives a reply message.
+ void ChildSendRequestAndWaitForReply() {
+ ChildSendRequest();
+ if (testing::Test::HasFatalFailure()) {
+ return;
+ }
+
+ if (test_type_ == kNonblocking) {
+ // The parent is waiting to read a byte to indicate that the message has
+ // been placed in the queue.
+ ChildNotifyParentViaPipe();
+ if (testing::Test::HasFatalFailure()) {
+ return;
+ }
+ }
+
+ ChildWaitForReply();
+ }
+
+ TestType test_type_;
+
+ static uint32_t requests_;
+ static uint32_t replies_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer);
+};
+
+uint32_t TestMachMessageServer::requests_;
+uint32_t TestMachMessageServer::replies_;
+
+TEST(MachMessageServer, Basic) {
+ TestMachMessageServer test_mach_message_server(TestMachMessageServer::kBasic);
+ EXPECT_EQ(1u, test_mach_message_server.Test());
+}
+
+TEST(MachMessageServer, NonblockingNoMessage) {
+ TestMachMessageServer test_mach_message_server(
+ TestMachMessageServer::kNonblockingNoMessage);
+ EXPECT_EQ(0u, test_mach_message_server.Test());
+}
+
+TEST(MachMessageServer, TimeoutNoMessage) {
+ TestMachMessageServer test_mach_message_server(
+ TestMachMessageServer::kTimeoutNoMessage);
+ EXPECT_EQ(0u, test_mach_message_server.Test());
+}
+
+TEST(MachMessageServer, Nonblocking) {
+ TestMachMessageServer test_mach_message_server(
+ TestMachMessageServer::kNonblocking);
+ EXPECT_EQ(1u, test_mach_message_server.Test());
+}
+
+TEST(MachMessageServer, PersistentTenMessages) {
+ TestMachMessageServer test_mach_message_server(
+ TestMachMessageServer::kPersistentTenMessages);
+ EXPECT_EQ(10u, test_mach_message_server.Test());
+}
+
+TEST(MachMessageServer, PersistentNonblockingFourMessages) {
+ TestMachMessageServer test_mach_message_server(
+ TestMachMessageServer::kPersistentNonblockingFourMessages);
+ EXPECT_EQ(4u, test_mach_message_server.Test());
+}
+
+} // namespace

Powered by Google App Engine
This is Rietveld 408576698