1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "subprocess.h"
16
17 #include "test.h"
18
19 #ifndef _WIN32
20 // SetWithLots need setrlimit.
21 #include <stdio.h>
22 #include <sys/time.h>
23 #include <sys/resource.h>
24 #include <unistd.h>
25 #endif
26
27 namespace {
28
29 #ifdef _WIN32
30 const char* kSimpleCommand = "cmd /c dir \\";
31 #else
32 const char* kSimpleCommand = "ls /";
33 #endif
34
35 struct SubprocessTest : public testing::Test {
36 SubprocessSet subprocs_;
37 };
38
39 } // anonymous namespace
40
41 // Run a command that fails and emits to stderr.
TEST_F(SubprocessTest,BadCommandStderr)42 TEST_F(SubprocessTest, BadCommandStderr) {
43 Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
44 ASSERT_NE((Subprocess *) 0, subproc);
45
46 while (!subproc->Done()) {
47 // Pretend we discovered that stderr was ready for writing.
48 subprocs_.DoWork();
49 }
50
51 EXPECT_EQ(ExitFailure, subproc->Finish());
52 EXPECT_NE("", subproc->GetOutput());
53 }
54
55 // Run a command that does not exist
TEST_F(SubprocessTest,NoSuchCommand)56 TEST_F(SubprocessTest, NoSuchCommand) {
57 Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
58 ASSERT_NE((Subprocess *) 0, subproc);
59
60 while (!subproc->Done()) {
61 // Pretend we discovered that stderr was ready for writing.
62 subprocs_.DoWork();
63 }
64
65 EXPECT_EQ(ExitFailure, subproc->Finish());
66 EXPECT_NE("", subproc->GetOutput());
67 #ifdef _WIN32
68 ASSERT_EQ("CreateProcess failed: The system cannot find the file "
69 "specified.\n", subproc->GetOutput());
70 #endif
71 }
72
73 #ifndef _WIN32
74
TEST_F(SubprocessTest,InterruptChild)75 TEST_F(SubprocessTest, InterruptChild) {
76 Subprocess* subproc = subprocs_.Add("kill -INT $$");
77 ASSERT_NE((Subprocess *) 0, subproc);
78
79 while (!subproc->Done()) {
80 subprocs_.DoWork();
81 }
82
83 EXPECT_EQ(ExitInterrupted, subproc->Finish());
84 }
85
TEST_F(SubprocessTest,InterruptParent)86 TEST_F(SubprocessTest, InterruptParent) {
87 Subprocess* subproc = subprocs_.Add("kill -INT $PPID ; sleep 1");
88 ASSERT_NE((Subprocess *) 0, subproc);
89
90 while (!subproc->Done()) {
91 bool interrupted = subprocs_.DoWork();
92 if (interrupted)
93 return;
94 }
95
96 ASSERT_FALSE("We should have been interrupted");
97 }
98
TEST_F(SubprocessTest,InterruptChildWithSigTerm)99 TEST_F(SubprocessTest, InterruptChildWithSigTerm) {
100 Subprocess* subproc = subprocs_.Add("kill -TERM $$");
101 ASSERT_NE((Subprocess *) 0, subproc);
102
103 while (!subproc->Done()) {
104 subprocs_.DoWork();
105 }
106
107 EXPECT_EQ(ExitInterrupted, subproc->Finish());
108 }
109
TEST_F(SubprocessTest,InterruptParentWithSigTerm)110 TEST_F(SubprocessTest, InterruptParentWithSigTerm) {
111 Subprocess* subproc = subprocs_.Add("kill -TERM $PPID ; sleep 1");
112 ASSERT_NE((Subprocess *) 0, subproc);
113
114 while (!subproc->Done()) {
115 bool interrupted = subprocs_.DoWork();
116 if (interrupted)
117 return;
118 }
119
120 ASSERT_FALSE("We should have been interrupted");
121 }
122
TEST_F(SubprocessTest,InterruptChildWithSigHup)123 TEST_F(SubprocessTest, InterruptChildWithSigHup) {
124 Subprocess* subproc = subprocs_.Add("kill -HUP $$");
125 ASSERT_NE((Subprocess *) 0, subproc);
126
127 while (!subproc->Done()) {
128 subprocs_.DoWork();
129 }
130
131 EXPECT_EQ(ExitInterrupted, subproc->Finish());
132 }
133
TEST_F(SubprocessTest,InterruptParentWithSigHup)134 TEST_F(SubprocessTest, InterruptParentWithSigHup) {
135 Subprocess* subproc = subprocs_.Add("kill -HUP $PPID ; sleep 1");
136 ASSERT_NE((Subprocess *) 0, subproc);
137
138 while (!subproc->Done()) {
139 bool interrupted = subprocs_.DoWork();
140 if (interrupted)
141 return;
142 }
143
144 ASSERT_FALSE("We should have been interrupted");
145 }
146
TEST_F(SubprocessTest,Console)147 TEST_F(SubprocessTest, Console) {
148 // Skip test if we don't have the console ourselves.
149 if (isatty(0) && isatty(1) && isatty(2)) {
150 Subprocess* subproc =
151 subprocs_.Add("test -t 0 -a -t 1 -a -t 2", /*use_console=*/true);
152 ASSERT_NE((Subprocess*)0, subproc);
153
154 while (!subproc->Done()) {
155 subprocs_.DoWork();
156 }
157
158 EXPECT_EQ(ExitSuccess, subproc->Finish());
159 }
160 }
161
162 #endif
163
TEST_F(SubprocessTest,SetWithSingle)164 TEST_F(SubprocessTest, SetWithSingle) {
165 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
166 ASSERT_NE((Subprocess *) 0, subproc);
167
168 while (!subproc->Done()) {
169 subprocs_.DoWork();
170 }
171 ASSERT_EQ(ExitSuccess, subproc->Finish());
172 ASSERT_NE("", subproc->GetOutput());
173
174 ASSERT_EQ(1u, subprocs_.finished_.size());
175 }
176
TEST_F(SubprocessTest,SetWithMulti)177 TEST_F(SubprocessTest, SetWithMulti) {
178 Subprocess* processes[3];
179 const char* kCommands[3] = {
180 kSimpleCommand,
181 #ifdef _WIN32
182 "cmd /c echo hi",
183 "cmd /c time /t",
184 #else
185 "id -u",
186 "pwd",
187 #endif
188 };
189
190 for (int i = 0; i < 3; ++i) {
191 processes[i] = subprocs_.Add(kCommands[i]);
192 ASSERT_NE((Subprocess *) 0, processes[i]);
193 }
194
195 ASSERT_EQ(3u, subprocs_.running_.size());
196 for (int i = 0; i < 3; ++i) {
197 ASSERT_FALSE(processes[i]->Done());
198 ASSERT_EQ("", processes[i]->GetOutput());
199 }
200
201 while (!processes[0]->Done() || !processes[1]->Done() ||
202 !processes[2]->Done()) {
203 ASSERT_GT(subprocs_.running_.size(), 0u);
204 subprocs_.DoWork();
205 }
206
207 ASSERT_EQ(0u, subprocs_.running_.size());
208 ASSERT_EQ(3u, subprocs_.finished_.size());
209
210 for (int i = 0; i < 3; ++i) {
211 ASSERT_EQ(ExitSuccess, processes[i]->Finish());
212 ASSERT_NE("", processes[i]->GetOutput());
213 delete processes[i];
214 }
215 }
216
217 #if defined(USE_PPOLL)
TEST_F(SubprocessTest,SetWithLots)218 TEST_F(SubprocessTest, SetWithLots) {
219 // Arbitrary big number; needs to be over 1024 to confirm we're no longer
220 // hostage to pselect.
221 const unsigned kNumProcs = 1025;
222
223 // Make sure [ulimit -n] isn't going to stop us from working.
224 rlimit rlim;
225 ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));
226 if (rlim.rlim_cur < kNumProcs) {
227 printf("Raise [ulimit -n] above %u (currently %lu) to make this test go\n",
228 kNumProcs, rlim.rlim_cur);
229 return;
230 }
231
232 vector<Subprocess*> procs;
233 for (size_t i = 0; i < kNumProcs; ++i) {
234 Subprocess* subproc = subprocs_.Add("/bin/echo");
235 ASSERT_NE((Subprocess *) 0, subproc);
236 procs.push_back(subproc);
237 }
238 while (!subprocs_.running_.empty())
239 subprocs_.DoWork();
240 for (size_t i = 0; i < procs.size(); ++i) {
241 ASSERT_EQ(ExitSuccess, procs[i]->Finish());
242 ASSERT_NE("", procs[i]->GetOutput());
243 }
244 ASSERT_EQ(kNumProcs, subprocs_.finished_.size());
245 }
246 #endif // !__APPLE__ && !_WIN32
247
248 // TODO: this test could work on Windows, just not sure how to simply
249 // read stdin.
250 #ifndef _WIN32
251 // Verify that a command that attempts to read stdin correctly thinks
252 // that stdin is closed.
TEST_F(SubprocessTest,ReadStdin)253 TEST_F(SubprocessTest, ReadStdin) {
254 Subprocess* subproc = subprocs_.Add("cat -");
255 while (!subproc->Done()) {
256 subprocs_.DoWork();
257 }
258 ASSERT_EQ(ExitSuccess, subproc->Finish());
259 ASSERT_EQ(1u, subprocs_.finished_.size());
260 }
261 #endif // _WIN32
262