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 "test.h"
18 
19 #ifndef _WIN32
20 // SetWithLots need setrlimit.
21 #include <sys/time.h>
22 #include <sys/resource.h>
23 #include <unistd.h>
24 #endif
25 
26 namespace {
27 
28 #ifdef _WIN32
29 const char* kSimpleCommand = "cmd /c dir \\";
30 #else
31 const char* kSimpleCommand = "ls /";
32 #endif
33 
34 struct SubprocessTest : public testing::Test {
35  SubprocessSet subprocs_;
36 };
37 
38 } // anonymous namespace
39 
40 // Run a command that fails and emits to stderr.
41 TEST_F(SubprocessTest, BadCommandStderr) {
42  Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
43  ASSERT_NE((Subprocess *) 0, subproc);
44 
45  while (!subproc->Done()) {
46  // Pretend we discovered that stderr was ready for writing.
47  subprocs_.DoWork();
48  }
49 
50  EXPECT_EQ(ExitFailure, subproc->Finish());
51  EXPECT_NE("", subproc->GetOutput());
52 }
53 
54 // Run a command that does not exist
55 TEST_F(SubprocessTest, NoSuchCommand) {
56  Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
57  ASSERT_NE((Subprocess *) 0, subproc);
58 
59  while (!subproc->Done()) {
60  // Pretend we discovered that stderr was ready for writing.
61  subprocs_.DoWork();
62  }
63 
64  EXPECT_EQ(ExitFailure, subproc->Finish());
65  EXPECT_NE("", subproc->GetOutput());
66 #ifdef _WIN32
67  ASSERT_EQ("CreateProcess failed: The system cannot find the file "
68  "specified.\n", subproc->GetOutput());
69 #endif
70 }
71 
72 #ifndef _WIN32
73 
74 TEST_F(SubprocessTest, InterruptChild) {
75  Subprocess* subproc = subprocs_.Add("kill -INT $$");
76  ASSERT_NE((Subprocess *) 0, subproc);
77 
78  while (!subproc->Done()) {
79  subprocs_.DoWork();
80  }
81 
82  EXPECT_EQ(ExitInterrupted, subproc->Finish());
83 }
84 
85 TEST_F(SubprocessTest, InterruptParent) {
86  Subprocess* subproc = subprocs_.Add("kill -INT $PPID ; sleep 1");
87  ASSERT_NE((Subprocess *) 0, subproc);
88 
89  while (!subproc->Done()) {
90  bool interrupted = subprocs_.DoWork();
91  if (interrupted)
92  return;
93  }
94 
95  ADD_FAILURE() << "We should have been interrupted";
96 }
97 
98 #endif
99 
100 TEST_F(SubprocessTest, SetWithSingle) {
101  Subprocess* subproc = subprocs_.Add(kSimpleCommand);
102  ASSERT_NE((Subprocess *) 0, subproc);
103 
104  while (!subproc->Done()) {
105  subprocs_.DoWork();
106  }
107  ASSERT_EQ(ExitSuccess, subproc->Finish());
108  ASSERT_NE("", subproc->GetOutput());
109 
110  ASSERT_EQ(1u, subprocs_.finished_.size());
111 }
112 
113 TEST_F(SubprocessTest, SetWithMulti) {
114  Subprocess* processes[3];
115  const char* kCommands[3] = {
116  kSimpleCommand,
117 #ifdef _WIN32
118  "cmd /c echo hi",
119  "cmd /c time /t",
120 #else
121  "whoami",
122  "pwd",
123 #endif
124  };
125 
126  for (int i = 0; i < 3; ++i) {
127  processes[i] = subprocs_.Add(kCommands[i]);
128  ASSERT_NE((Subprocess *) 0, processes[i]);
129  }
130 
131  ASSERT_EQ(3u, subprocs_.running_.size());
132  for (int i = 0; i < 3; ++i) {
133  ASSERT_FALSE(processes[i]->Done());
134  ASSERT_EQ("", processes[i]->GetOutput());
135  }
136 
137  while (!processes[0]->Done() || !processes[1]->Done() ||
138  !processes[2]->Done()) {
139  ASSERT_GT(subprocs_.running_.size(), 0u);
140  subprocs_.DoWork();
141  }
142 
143  ASSERT_EQ(0u, subprocs_.running_.size());
144  ASSERT_EQ(3u, subprocs_.finished_.size());
145 
146  for (int i = 0; i < 3; ++i) {
147  ASSERT_EQ(ExitSuccess, processes[i]->Finish());
148  ASSERT_NE("", processes[i]->GetOutput());
149  delete processes[i];
150  }
151 }
152 
153 // OS X's process limit is less than 1025 by default
154 // (|sysctl kern.maxprocperuid| is 709 on 10.7 and 10.8 and less prior to that).
155 #if defined(linux) || defined(__OpenBSD__)
156 TEST_F(SubprocessTest, SetWithLots) {
157  // Arbitrary big number; needs to be over 1024 to confirm we're no longer
158  // hostage to pselect.
159  const size_t kNumProcs = 1025;
160 
161  // Make sure [ulimit -n] isn't going to stop us from working.
162  rlimit rlim;
163  ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));
164  ASSERT_GT(rlim.rlim_cur, kNumProcs)
165  << "Raise [ulimit -n] well above " << kNumProcs
166  << " to make this test go";
167 
168  vector<Subprocess*> procs;
169  for (size_t i = 0; i < kNumProcs; ++i) {
170  Subprocess* subproc = subprocs_.Add("/bin/echo");
171  ASSERT_NE((Subprocess *) 0, subproc);
172  procs.push_back(subproc);
173  }
174  while (!subprocs_.running_.empty())
175  subprocs_.DoWork();
176  for (size_t i = 0; i < procs.size(); ++i) {
177  ASSERT_EQ(ExitSuccess, procs[i]->Finish());
178  ASSERT_NE("", procs[i]->GetOutput());
179  }
180  ASSERT_EQ(kNumProcs, subprocs_.finished_.size());
181 }
182 #endif // linux || __OpenBSD__
183 
184 // TODO: this test could work on Windows, just not sure how to simply
185 // read stdin.
186 #ifndef _WIN32
187 // Verify that a command that attempts to read stdin correctly thinks
188 // that stdin is closed.
189 TEST_F(SubprocessTest, ReadStdin) {
190  Subprocess* subproc = subprocs_.Add("cat -");
191  while (!subproc->Done()) {
192  subprocs_.DoWork();
193  }
194  ASSERT_EQ(ExitSuccess, subproc->Finish());
195  ASSERT_EQ(1u, subprocs_.finished_.size());
196 }
197 #endif // _WIN32
SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
Definition: subprocess.h:74
bool Done() const
ExitStatus Finish()
Returns ExitSuccess on successful process exit, ExitInterrupted if the process was interrupted...
const string & GetOutput() const
Subprocess wraps a single async subprocess.
Definition: subprocess.h:35
TEST_F(SubprocessTest, BadCommandStderr)