Index: util/mach/child_port_handshake_test.cc |
diff --git a/util/mach/child_port_handshake_test.cc b/util/mach/child_port_handshake_test.cc |
index 12ca424c54cc20b269d3cb5417cac4a79a80d7b3..f77107b53982614c66074238711cb3f372006369 100644 |
--- a/util/mach/child_port_handshake_test.cc |
+++ b/util/mach/child_port_handshake_test.cc |
@@ -26,161 +26,355 @@ namespace { |
class ChildPortHandshakeTest : public Multiprocess { |
public: |
- enum TestType { |
- kTestTypeChildChecksIn = 0, |
- kTestTypeChildDoesNotCheckIn_ReadsPipe, |
- kTestTypeChildDoesNotCheckIn, |
- kTestTypeTokenIncorrect, |
- kTestTypeTokenIncorrectThenCorrect, |
+ 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, |
}; |
- explicit ChildPortHandshakeTest(TestType test_type) |
- : Multiprocess(), child_port_handshake_(), test_type_(test_type) {} |
- ~ChildPortHandshakeTest() {} |
+ ChildPortHandshakeTest(ClientProcess client_process, TestType test_type) |
+ : Multiprocess(), |
+ child_port_handshake_(), |
+ client_process_(client_process), |
+ test_type_(test_type) { |
+ } |
+ |
+ ~ChildPortHandshakeTest() { |
+ } |
private: |
- // Multiprocess: |
+ 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)); |
+ } |
- void MultiprocessParent() override { |
- base::mac::ScopedMachSendRight child_port( |
- child_port_handshake_.RunServer()); |
switch (test_type_) { |
- case kTestTypeChildChecksIn: |
- case kTestTypeTokenIncorrectThenCorrect: |
- EXPECT_EQ(bootstrap_port, child_port); |
+ case TestType::kClientChecksIn_ReceiveRight: |
+ EXPECT_TRUE(receive_right.is_valid()); |
+ break; |
+ |
+ case TestType::kClientChecksIn_SendRight: |
+ case TestType::kTokenIncorrectThenCorrect: |
+ EXPECT_EQ(bootstrap_port, send_right); |
+ break; |
+ |
+ case TestType::kClientChecksIn_SendOnceRight: |
+ EXPECT_TRUE(send_right.is_valid()); |
+ EXPECT_NE(bootstrap_port, send_right); |
+ break; |
+ |
+ case TestType::kClientDoesNotCheckIn: |
+ case TestType::kClientDoesNotCheckIn_ReadsPipe: |
+ case TestType::kTokenIncorrect: |
+ EXPECT_FALSE(send_right.is_valid()); |
break; |
- case kTestTypeChildDoesNotCheckIn_ReadsPipe: |
- case kTestTypeChildDoesNotCheckIn: |
- case kTestTypeTokenIncorrect: |
- EXPECT_EQ(kMachPortNull, child_port); |
+ case TestType::kServerDies: |
+ // This was special-cased as an early return above. |
+ FAIL(); |
break; |
} |
} |
- void MultiprocessChild() override { |
- int read_pipe = child_port_handshake_.ReadPipeFD(); |
+ void RunClient() { |
switch (test_type_) { |
- case kTestTypeChildChecksIn: |
- ChildPortHandshake::RunClient( |
- read_pipe, bootstrap_port, MACH_MSG_TYPE_COPY_SEND); |
+ 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 kTestTypeChildDoesNotCheckIn_ReadsPipe: { |
+ case TestType::kClientDoesNotCheckIn_ReadsPipe: { |
// Don’t 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; |
- ChildPortHandshake::RunClientInternal_ReadPipe( |
- read_pipe, &token, &service_name); |
+ ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( |
+ client_read_fd.get(), &token, &service_name)); |
break; |
} |
- case kTestTypeChildDoesNotCheckIn: |
- break; |
- |
- case kTestTypeTokenIncorrect: { |
+ case TestType::kTokenIncorrect: { |
// Don’t 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; |
- ChildPortHandshake::RunClientInternal_ReadPipe( |
- read_pipe, &token, &service_name); |
+ ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( |
+ client_read_fd.get(), &token, &service_name)); |
child_port_token_t bad_token = ~token; |
- ChildPortHandshake::RunClientInternal_SendCheckIn( |
- service_name, bad_token, mach_task_self(), MACH_MSG_TYPE_COPY_SEND); |
+ ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( |
+ service_name, |
+ bad_token, |
+ mach_task_self(), |
+ MACH_MSG_TYPE_COPY_SEND)); |
break; |
} |
- case kTestTypeTokenIncorrectThenCorrect: { |
+ case TestType::kTokenIncorrectThenCorrect: { |
// Don’t 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; |
- ChildPortHandshake::RunClientInternal_ReadPipe( |
- read_pipe, &token, &service_name); |
+ ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( |
+ client_read_fd.release(), &token, &service_name)); |
child_port_token_t bad_token = ~token; |
- ChildPortHandshake::RunClientInternal_SendCheckIn( |
- service_name, bad_token, mach_task_self(), MACH_MSG_TYPE_COPY_SEND); |
- ChildPortHandshake::RunClientInternal_SendCheckIn( |
- service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND); |
+ 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(ClientProcess::kParentClient, client_process_); |
+ 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, ChildChecksIn) { |
- // 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. |
- ChildPortHandshakeTest test(ChildPortHandshakeTest::kTestTypeChildChecksIn); |
+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, ChildDoesNotCheckIn) { |
- // 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 |
- // ChildDoesNotCheckIn_ReadsPipe and NoChild tests also exist to test these |
- // individual cases more deterministically. |
+TEST(ChildPortHandshake, ParentClientDoesNotCheckIn_ReadsPipe) { |
ChildPortHandshakeTest test( |
- ChildPortHandshakeTest::kTestTypeChildDoesNotCheckIn); |
+ ChildPortHandshakeTest::ClientProcess::kParentClient, |
+ ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe); |
test.Run(); |
} |
-TEST(ChildPortHandshake, ChildDoesNotCheckIn_ReadsPipe) { |
- // 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. |
+TEST(ChildPortHandshake, ParentClientTokenIncorrect) { |
ChildPortHandshakeTest test( |
- ChildPortHandshakeTest::kTestTypeChildDoesNotCheckIn_ReadsPipe); |
+ ChildPortHandshakeTest::ClientProcess::kParentClient, |
+ ChildPortHandshakeTest::TestType::kTokenIncorrect); |
test.Run(); |
} |
-TEST(ChildPortHandshake, TokenIncorrect) { |
- // 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. |
- ChildPortHandshakeTest test(ChildPortHandshakeTest::kTestTypeTokenIncorrect); |
+TEST(ChildPortHandshake, ParentClientTokenIncorrectThenCorrect) { |
+ ChildPortHandshakeTest test( |
+ ChildPortHandshakeTest::ClientProcess::kParentClient, |
+ ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect); |
test.Run(); |
} |
-TEST(ChildPortHandshake, TokenIncorrectThenCorrect) { |
- // 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. |
+TEST(ChildPortHandshake, ParentClientServerDies) { |
ChildPortHandshakeTest test( |
- ChildPortHandshakeTest::kTestTypeTokenIncorrectThenCorrect); |
+ ChildPortHandshakeTest::ClientProcess::kParentClient, |
+ ChildPortHandshakeTest::TestType::kServerDies); |
test.Run(); |
} |
-TEST(ChildPortHandshake, NoChild) { |
- // In this test, the client never checks in with the parent because the child |
- // 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 |
+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 ChildDoesNotCheckIn, but because there’s no child at all, the |
- // server is guaranteed to see that its pipe partner is gone. |
+ // is similar to kClientDoesNotCheckIn, but because there’s 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()); |
- EXPECT_EQ(kMachPortNull, child_port); |
+ base::mac::ScopedMachSendRight child_port(child_port_handshake.RunServer( |
+ ChildPortHandshake::PortRightType::kSendRight)); |
+ EXPECT_FALSE(child_port.is_valid()); |
} |
} // namespace |