17c145d177e93d913d311edae0a3996651cd6a7b
[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, ExecFails) {
53   Subprocess proc(std::vector<std::string>{ "/no/such/file" });
54   EXPECT_EQ(127, proc.wait().exitStatus());
55 }
56
57 TEST(SimpleSubprocessTest, ShellExitsSuccesssfully) {
58   Subprocess proc("true");
59   EXPECT_EQ(0, proc.wait().exitStatus());
60 }
61
62 TEST(SimpleSubprocessTest, ShellExitsWithError) {
63   Subprocess proc("false");
64   EXPECT_EQ(1, proc.wait().exitStatus());
65 }
66
67 TEST(ParentDeathSubprocessTest, ParentDeathSignal) {
68   // Find out where we are.
69   static constexpr size_t pathLength = 2048;
70   char buf[pathLength + 1];
71   int r = readlink("/proc/self/exe", buf, pathLength);
72   CHECK_ERR(r);
73   buf[r] = '\0';
74
75   fs::path helper(buf);
76   helper.remove_filename();
77   helper /= "subprocess_test_parent_death_helper";
78
79   fs::path tempFile(fs::temp_directory_path() / fs::unique_path());
80
81   std::vector<std::string> args {helper.string(), tempFile.string()};
82   Subprocess proc(args);
83   // The helper gets killed by its child, see details in
84   // SubprocessTestParentDeathHelper.cpp
85   ASSERT_EQ(SIGKILL, proc.wait().killSignal());
86
87   // Now wait for the file to be created, see details in
88   // SubprocessTestParentDeathHelper.cpp
89   while (!fs::exists(tempFile)) {
90     usleep(20000);  // 20ms
91   }
92
93   fs::remove(tempFile);
94 }
95
96 TEST(PopenSubprocessTest, PopenRead) {
97   Subprocess proc("ls /", Subprocess::pipeStdout());
98   int found = 0;
99   gen::byLine(proc.stdout()) |
100     [&] (StringPiece line) {
101       if (line == "etc" || line == "bin" || line == "usr") {
102         ++found;
103       }
104     };
105   EXPECT_EQ(3, found);
106   proc.waitChecked();
107 }
108
109 TEST(CommunicateSubprocessTest, SimpleRead) {
110   Subprocess proc(std::vector<std::string>{ "/bin/echo", "-n", "foo", "bar"},
111                   Subprocess::pipeStdout());
112   auto p = proc.communicate();
113   EXPECT_EQ("foo bar", p.first);
114   proc.waitChecked();
115 }
116
117 TEST(CommunicateSubprocessTest, BigWrite) {
118   const int numLines = 1 << 20;
119   std::string line("hello\n");
120   std::string data;
121   data.reserve(numLines * line.size());
122   for (int i = 0; i < numLines; ++i) {
123     data.append(line);
124   }
125
126   Subprocess proc("wc -l", Subprocess::pipeStdin() | Subprocess::pipeStdout());
127   auto p = proc.communicate(Subprocess::writeStdin() | Subprocess::readStdout(),
128                             data);
129   EXPECT_EQ(folly::format("{}\n", numLines).str(), p.first);
130   proc.waitChecked();
131 }
132
133 TEST(CommunicateSubprocessTest, Duplex) {
134   // Take 10MB of data and pass them through a filter.
135   // One line, as tr is line-buffered
136   const int bytes = 10 << 20;
137   std::string line(bytes, 'x');
138
139   Subprocess proc("tr a-z A-Z",
140                   Subprocess::pipeStdin() | Subprocess::pipeStdout());
141   auto p = proc.communicate(Subprocess::writeStdin() | Subprocess::readStdout(),
142                             line);
143   EXPECT_EQ(bytes, p.first.size());
144   EXPECT_EQ(std::string::npos, p.first.find_first_not_of('X'));
145   proc.waitChecked();
146 }
147