#include "lldb/Host/Pipe.h"
#include "TestingSupport/SubsystemRAII.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <chrono>
#include <fcntl.h>
#include <future>
#include <numeric>
#include <thread>
#include <vector>
using namespace lldb_private;
class PipeTest : public testing::Test {
public:
SubsystemRAII<FileSystem, HostInfo> subsystems;
};
TEST_F(PipeTest, CreateWithUniqueName) {
Pipe pipe;
llvm::SmallString<0> name;
ASSERT_THAT_ERROR(
pipe.CreateWithUniqueName("PipeTest-CreateWithUniqueName", name)
.ToError(),
llvm::Succeeded());
}
#ifndef _WIN32
TEST_F(PipeTest, OpenAsReader) {
Pipe pipe;
llvm::SmallString<0> name;
ASSERT_THAT_ERROR(
pipe.CreateWithUniqueName("PipeTest-OpenAsReader", name).ToError(),
llvm::Succeeded());
size_t name_len = name.size();
name += "foobar";
llvm::StringRef name_ref(name.data(), name_len);
ASSERT_THAT_ERROR(pipe.OpenAsReader(name_ref).ToError(), llvm::Succeeded());
ASSERT_TRUE(pipe.CanRead());
}
#endif
#ifndef _WIN32
TEST_F(PipeTest, WriteWithTimeout) {
Pipe pipe;
ASSERT_THAT_ERROR(pipe.CreateNew().ToError(), llvm::Succeeded());
#if !defined(_WIN32) && defined(F_SETPIPE_SZ)
::fcntl(pipe.GetWriteFileDescriptor(), F_SETPIPE_SZ, 4096);
#endif
const size_t buf_size = 66000;
const size_t write_chunk_size = 234;
std::vector<int32_t> write_buf(buf_size / sizeof(int32_t));
std::iota(write_buf.begin(), write_buf.end(), 0);
std::vector<int32_t> read_buf(write_buf.size() + 100, -1);
char *write_ptr = reinterpret_cast<char *>(write_buf.data());
char *read_ptr = reinterpret_cast<char *>(read_buf.data());
size_t write_bytes = 0;
size_t read_bytes = 0;
while (write_bytes + write_chunk_size <= buf_size) {
llvm::Expected<size_t> num_bytes =
pipe.Write(write_ptr + write_bytes, write_chunk_size,
std::chrono::milliseconds(10));
if (num_bytes) {
write_bytes += *num_bytes;
} else {
ASSERT_THAT_ERROR(num_bytes.takeError(), llvm::Failed());
break;
}
}
ASSERT_LE(write_bytes + write_chunk_size, buf_size)
<< "Pipe buffer larger than expected";
auto start_time = std::chrono::steady_clock::now();
ASSERT_THAT_EXPECTED(pipe.Write(write_ptr + write_bytes, write_chunk_size,
std::chrono::seconds(2)),
llvm::Failed());
auto dur = std::chrono::steady_clock::now() - start_time;
ASSERT_GE(dur, std::chrono::seconds(2));
start_time = std::chrono::steady_clock::now();
ASSERT_THAT_EXPECTED(pipe.Write(write_ptr + write_bytes, write_chunk_size,
std::chrono::milliseconds(200)),
llvm::Failed());
dur = std::chrono::steady_clock::now() - start_time;
ASSERT_GE(dur, std::chrono::milliseconds(200));
ASSERT_LT(dur, std::chrono::seconds(2));
while (read_bytes < write_bytes) {
llvm::Expected<size_t> num_bytes =
pipe.Read(read_ptr + read_bytes, write_bytes - read_bytes,
std::chrono::milliseconds(10));
ASSERT_THAT_EXPECTED(num_bytes, llvm::Succeeded());
read_bytes += *num_bytes;
}
ASSERT_THAT_EXPECTED(
pipe.Read(read_ptr + read_bytes, 100, std::chrono::milliseconds(10)),
llvm::Failed());
ASSERT_EQ(write_bytes, read_bytes);
ASSERT_TRUE(std::equal(write_buf.begin(),
write_buf.begin() + write_bytes / sizeof(uint32_t),
read_buf.begin()));
ASSERT_THAT_EXPECTED(
pipe.Write(write_ptr, write_chunk_size, std::chrono::milliseconds(10)),
llvm::Succeeded());
}
TEST_F(PipeTest, ReadWithTimeout) {
Pipe pipe;
ASSERT_THAT_ERROR(pipe.CreateNew().ToError(), llvm::Succeeded());
char buf[100];
ASSERT_THAT_EXPECTED(pipe.Read(buf, sizeof(buf), std::chrono::seconds(0)),
llvm::Failed());
auto start = std::chrono::steady_clock::now();
ASSERT_THAT_EXPECTED(
pipe.Read(buf, sizeof(buf), std::chrono::milliseconds(200)),
llvm::Failed());
auto dur = std::chrono::steady_clock::now() - start;
EXPECT_GT(dur, std::chrono::milliseconds(200));
EXPECT_LT(dur, std::chrono::seconds(2));
llvm::StringRef hello_world("Hello world!");
ASSERT_THAT_EXPECTED(pipe.Write(hello_world.data(), hello_world.size()),
llvm::HasValue(hello_world.size()));
ASSERT_THAT_EXPECTED(pipe.Read(buf, sizeof(buf)),
llvm::HasValue(hello_world.size()));
EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world);
memset(buf, 0, sizeof(buf));
ASSERT_THAT_EXPECTED(pipe.Write(hello_world.data(), hello_world.size()),
llvm::HasValue(hello_world.size()));
ASSERT_THAT_EXPECTED(pipe.Read(buf, 4), llvm::HasValue(4));
ASSERT_THAT_EXPECTED(pipe.Read(buf + 4, sizeof(buf) - 4),
llvm::HasValue(hello_world.size() - 4));
EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world);
memset(buf, 0, sizeof(buf));
std::future<llvm::Expected<size_t>> future_num_bytes = std::async(
std::launch::async, [&] { return pipe.Read(buf, sizeof(buf)); });
std::this_thread::sleep_for(std::chrono::milliseconds(10));
ASSERT_THAT_EXPECTED(pipe.Write(hello_world.data(), hello_world.size()),
llvm::HasValue(hello_world.size()));
ASSERT_THAT_EXPECTED(future_num_bytes.get(),
llvm::HasValue(hello_world.size()));
EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world);
}
#endif