Optionally, kill subprocess when parent dies
[folly.git] / folly / test / SubprocessTest.cpp
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "folly/Subprocess.h"
18
19 #include <unistd.h>
20
21 #include <glog/logging.h>
22 #include <gtest/gtest.h>
23
24 #include "folly/Format.h"
25 #include "folly/experimental/Gen.h"
26 #include "folly/experimental/FileGen.h"
27 #include "folly/experimental/StringGen.h"
28 #include "folly/experimental/io/FsUtil.h"
29
30 using namespace folly;
31
32 TEST(SimpleSubprocessTest, ExitsSuccessfully) {
33   Subprocess proc(std::vector<std::string>{ "/bin/true" });
34   EXPECT_EQ(0, proc.wait().exitStatus());
35 }
36
37 TEST(SimpleSubprocessTest, ExitsSuccessfullyChecked) {
38   Subprocess proc(std::vector<std::string>{ "/bin/true" });
39   proc.waitChecked();
40 }
41
42 TEST(SimpleSubprocessTest, ExitsWithError) {
43   Subprocess proc(std::vector<std::string>{ "/bin/false" });
44   EXPECT_EQ(1, proc.wait().exitStatus());
45 }
46
47 TEST(SimpleSubprocessTest, ExitsWithErrorChecked) {
48   Subprocess proc(std::vector<std::string>{ "/bin/false" });
49   EXPECT_THROW(proc.waitChecked(), CalledProcessError);
50 }
51
52 TEST(SimpleSubprocessTest, ShellExitsSuccesssfully) {
53   Subprocess proc("true");
54   EXPECT_EQ(0, proc.wait().exitStatus());
55 }
56
57 TEST(SimpleSubprocessTest, ShellExitsWithError) {
58   Subprocess proc("false");
59   EXPECT_EQ(1, proc.wait().exitStatus());
60 }
61
62 TEST(ParentDeathSubprocessTest, ParentDeathSignal) {
63   // Find out where we are.
64   static constexpr size_t pathLength = 2048;
65   char buf[pathLength];
66   int r = readlink("/proc/self/exe", buf, pathLength);
67   CHECK_ERR(r >= 0);
68   buf[r] = '\0';
69
70   fs::path helper(buf);
71   helper.remove_filename();
72   helper /= "subprocess_test_parent_death_helper";
73
74   fs::path tempFile(fs::temp_directory_path() / fs::unique_path());
75
76   std::vector<std::string> args {helper.string(), tempFile.string()};
77   Subprocess proc(args);
78   // The helper gets killed by its child, see details in
79   // SubprocessTestParentDeathHelper.cpp
80   ASSERT_EQ(SIGKILL, proc.wait().killSignal());
81
82   // Now wait for the file to be created, see details in
83   // SubprocessTestParentDeathHelper.cpp
84   while (!fs::exists(tempFile)) {
85     usleep(20000);  // 20ms
86   }
87
88   fs::remove(tempFile);
89 }
90
91 TEST(PopenSubprocessTest, PopenRead) {
92   Subprocess proc("ls /", Subprocess::pipeStdout());
93   int found = 0;
94   gen::byLine(proc.stdout()) |
95     [&] (StringPiece line) {
96       if (line == "etc" || line == "bin" || line == "usr") {
97         ++found;
98       }
99     };
100   EXPECT_EQ(3, found);
101   proc.waitChecked();
102 }
103
104 TEST(CommunicateSubprocessTest, SimpleRead) {
105   Subprocess proc(std::vector<std::string>{ "/bin/echo", "-n", "foo", "bar"},
106                   Subprocess::pipeStdout());
107   auto p = proc.communicate();
108   EXPECT_EQ("foo bar", p.first);
109   proc.waitChecked();
110 }
111
112 TEST(CommunicateSubprocessTest, BigWrite) {
113   const int numLines = 1 << 20;
114   std::string line("hello\n");
115   std::string data;
116   data.reserve(numLines * line.size());
117   for (int i = 0; i < numLines; ++i) {
118     data.append(line);
119   }
120
121   Subprocess proc("wc -l", Subprocess::pipeStdin() | Subprocess::pipeStdout());
122   auto p = proc.communicate(Subprocess::writeStdin() | Subprocess::readStdout(),
123                             data);
124   EXPECT_EQ(folly::format("{}\n", numLines).str(), p.first);
125   proc.waitChecked();
126 }
127
128 TEST(CommunicateSubprocessTest, Duplex) {
129   // Take 10MB of data and pass them through a filter.
130   // One line, as tr is line-buffered
131   const int bytes = 10 << 20;
132   std::string line(bytes, 'x');
133
134   Subprocess proc("tr a-z A-Z",
135                   Subprocess::pipeStdin() | Subprocess::pipeStdout());
136   auto p = proc.communicate(Subprocess::writeStdin() | Subprocess::readStdout(),
137                             line);
138   EXPECT_EQ(bytes, p.first.size());
139   EXPECT_EQ(std::string::npos, p.first.find_first_not_of('X'));
140   proc.waitChecked();
141 }
142