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