crashpad/util/mach/child_port_handshake_test.cc
Mark Mentovai 4b450c8137 test: Use (actual, [un]expected) in gtest {ASSERT,EXPECT}_{EQ,NE}
gtest used to require (expected, actual) ordering for arguments to
EXPECT_EQ and ASSERT_EQ, and in failed test assertions would identify
each side as “expected” or “actual.” Tests in Crashpad adhered to this
traditional ordering. After a gtest change in February 2016, it is now
agnostic with respect to the order of these arguments.

This change mechanically updates all uses of these macros to (actual,
expected) by reversing them. This provides consistency with our use of
the logging CHECK_EQ and DCHECK_EQ macros, and makes for better
readability by ordinary native speakers. The rough (but working!)
conversion tool is
https://chromium-review.googlesource.com/c/466727/1/rewrite_expectassert_eq.py,
and “git cl format” cleaned up its output.

EXPECT_NE and ASSERT_NE never had a preferred ordering. gtest never made
a judgment that one side or the other needed to provide an “unexpected”
value. Consequently, some code used (unexpected, actual) while other
code used (actual, unexpected). For consistency with the new EXPECT_EQ
and ASSERT_EQ usage, as well as consistency with CHECK_NE and DCHECK_NE,
this change also updates these use sites to (actual, unexpected) where
one side can be called “unexpected” as, for example, std::string::npos
can be. Unfortunately, this portion was a manual conversion.

References:

https://github.com/google/googletest/blob/master/googletest/docs/Primer.md#binary-comparison
77d6b17338
https://github.com/google/googletest/pull/713

Change-Id: I978fef7c94183b8b1ef63f12f5ab4d6693626be3
Reviewed-on: https://chromium-review.googlesource.com/466727
Reviewed-by: Scott Graham <scottmg@chromium.org>
2017-04-04 12:34:24 +00:00

