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 
28 using namespace std;
29 
30 namespace {
31 
32 #ifdef _WIN32
33 const char* kSimpleCommand = "cmd /c dir \\";
34 #else
35 const char* kSimpleCommand = "ls /";
36 #endif
37 
38 struct SubprocessTest : public testing::Test {
39  SubprocessSet subprocs_;
40 };
41 
42 } // anonymous namespace
43 
44 // Run a command that fails and emits to stderr.
45 TEST_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
60 TEST_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 
80 TEST_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 
91 TEST_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 
104 TEST_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 
115 TEST_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 
128 TEST_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 
139 TEST_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 
152 TEST_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 
169 TEST_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 
182 TEST_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)
223 TEST_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.
258 TEST_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
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Definition: subprocess.h:101
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)