| // 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 |