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