383 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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/child_port_handshake.h"
#include "base/mac/scoped_mach_port.h"
#include "gtest/gtest.h"
#include "test/multiprocess.h"
#include "util/mach/child_port_types.h"
#include "util/mach/mach_extensions.h"
namespace crashpad {
namespace test {
namespace {
class ChildPortHandshakeTest : public Multiprocess {
public:
enum class ClientProcess {
// The child runs the client and the parent runs the server.
kChildClient = 0,
// The parent runs the client and the child runs the server.
kParentClient,
};
enum class TestType {
// The client checks in with the server, transferring a receive right.
kClientChecksIn_ReceiveRight = 0,
// In this test, the client checks in with the server normally. It sends a
// copy of its bootstrap port to the server, because both parent and child
// should have the same bootstrap port, allowing for verification.
kClientChecksIn_SendRight,
// The client checks in with the server, transferring a send-once right.
kClientChecksIn_SendOnceRight,
// In this test, the client reads from its pipe, and subsequently exits
// without checking in. This tests that the server properly detects that it
// has lost its client after sending instructions to it via the pipe, while
// waiting for a check-in message.
kClientDoesNotCheckIn,
// In this test, the client exits without checking in. This tests that the
// server properly detects that it has lost a client. Whether or not the
// client closes the pipe before the server writes to it is a race, and the
// server needs to be able to detect client loss in both cases, so the
// ClientDoesNotCheckIn_ReadsPipe and NoClient tests also exist to test
// these individual cases more deterministically.
kClientDoesNotCheckIn_ReadsPipe,
// In this test, the client checks in with the server with an incorrect
// token value and a copy of its own task port. The server should reject the
// message because of the invalid token, and return MACH_PORT_NULL to its
// caller.
kTokenIncorrect,
// In this test, the client checks in with the server with an incorrect
// token value and a copy of its own task port, and subsequently, the
// correct token value and a copy of its bootstrap port. The server should
// reject the first because of the invalid token, but it should continue
// waiting for a message with a valid token as long as the pipe remains
// open. It should wind wind up returning the bootstrap port, allowing for
// verification.
kTokenIncorrectThenCorrect,
// The server dies. The failure should be reported in the client. This test
// type is only compatible with ClientProcess::kParentClient.
kServerDies,
};
ChildPortHandshakeTest(ClientProcess client_process, TestType test_type)
: Multiprocess(),
child_port_handshake_(),
client_process_(client_process),
test_type_(test_type) {
}
~ChildPortHandshakeTest() {
}
private:
void RunServer() {
if (test_type_ == TestType::kServerDies) {
return;
}
base::mac::ScopedMachReceiveRight receive_right;
base::mac::ScopedMachSendRight send_right;
if (test_type_ == TestType::kClientChecksIn_ReceiveRight) {
receive_right.reset(child_port_handshake_.RunServer(
ChildPortHandshake::PortRightType::kReceiveRight));
} else {
send_right.reset(child_port_handshake_.RunServer(
ChildPortHandshake::PortRightType::kSendRight));
}
switch (test_type_) {
case TestType::kClientChecksIn_ReceiveRight:
EXPECT_TRUE(receive_right.is_valid());
break;
case TestType::kClientChecksIn_SendRight:
case TestType::kTokenIncorrectThenCorrect:
EXPECT_EQ(send_right, bootstrap_port);
break;
case TestType::kClientChecksIn_SendOnceRight:
EXPECT_TRUE(send_right.is_valid());
EXPECT_NE(send_right, bootstrap_port);
break;
case TestType::kClientDoesNotCheckIn:
case TestType::kClientDoesNotCheckIn_ReadsPipe:
case TestType::kTokenIncorrect:
EXPECT_FALSE(send_right.is_valid());
break;
case TestType::kServerDies:
// This was special-cased as an early return above.
FAIL();
break;
}
}
void RunClient() {
switch (test_type_) {
case TestType::kClientChecksIn_SendRight: {
ASSERT_TRUE(child_port_handshake_.RunClient(bootstrap_port,
MACH_MSG_TYPE_COPY_SEND));
break;
}
case TestType::kClientChecksIn_ReceiveRight: {
mach_port_t receive_right = NewMachPort(MACH_PORT_RIGHT_RECEIVE);
ASSERT_TRUE(child_port_handshake_.RunClient(
receive_right, MACH_MSG_TYPE_MOVE_RECEIVE));
break;
}
case TestType::kClientChecksIn_SendOnceRight: {
base::mac::ScopedMachReceiveRight receive_right(
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
ASSERT_TRUE(child_port_handshake_.RunClient(
receive_right.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE));
break;
}
case TestType::kClientDoesNotCheckIn: {
child_port_handshake_.ServerWriteFD().reset();
child_port_handshake_.ClientReadFD().reset();
break;
}
case TestType::kClientDoesNotCheckIn_ReadsPipe: {
// Dont run the standard client routine. Instead, drain the pipe, which
// will get the parent to the point that it begins waiting for a
// check-in message. Then, exit. The pipe is drained using the same
// implementation that the real client would use.
child_port_handshake_.ServerWriteFD().reset();
base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD();
child_port_token_t token;
std::string service_name;
ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe(
client_read_fd.get(), &token, &service_name));
break;
}
case TestType::kTokenIncorrect: {
// Dont run the standard client routine. Instead, read the token and
// service name, mutate the token, and then check in with the bad token.
// The parent should reject the message.
child_port_handshake_.ServerWriteFD().reset();
base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD();
child_port_token_t token;
std::string service_name;
ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe(
client_read_fd.get(), &token, &service_name));
child_port_token_t bad_token = ~token;
ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn(
service_name,
bad_token,
mach_task_self(),
MACH_MSG_TYPE_COPY_SEND));
break;
}
case TestType::kTokenIncorrectThenCorrect: {
// Dont run the standard client routine. Instead, read the token and
// service name. Mutate the token, and check in with the bad token,
// expecting the parent to reject the message. Then, check in with the
// correct token, expecting the parent to accept it.
child_port_handshake_.ServerWriteFD().reset();
base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD();
child_port_token_t token;
std::string service_name;
ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe(
client_read_fd.release(), &token, &service_name));
child_port_token_t bad_token = ~token;
ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn(
service_name,
bad_token,
mach_task_self(),
MACH_MSG_TYPE_COPY_SEND));
ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn(
service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND));
break;
}
case TestType::kServerDies: {
ASSERT_EQ(client_process_, ClientProcess::kParentClient);
ASSERT_FALSE(child_port_handshake_.RunClient(bootstrap_port,
MACH_MSG_TYPE_COPY_SEND));
break;
}
}
}
// Multiprocess:
void MultiprocessParent() override {
switch (client_process_) {
case ClientProcess::kChildClient:
RunServer();
break;
case ClientProcess::kParentClient:
RunClient();
break;
}
}
void MultiprocessChild() override {
switch (client_process_) {
case ClientProcess::kChildClient:
RunClient();
break;
case ClientProcess::kParentClient:
RunServer();
break;
}
}
private:
ChildPortHandshake child_port_handshake_;
ClientProcess client_process_;
TestType test_type_;
DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeTest);
};
TEST(ChildPortHandshake, ChildClientChecksIn_ReceiveRight) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight);
test.Run();
}
TEST(ChildPortHandshake, ChildClientChecksIn_SendRight) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight);
test.Run();
}
TEST(ChildPortHandshake, ChildClientChecksIn_SendOnceRight) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight);
test.Run();
}
TEST(ChildPortHandshake, ChildClientDoesNotCheckIn) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn);
test.Run();
}
TEST(ChildPortHandshake, ChildClientDoesNotCheckIn_ReadsPipe) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe);
test.Run();
}
TEST(ChildPortHandshake, ChildClientTokenIncorrect) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kTokenIncorrect);
test.Run();
}
TEST(ChildPortHandshake, ChildClientTokenIncorrectThenCorrect) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kChildClient,
ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect);
test.Run();
}
TEST(ChildPortHandshake, ParentClientChecksIn_ReceiveRight) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight);
test.Run();
}
TEST(ChildPortHandshake, ParentClientChecksIn_SendRight) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight);
test.Run();
}
TEST(ChildPortHandshake, ParentClientChecksIn_SendOnceRight) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight);
test.Run();
}
TEST(ChildPortHandshake, ParentClientDoesNotCheckIn) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn);
test.Run();
}
TEST(ChildPortHandshake, ParentClientDoesNotCheckIn_ReadsPipe) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe);
test.Run();
}
TEST(ChildPortHandshake, ParentClientTokenIncorrect) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kTokenIncorrect);
test.Run();
}
TEST(ChildPortHandshake, ParentClientTokenIncorrectThenCorrect) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect);
test.Run();
}
TEST(ChildPortHandshake, ParentClientServerDies) {
ChildPortHandshakeTest test(
ChildPortHandshakeTest::ClientProcess::kParentClient,
ChildPortHandshakeTest::TestType::kServerDies);
test.Run();
}
TEST(ChildPortHandshake, NoClient) {
// In this test, the client never checks in with the server because it never
// even runs. This tests that the server properly detects that it has no
// client at all, and does not terminate execution with an error such as
// “broken pipe” when attempting to send instructions to the client. This test
// is similar to kClientDoesNotCheckIn, but because theres no client at all,
// the server is guaranteed to see that its pipe partner is gone.
ChildPortHandshake child_port_handshake;
base::mac::ScopedMachSendRight child_port(child_port_handshake.RunServer(
ChildPortHandshake::PortRightType::kSendRight));
EXPECT_FALSE(child_port.is_valid());
}
} // namespace
} // namespace test
} // namespace crashpad