19 #include <sys/types.h> 22 #include <boost/container/flat_set.hpp> 23 #include <glog/logging.h> 39 using namespace
folly;
40 using namespace
std::chrono_literals;
42 TEST(SimpleSubprocessTest, ExitsSuccessfully) {
43 Subprocess proc(std::vector<std::string>{
"/bin/true"});
47 TEST(SimpleSubprocessTest, ExitsSuccessfullyChecked) {
48 Subprocess proc(std::vector<std::string>{
"/bin/true"});
52 TEST(SimpleSubprocessTest, CloneFlagsWithVfork) {
54 std::vector<std::string>{
"/bin/true"},
59 TEST(SimpleSubprocessTest, CloneFlagsWithFork) {
61 std::vector<std::string>{
"/bin/true"},
66 TEST(SimpleSubprocessTest, CloneFlagsSubprocessCtorExitsAfterExec) {
68 std::vector<std::string>{
"/bin/sleep",
"3600"},
71 auto retCode = proc.wait();
75 TEST(SimpleSubprocessTest, ExitsWithError) {
76 Subprocess proc(std::vector<std::string>{
"/bin/false"});
80 TEST(SimpleSubprocessTest, ExitsWithErrorChecked) {
81 Subprocess proc(std::vector<std::string>{
"/bin/false"});
85 TEST(SimpleSubprocessTest, DefaultConstructibleProcessReturnCode) {
90 TEST(SimpleSubprocessTest, MoveSubprocess) {
91 Subprocess old_proc(std::vector<std::string>{
"/bin/true"});
96 EXPECT_EQ(0, new_proc.wait().exitStatus());
100 TEST(SimpleSubprocessTest, DefaultConstructor) {
105 auto p1 =
Subprocess(std::vector<std::string>{
"/bin/true"});
113 #define EXPECT_SPAWN_OPT_ERROR(err, errMsg, options, cmd, ...) \ 117 std::vector<std::string>{(cmd), ##__VA_ARGS__}, (options)); \ 118 ADD_FAILURE() << "expected an error when running " << (cmd); \ 119 } catch (const SubprocessSpawnError& ex) { \ 120 EXPECT_EQ((err), ex.errnoValue()); \ 121 if (StringPiece(ex.what()).find(errMsg) == StringPiece::npos) { \ 122 ADD_FAILURE() << "failed to find \"" << (errMsg) \ 123 << "\" in exception: \"" << ex.what() << "\""; \ 128 #define EXPECT_SPAWN_ERROR(err, errMsg, cmd, ...) \ 129 EXPECT_SPAWN_OPT_ERROR(err, errMsg, Subprocess::Options(), cmd, ##__VA_ARGS__) 131 TEST(SimpleSubprocessTest, ExecFails) {
133 ENOENT,
"failed to execute /no/such/file:",
"/no/such/file");
137 "failed to execute /etc/passwd/not/a/file:",
138 "/etc/passwd/not/a/file");
141 TEST(SimpleSubprocessTest, ShellExitsSuccesssfully) {
146 TEST(SimpleSubprocessTest, ShellExitsWithError) {
151 TEST(SimpleSubprocessTest, ChangeChildDirectorySuccessfully) {
160 TEST(SimpleSubprocessTest, ChangeChildDirectoryWithError) {
163 std::vector<std::string>{
"/bin/true"},
165 ADD_FAILURE() <<
"expected to fail when changing the child's directory";
169 "error preparing to execute /bin/true: No such file or directory";
170 if (
StringPiece(ex.what()).find(expectedError) == StringPiece::npos) {
171 ADD_FAILURE() <<
"failed to find \"" << expectedError
172 <<
"\" in exception: \"" << ex.what() <<
"\"";
178 boost::container::flat_set<int> getOpenFds() {
180 auto dirname = to<std::string>(
"/proc/", pid,
"/fd");
182 boost::container::flat_set<int> fds;
183 for (fs::directory_iterator it(dirname); it != fs::directory_iterator();
185 int fd = to<int>(it->path().filename().native());
191 template <
class Runnable>
192 void checkFdLeak(
const Runnable& r) {
198 auto fdsBefore = getOpenFds();
200 auto fdsAfter = getOpenFds();
201 EXPECT_EQ(fdsAfter.size(), fdsBefore.size());
206 TEST(SimpleSubprocessTest, FdLeakTest) {
215 "echo foo; echo bar >&2",
230 std::vector<std::string>({
"/no/such/file"}),
232 ADD_FAILURE() <<
"expected an error when running /no/such/file";
239 TEST(SimpleSubprocessTest, Detach) {
243 std::vector<std::string>{
"/bin/sleep",
"10"},
255 TEST(SimpleSubprocessTest, DetachExecFails) {
260 "failed to execute /no/such/file:",
265 TEST(ParentDeathSubprocessTest, ParentDeathSignal) {
267 const auto basename =
"subprocess_test_parent_death_helper";
269 helper.remove_filename() /= basename;
270 if (!fs::exists(helper)) {
271 helper = helper.parent_path().parent_path() / basename / basename;
274 fs::path tempFile(fs::temp_directory_path() / fs::unique_path());
276 std::vector<std::string> args{helper.string(), tempFile.string()};
284 while (!fs::exists(tempFile)) {
288 fs::remove(tempFile);
291 TEST(PopenSubprocessTest, PopenRead) {
295 if (line ==
"etc" || line ==
"bin" || line ==
"usr") {
312 : filename_(
std::
move(filename)) {}
320 TEST(AfterForkCallbackSubprocessTest, TestAfterForkCallbackSuccess) {
325 std::vector<std::string>{
"/bin/echo"},
334 TEST(AfterForkCallbackSubprocessTest, TestAfterForkCallbackError) {
340 std::vector<std::string>{
"/bin/echo"},
346 TEST(CommunicateSubprocessTest, SimpleRead) {
348 std::vector<std::string>{
"/bin/echo",
"-n",
"foo",
"bar"},
355 TEST(CommunicateSubprocessTest, BigWrite) {
356 const int numLines = 1 << 20;
359 data.reserve(numLines * line.size());
360 for (
int i = 0;
i < numLines; ++
i) {
370 TEST(CommunicateSubprocessTest, Duplex) {
373 const int bytes = 10 << 20;
379 EXPECT_EQ(std::string::npos, p.first.find_first_not_of(
'X'));
383 TEST(CommunicateSubprocessTest, ProcessGroupLeader) {
384 const auto testIsLeader =
"test $(cut -d ' ' -f 5 /proc/$$/stat) = $$";
388 leader.waitChecked();
391 TEST(CommunicateSubprocessTest, Duplex2) {
394 const size_t numCopies = 100000;
397 for (
size_t n = 0; n < numCopies; ++n) {
398 input.
append(iobuf->clone());
401 std::vector<std::string>
cmd({
405 "s/a test/a successful test/",
407 "/^another line/w/dev/stderr",
417 if (out.first.front()) {
418 stdoutStr = out.first.move()->moveToFbString();
421 if (out.second.front()) {
422 stderrStr = out.second.move()->moveToFbString();
427 std::vector<StringPiece> stdoutLines;
428 split(
'\n', stdoutStr, stdoutLines);
429 EXPECT_EQ(numCopies * 2 + 1, stdoutLines.size());
431 if (!stdoutLines.empty()) {
433 stdoutLines.pop_back();
436 for (
const auto& line : stdoutLines) {
437 if ((linenum & 1) == 0) {
438 EXPECT_EQ(
"this is a successful test", line);
446 std::vector<StringPiece> stderrLines;
447 split(
'\n', stderrStr, stderrLines);
448 EXPECT_EQ(numCopies + 1, stderrLines.size());
450 if (!stderrLines.empty()) {
452 stderrLines.pop_back();
454 for (
const auto& line : stderrLines) {
462 bool readToString(
int fd,
std::string& buf,
size_t maxSize) {
464 char*
dest = &buf.front();
465 size_t remaining = maxSize;
469 n =
::read(fd, dest, remaining);
471 if (errno == EINTR) {
474 if (errno == EAGAIN) {
477 PCHECK(
"read failed");
485 buf.resize(dest - buf.data());
491 TEST(CommunicateSubprocessTest, Chatty) {
493 const int lineCount = 1000;
500 std::vector<std::string>
cmd{
504 "s/a test/a successful test/",
509 auto writeCallback = [&](
int pfd,
int cfd) ->
bool {
513 auto msg = folly::to<std::string>(
"a test ", wcount,
"\n");
522 return (wcount == lineCount);
525 bool eofSeen =
false;
527 auto readCallback = [&](
int pfd,
int cfd) ->
bool {
541 if (rcount < lineCount) {
542 expected = folly::to<std::string>(
"a successful test ", rcount++,
"\n");
549 bool atEof = readToString(pfd, lineBuf, expected.size() + 1);
558 if (wcount != lineCount) {
575 TEST(CommunicateSubprocessTest, TakeOwnershipOfPipes) {
576 std::vector<Subprocess::ChildPipe> pipes;
#define EXPECT_LE(val1, val2)
#define FOLLY_GNU_DISABLE_WARNING(warningName)
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
bool readFile(int fd, Container &out, size_t num_bytes=std::numeric_limits< size_t >::max())
Options & chdir(const std::string &dir)
#define EXPECT_THROW(statement, expected_exception)
#define ASSERT_EQ(val1, val2)
~WriteFileAfterFork() override
TEST(SimpleSubprocessTest, ExitsSuccessfully)
#define EXPECT_EQ(val1, val2)
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
ssize_t readFull(int fd, void *buf, size_t count)
std::pair< std::string, std::string > communicate(StringPiece input=StringPiece())
—— Concurrent Priority Queue Implementation ——
void enableNotifications(int childFd, bool enabled)
std::pair< IOBufQueue, IOBufQueue > communicateIOBuf(IOBufQueue input=IOBufQueue())
size_t read(T &out, folly::io::Cursor &cursor)
auto end(TestAdlIterable &instance)
S split(const StringPiece source, char delimiter)
ssize_t writeFull(int fd, const void *buf, size_t count)
void checkUnixError(ssize_t ret, Args &&...args)
std::unique_ptr< IOBuf > copyBuffer(const folly::IOBuf &buf)
#define EXPECT_TRUE(condition)
#define EXPECT_SPAWN_ERROR(err, errMsg, cmd,...)
auto byLine(File file, char delim= '\n')
std::vector< ChildPipe > takeOwnershipOfPipes()
ProcessReturnCode returnCode() const
Formatter< false, Args... > format(StringPiece fmt, Args &&...args)
bool writeFile(const Container &data, const char *filename, int flags=O_WRONLY|O_CREAT|O_TRUNC, mode_t mode=0666)
const std::string filename_
Options & dangerousPostForkPreExecCallback(DangerousPostForkPreExecCallback *cob)
#define EXPECT_FALSE(condition)
Range< const char * > StringPiece
int operator()() override
#define EXPECT_SPAWN_OPT_ERROR(err, errMsg, options, cmd,...)
WriteFileAfterFork(std::string filename)
static constexpr uint64_t data[1]
void pipe(CPUExecutor cpu, IOExecutor io)