blob: 47412e2028d7966cd953f7094ba18a1ee0be7857 [file] [log] [blame]
// Copyright 2023 Google Inc. 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 "ipc_handle.h"
#include <stdio.h>
#include <string.h>
#include "test.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <fcntl.h>
#include <unistd.h>
#endif
namespace {
IpcHandle::HandleType CreateTestNativeHandle() {
#ifdef _WIN32
return CreateFileW(L"NUL", GENERIC_READ | GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, NULL);
#else
return open("/dev/null", O_RDWR);
#endif
}
IpcHandle CreateTestHandle() {
return { CreateTestNativeHandle() };
}
// Create a pipe for testing. On success return true and
// set |*read| and |*write| to be the read and write ends
// of the pipe. Note that the pipe is unidirectional due
// to portability with Windows.
bool CreateTestPipe(IpcHandle* read, IpcHandle* write) {
std::string error;
if (!IpcHandle::CreatePipe(read, write, &error)) {
fprintf(stderr, "ERROR: when creating pipe: %s\n", error.c_str());
return false;
}
return true;
}
} // namespace
TEST(IpcHandle, Constructor) {
// Default constructor makes invalid handle
IpcHandle empty;
EXPECT_FALSE(empty);
EXPECT_EQ(IpcHandle::kInvalid, empty.native_handle());
// Constructor gets test native handle
IpcHandle::HandleType test_native_handle = CreateTestNativeHandle();
IpcHandle test_handle(test_native_handle);
EXPECT_TRUE(test_handle);
EXPECT_EQ(test_native_handle, test_handle.native_handle());
// Move constructor works.
IpcHandle test_handle2 = std::move(test_handle);
EXPECT_TRUE(test_handle2);
EXPECT_EQ(test_native_handle, test_handle2.native_handle());
EXPECT_FALSE(test_handle);
EXPECT_EQ(IpcHandle::kInvalid, test_handle.native_handle());
}
TEST(IpcHandle, Close) {
IpcHandle test_handle = CreateTestHandle();
EXPECT_TRUE(test_handle);
EXPECT_NE(IpcHandle::kInvalid, test_handle.native_handle());
test_handle.Close();
EXPECT_FALSE(test_handle);
EXPECT_EQ(IpcHandle::kInvalid, test_handle.native_handle());
// Ensure closing a closed handle doesn't crash.
test_handle.Close();
EXPECT_FALSE(test_handle);
EXPECT_EQ(IpcHandle::kInvalid, test_handle.native_handle());
}
TEST(IpcHandle, ReadWrite) {
const char kInputData[] = "Hello World!";
const size_t kInputDataSize = sizeof(kInputData); // includes terminating \0
IpcHandle pipe_read;
IpcHandle pipe_write;
ASSERT_TRUE(CreateTestPipe(&pipe_read, &pipe_write));
std::string error_message;
ssize_t count = pipe_write.Write(kInputData, kInputDataSize, &error_message);
EXPECT_EQ(static_cast<ssize_t>(kInputDataSize), count);
EXPECT_TRUE(error_message.empty());
char data[kInputDataSize] = {};
count = pipe_read.Read(data, sizeof(data), &error_message);
EXPECT_EQ(static_cast<ssize_t>(kInputDataSize), count);
EXPECT_TRUE(error_message.empty());
// Check transfered content.
EXPECT_TRUE(!memcmp(data, kInputData, kInputDataSize));
// Close the write pipe, trying to read should result in zero
// being returned without an error (indicating closed pipe).
pipe_write.Close();
count = pipe_read.Read(data, kInputDataSize, &error_message);
EXPECT_EQ(0, count);
EXPECT_TRUE(error_message.empty());
}
TEST(IpcHandle, Clone) {
const char kInputData[] = "Hello World!";
const size_t kInputDataSize = sizeof(kInputData); // includes terminating \0
IpcHandle pipe_read;
IpcHandle pipe_write;
ASSERT_TRUE(CreateTestPipe(&pipe_read, &pipe_write));
// Clone pipe_read, then close it.
IpcHandle pipe_read2 = pipe_read.Clone();
pipe_read.Close();
std::string error_message;
ssize_t count = pipe_write.Write(kInputData, kInputDataSize, &error_message);
EXPECT_EQ(static_cast<ssize_t>(kInputDataSize), count);
EXPECT_TRUE(error_message.empty());
char data[kInputDataSize] = {};
count = pipe_read2.Read(data, sizeof(data), &error_message);
EXPECT_EQ(static_cast<ssize_t>(kInputDataSize), count);
EXPECT_TRUE(error_message.empty());
// Check transfered content.
EXPECT_TRUE(!memcmp(data, kInputData, kInputDataSize));
// Close the write pipe, trying to read should result in zero
// being returned without an error (indicating closed pipe).
pipe_write.Close();
count = pipe_read2.Read(data, kInputDataSize, &error_message);
EXPECT_EQ(0, count);
EXPECT_TRUE(error_message.empty());
}
TEST(IpcHandle, ReadWriteFull) {
const char kInputData[] = "Hello World!";
const size_t kInputDataSize = sizeof(kInputData); // includes terminating \0
IpcHandle pipe_read;
IpcHandle pipe_write;
ASSERT_TRUE(CreateTestPipe(&pipe_read, &pipe_write));
std::string error_message;
EXPECT_TRUE(pipe_write.WriteFull(kInputData, kInputDataSize, &error_message));
EXPECT_TRUE(error_message.empty());
char data[kInputDataSize] = {};
EXPECT_TRUE(pipe_read.ReadFull(data, kInputDataSize, &error_message));
EXPECT_TRUE(error_message.empty());
// Check transfered content.
EXPECT_TRUE(!memcmp(data, kInputData, kInputDataSize));
// Close the write pipe, trying to read should result in an error.
pipe_write.Close();
EXPECT_FALSE(pipe_read.ReadFull(data, kInputDataSize, &error_message));
EXPECT_FALSE(error_message.empty());
}
TEST(IpcHandle, BindConnectAndAccept) {
std::string error_message;
StringPiece service = "test_service";
ASSERT_FALSE(IpcServiceHandle::IsBound(service));
// Tring to connect to a service without a server should fail.
IpcHandle no_client = IpcServiceHandle::ConnectTo(service, &error_message);
EXPECT_FALSE(no_client);
EXPECT_FALSE(error_message.empty());
ASSERT_FALSE(IpcServiceHandle::IsBound(service));
// Create server
IpcServiceHandle server = IpcServiceHandle::BindTo(service, &error_message);
ASSERT_TRUE(server);
EXPECT_FALSE(error_message.empty());
error_message.clear();
ASSERT_TRUE(IpcServiceHandle::IsBound(service));
// Trying to create a secondary server for the same service should fail.
IpcServiceHandle no_server =
IpcServiceHandle::BindTo(service, &error_message);
ASSERT_FALSE(no_server);
EXPECT_FALSE(error_message.empty());
error_message.clear();
// Connecto the server, and receive a peer handle.
IpcHandle client = IpcServiceHandle::ConnectTo(service, &error_message);
EXPECT_TRUE(client);
EXPECT_TRUE(error_message.empty());
IpcHandle peer = server.AcceptClient(&error_message);
EXPECT_TRUE(peer);
EXPECT_TRUE(error_message.empty());
// Send data from the client to the peer.
const char kInput[] = "sending data";
const size_t kInputSize = sizeof(kInput);
EXPECT_TRUE(client.WriteFull(kInput, kInputSize, &error_message));
EXPECT_TRUE(error_message.empty());
char output[kInputSize] = {};
EXPECT_TRUE(peer.ReadFull(output, sizeof(output), &error_message));
EXPECT_TRUE(error_message.empty());
EXPECT_TRUE(!memcmp(output, kInput, kInputSize));
// Close all handles and verify that IsBound() returns false.
// Note that on Unix, only closing the server handle is necessary for this,
// but the way Win32 named pipes work require all client handles closed too.
peer.Close();
client.Close();
server.Close();
ASSERT_FALSE(IpcServiceHandle::IsBound(service));
}
TEST(IpcHandle, BindAcceptWithTimeout) {
std::string error_message;
StringPiece service = "test_service";
// Create server
IpcServiceHandle server = IpcServiceHandle::BindTo(service, &error_message);
ASSERT_TRUE(server);
EXPECT_TRUE(error_message.empty());
// Accepting should time out since no client is trying to connect.
bool did_timeout = false;
IpcHandle connection = server.AcceptClient(10, &did_timeout, &error_message);
EXPECT_FALSE(connection);
EXPECT_TRUE(did_timeout);
EXPECT_FALSE(error_message.empty());
error_message.clear();
// Connecto the server, and receive a peer handle.
IpcHandle client = IpcServiceHandle::ConnectTo(service, &error_message);
EXPECT_TRUE(client);
EXPECT_TRUE(error_message.empty());
// Accepting should not work.
connection = server.AcceptClient(10, &did_timeout, &error_message);
EXPECT_TRUE(connection);
EXPECT_FALSE(did_timeout);
EXPECT_TRUE(error_message.empty());
}
TEST(IpcHandle, SendAndReceiveNativeHandle) {
IpcHandle pipe1_read;
IpcHandle pipe1_write;
ASSERT_TRUE(CreateTestPipe(&pipe1_read, &pipe1_write));
std::string error_message;
StringPiece service = "test_service";
IpcServiceHandle server = IpcServiceHandle::BindTo(service, &error_message);
ASSERT_TRUE(server);
EXPECT_TRUE(error_message.empty());
IpcHandle client = IpcServiceHandle::ConnectTo(service, &error_message);
EXPECT_TRUE(client);
EXPECT_TRUE(error_message.empty());
IpcHandle peer = server.AcceptClient(&error_message);
EXPECT_TRUE(peer);
EXPECT_TRUE(error_message.empty());
// Send pipe1_write.native_handle() from client to peer.
EXPECT_TRUE(
client.SendNativeHandle(pipe1_write.native_handle(), &error_message));
EXPECT_TRUE(error_message.empty());
IpcHandle received;
EXPECT_TRUE(peer.ReceiveNativeHandle(&received, &error_message));
EXPECT_TRUE(error_message.empty());
// Write to |received|, this should send data to the first pipe.
const char kInputData[] = "Bonjour monde!";
const size_t kInputDataSize = sizeof(kInputData);
EXPECT_TRUE(received.WriteFull(kInputData, kInputDataSize, &error_message));
EXPECT_TRUE(error_message.empty());
char data[kInputDataSize] = {};
ssize_t count = pipe1_read.Read(data, sizeof(data), &error_message);
EXPECT_EQ(static_cast<ssize_t>(sizeof(data)), count);
EXPECT_TRUE(error_message.empty());
// Verify content.
EXPECT_TRUE(!memcmp(data, kInputData, kInputDataSize));
}