blob: 7edc5c0ce13aa17e8c8baf2573dbb8a4e5fd3253 [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_utils.h"
#include "test.h"
namespace {
#ifndef _WIN32
// Helper class used to catch SIGPIPE and set a global flag when
// it happens instead of terminating the current process. Will be
// used to verify that SigPipeBlocker works.
struct SigPipeCatcher {
SigPipeCatcher() {
// Install signal handler.
struct sigaction action = {};
action.sa_handler = [](int) { sigpipe_happened_ = 1; };
sigaction(SIGPIPE, &action, &prev_action_);
// Unblock signal
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGPIPE);
sigprocmask(SIG_UNBLOCK, &mask, &prev_mask_);
}
~SigPipeCatcher() {
sigaction(SIGPIPE, &prev_action_, nullptr);
sigprocmask(SIG_SETMASK, &prev_mask_, nullptr);
}
void Reset() { sigpipe_happened_ = 0; }
bool sigpipe_happened() const { return bool(sigpipe_happened_); }
struct sigaction prev_action_;
sigset_t prev_mask_;
static volatile sig_atomic_t sigpipe_happened_;
};
// static
volatile sig_atomic_t SigPipeCatcher::sigpipe_happened_ = 0;
#endif // !_WIN32
} // namespace
TEST(RemoteArguments, DefaultConstruction) {
RemoteArguments args;
EXPECT_EQ(0, args.argc());
EXPECT_FALSE(args.argv());
EXPECT_EQ(0u, args.args().size());
}
TEST(RemoteArguments, Construction) {
const char* const kTestArgs[] = {
"foo",
"bar",
};
RemoteArguments args(2, (char**)kTestArgs);
EXPECT_EQ(2, args.argc());
char** argv = args.argv();
ASSERT_TRUE(argv);
ASSERT_TRUE(argv[0]);
ASSERT_TRUE(argv[1]);
ASSERT_TRUE(!strcmp(argv[0], kTestArgs[0]));
ASSERT_TRUE(!strcmp(argv[1], kTestArgs[1]));
auto vec = args.args();
ASSERT_EQ(2u, vec.size());
ASSERT_EQ(vec[0], kTestArgs[0]);
ASSERT_EQ(vec[1], kTestArgs[1]);
}
TEST(RemoteArguments, Reset) {
const char* const kTestArgs[] = {
"foo",
"bar",
};
RemoteArguments args(2, (char**)kTestArgs);
EXPECT_EQ(2, args.argc());
std::vector<std::string> new_args;
new_args.push_back("zoo");
args.Reset(new_args);
EXPECT_EQ(1, args.argc());
char** argv = args.argv();
ASSERT_TRUE(argv);
ASSERT_TRUE(argv[0]);
ASSERT_EQ(new_args[0], argv[0]);
}
TEST(RemoteArguments, InsertAt) {
RemoteArguments args;
args.InsertAt(10, "foo");
ASSERT_EQ(1, args.argc());
ASSERT_EQ(args.args()[0], "foo");
args.InsertAt(0, "bar");
ASSERT_EQ(2, args.argc());
ASSERT_EQ(args.args()[0], "bar");
ASSERT_EQ(args.args()[1], "foo");
args.InsertAt(1, "zoo");
ASSERT_EQ(3, args.argc());
ASSERT_EQ(args.args()[0], "bar");
ASSERT_EQ(args.args()[1], "zoo");
ASSERT_EQ(args.args()[2], "foo");
}
TEST(WireEncoderAndDecoder, SimpleValues) {
WireEncoder encoder;
uint8_t byte1 = 12;
uint32_t word1 = 42;
uint8_t byte2 = 56;
encoder.Write(byte1);
encoder.Write(word1);
encoder.Write(byte2);
std::string r = encoder.TakeResult();
ASSERT_EQ(6u, r.size());
WireDecoder decoder(r);
uint8_t rbyte1 = 0;
uint32_t rword1 = 0;
uint8_t rbyte2 = 0;
decoder.Read(rbyte1);
ASSERT_FALSE(decoder.has_error());
decoder.Read(rword1);
ASSERT_FALSE(decoder.has_error());
decoder.Read(rbyte2);
ASSERT_FALSE(decoder.has_error());
ASSERT_EQ(byte1, rbyte1);
ASSERT_EQ(word1, rword1);
ASSERT_EQ(byte2, rbyte2);
decoder.Read(rbyte1);
ASSERT_TRUE(decoder.has_error());
}
TEST(WireEncoderAndDecoder, Strings) {
static const char* const kStrings[] = {
"foo_bar",
"",
"hello world!",
};
WireEncoder encoder;
size_t expected_size = 0;
for (const char* str : kStrings) {
encoder.Write(std::string(str));
expected_size += 4 + ::strlen(str);
}
std::string r = encoder.TakeResult();
ASSERT_EQ(expected_size, r.size());
WireDecoder decoder(r);
std::vector<std::string> result;
for (const char* str : kStrings) {
result.emplace_back(str);
decoder.Read(result.back());
}
ASSERT_FALSE(decoder.has_error());
auto it = result.begin();
for (const char* str : kStrings) {
ASSERT_EQ(*it, str);
++it;
}
}
#ifndef _WIN32
TEST(SigPipeBlocker, Test) {
SigPipeCatcher catcher;
// First, verify that the catcher works properly.
std::string error;
char byte[1] = { 'x' };
IpcHandle read_end, write_end;
ASSERT_TRUE(IpcHandle::CreatePipe(&read_end, &write_end, &error));
read_end.Close();
errno = 0;
ASSERT_FALSE(write_end.WriteFull(byte, 1, &error));
ASSERT_EQ(EPIPE, errno);
ASSERT_TRUE(catcher.sigpipe_happened());
write_end.Close();
// Then verify that the blocker works properly.
{
SigPipeBlocker blocker;
catcher.Reset();
ASSERT_TRUE(IpcHandle::CreatePipe(&read_end, &write_end, &error));
read_end.Close();
errno = 0;
ASSERT_FALSE(write_end.WriteFull(byte, 1, &error));
ASSERT_EQ(EPIPE, errno);
ASSERT_FALSE(catcher.sigpipe_happened());
write_end.Close();
}
// Do it again, to verify the destructor released the action.
ASSERT_TRUE(IpcHandle::CreatePipe(&read_end, &write_end, &error));
read_end.Close();
errno = 0;
ASSERT_FALSE(write_end.WriteFull(byte, 1, &error));
ASSERT_EQ(EPIPE, errno);
ASSERT_TRUE(catcher.sigpipe_happened());
}
#endif // !_WIN32