github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/subprocess_test.go (about) 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 package nin 16 17 import ( 18 "os" 19 "os/signal" 20 "runtime" 21 "testing" 22 ) 23 24 func testCommand() string { 25 if runtime.GOOS == "windows" { 26 return "cmd /c dir \\" 27 } 28 return "ls /" 29 } 30 31 func newSubprocessSetTest(t *testing.T) *subprocessSet { 32 s := newSubprocessSet() 33 t.Cleanup(s.Clear) 34 return s 35 } 36 37 // Run a command that fails and emits to stderr. 38 func TestSubprocessTest_BadCommandStderr(t *testing.T) { 39 subprocs := newSubprocessSetTest(t) 40 cmd := "bash -c foo" 41 if runtime.GOOS == "windows" { 42 cmd = "cmd /c ninja_no_such_command" 43 } 44 subproc := subprocs.Add(cmd, false) 45 if nil == subproc { 46 t.Fatal("expected different") 47 } 48 49 for !subproc.Done() { 50 // Pretend we discovered that stderr was ready for writing. 51 subprocs.DoWork() 52 } 53 54 // ExitFailure 55 want := 127 56 if runtime.GOOS == "windows" { 57 want = 1 58 } 59 if got := subproc.Finish(); got != want { 60 t.Fatal(got) 61 } 62 if got := subproc.GetOutput(); got == "" { 63 t.Fatal("expected error output") 64 } 65 } 66 67 // Run a command that does not exist 68 func TestSubprocessTest_NoSuchCommand(t *testing.T) { 69 subprocs := newSubprocessSetTest(t) 70 subproc := subprocs.Add("ninja_no_such_command", false) 71 if nil == subproc { 72 t.Fatal("expected different") 73 } 74 75 for !subproc.Done() { 76 // Pretend we discovered that stderr was ready for writing. 77 subprocs.DoWork() 78 } 79 80 // ExitFailure 81 // 127 on posix, -1 on Windows. 82 want := 127 // Generated by /bin/sh. 83 if runtime.GOOS == "windows" { 84 want = -1 85 } 86 if got := subproc.Finish(); got != want { 87 t.Fatal(got) 88 } 89 /* 90 if got := subproc.GetOutput(); got != "" { 91 t.Fatalf("%q", got) 92 } 93 if runtime.GOOS == "windows" { 94 if "CreateProcess failed: The system cannot find the file specified.\n" != subproc.GetOutput() { 95 t.Fatal() 96 } 97 } 98 */ 99 } 100 101 func TestSubprocessTest_InterruptChild(t *testing.T) { 102 if runtime.GOOS == "windows" { 103 t.Skip("can't run on Windows") 104 } 105 subprocs := newSubprocessSetTest(t) 106 subproc := subprocs.Add("kill -INT $$", false) 107 if nil == subproc { 108 t.Fatal("expected different") 109 } 110 111 for !subproc.Done() { 112 subprocs.DoWork() 113 } 114 115 // ExitInterrupted 116 if got := subproc.Finish(); got != -1 { 117 t.Fatal(got) 118 } 119 } 120 121 func TestSubprocessTest_InterruptParent(t *testing.T) { 122 if runtime.GOOS == "windows" { 123 t.Skip("can't run on Windows") 124 } 125 t.Skip("TODO") 126 subprocs := newSubprocessSetTest(t) 127 c := make(chan os.Signal, 1) 128 go func() { 129 <-c 130 subprocs.Clear() 131 }() 132 signal.Notify(c, os.Interrupt) 133 defer signal.Reset(os.Interrupt) 134 subproc := subprocs.Add("kill -INT $PPID ; sleep 1", false) 135 if nil == subproc { 136 t.Fatal("expected different") 137 } 138 139 for !subproc.Done() { 140 if subprocs.DoWork() { 141 return 142 } 143 } 144 145 t.Fatal("We should have been interrupted") 146 } 147 148 func TestSubprocessTest_InterruptChildWithSigTerm(t *testing.T) { 149 if runtime.GOOS == "windows" { 150 t.Skip("can't run on Windows") 151 } 152 subprocs := newSubprocessSetTest(t) 153 subproc := subprocs.Add("kill -TERM $$", false) 154 if nil == subproc { 155 t.Fatal("expected different") 156 } 157 158 for !subproc.Done() { 159 subprocs.DoWork() 160 } 161 162 // TODO(maruel): ExitInterrupted 163 if got := subproc.Finish(); got != -1 { 164 t.Fatal(got) 165 } 166 } 167 168 func TestSubprocessTest_InterruptParentWithSigTerm(t *testing.T) { 169 if runtime.GOOS == "windows" { 170 t.Skip("can't run on Windows") 171 } 172 t.Skip("TODO") 173 subprocs := newSubprocessSetTest(t) 174 subproc := subprocs.Add("kill -TERM $PPID ; sleep 1", false) 175 if nil == subproc { 176 t.Fatal("expected different") 177 } 178 179 for !subproc.Done() { 180 if subprocs.DoWork() { 181 return 182 } 183 } 184 185 t.Fatal("We should have been interrupted") 186 } 187 188 func TestSubprocessTest_InterruptChildWithSigHup(t *testing.T) { 189 if runtime.GOOS == "windows" { 190 t.Skip("can't run on Windows") 191 } 192 subprocs := newSubprocessSetTest(t) 193 subproc := subprocs.Add("kill -HUP $$", false) 194 if nil == subproc { 195 t.Fatal("expected different") 196 } 197 198 for !subproc.Done() { 199 subprocs.DoWork() 200 } 201 202 // TODO(maruel): ExitInterrupted 203 if got := subproc.Finish(); got != -1 { 204 t.Fatal(got) 205 } 206 } 207 208 func TestSubprocessTest_InterruptParentWithSigHup(t *testing.T) { 209 t.Skip("TODO") 210 if runtime.GOOS == "windows" { 211 t.Skip("can't run on Windows") 212 } 213 subprocs := newSubprocessSetTest(t) 214 subproc := subprocs.Add("kill -HUP $PPID ; sleep 1", false) 215 if nil == subproc { 216 t.Fatal("expected different") 217 } 218 219 for !subproc.Done() { 220 if subprocs.DoWork() { 221 return 222 } 223 } 224 225 t.Fatal("We should have been interrupted") 226 } 227 228 func TestSubprocessTest_Console(t *testing.T) { 229 if runtime.GOOS == "windows" { 230 t.Skip("can't run on Windows") 231 } 232 t.Skip("TODO") 233 /* 234 // Skip test if we don't have the console ourselves. 235 // TODO(maruel): Sub-run with a fake pty? 236 if !isatty(0) || !isatty(1) || !isatty(2) { 237 t.Skip("need a real console to run this test") 238 } 239 */ 240 subprocs := newSubprocessSetTest(t) 241 // useConsole = true 242 subproc := subprocs.Add("test -t 0 -a -t 1 -a -t 2", true) 243 if nil == subproc { 244 t.Fatal("expected different") 245 } 246 247 for !subproc.Done() { 248 subprocs.DoWork() 249 } 250 251 if got := subproc.Finish(); got != ExitSuccess { 252 t.Fatal(got) 253 } 254 } 255 256 func TestSubprocessTest_SetWithSingle(t *testing.T) { 257 subprocs := newSubprocessSetTest(t) 258 subproc := subprocs.Add(testCommand(), false) 259 if subproc == nil { 260 t.Fatal("expected different") 261 } 262 263 for !subproc.Done() { 264 subprocs.DoWork() 265 } 266 if subproc.Finish() != ExitSuccess { 267 t.Fatal("expected equal") 268 } 269 if subproc.GetOutput() == "" { 270 t.Fatal("expected different") 271 } 272 273 if got := subprocs.Finished(); got != 1 { 274 t.Fatal(got) 275 } 276 } 277 278 func TestSubprocessTest_SetWithMulti(t *testing.T) { 279 processes := [3]*subprocess{} 280 commands := []string{testCommand()} 281 if runtime.GOOS == "windows" { 282 commands = append(commands, "cmd /c echo hi", "cmd /c time /t") 283 } else { 284 commands = append(commands, "id -u", "pwd") 285 } 286 287 subprocs := newSubprocessSetTest(t) 288 for i := 0; i < 3; i++ { 289 processes[i] = subprocs.Add(commands[i], false) 290 if processes[i] == nil { 291 t.Fatal("expected different") 292 } 293 } 294 295 if subprocs.Running() != 3 { 296 t.Fatal("expected equal") 297 } 298 /* The expectations with the C++ code is different. 299 for i := 0; i < 3; i++ { 300 if processes[i].Done() { 301 t.Fatal("expected false") 302 } 303 if got := processes[i].GetOutput(); got != "" { 304 t.Fatalf("%q", got) 305 } 306 } 307 */ 308 309 for !processes[0].Done() || !processes[1].Done() || !processes[2].Done() { 310 if subprocs.Running() <= 0 { 311 t.Fatal("expected greater") 312 } 313 subprocs.DoWork() 314 } 315 316 if subprocs.Running() != 0 { 317 t.Fatal("expected equal") 318 } 319 if subprocs.Finished() != 3 { 320 t.Fatal("expected equal") 321 } 322 323 for i := 0; i < 3; i++ { 324 if processes[i].Finish() != ExitSuccess { 325 t.Fatal("expected equal") 326 } 327 if processes[i].GetOutput() == "" { 328 t.Fatal("expected different") 329 } 330 } 331 } 332 333 func TestSubprocessTest_SetWithLots(t *testing.T) { 334 if runtime.GOOS == "windows" { 335 t.Skip("skipped on windows") 336 } 337 338 // Arbitrary big number; needs to be over 1024 to confirm we're no longer 339 // hostage to pselect. 340 const numProcs = 1025 341 342 subprocessTestFixUlimit(t, numProcs) 343 cmd := "/bin/echo" 344 345 subprocs := newSubprocessSetTest(t) 346 var procs []*subprocess 347 for i := 0; i < numProcs; i++ { 348 subproc := subprocs.Add(cmd, false) 349 if nil == subproc { 350 t.Fatal("expected different") 351 } 352 procs = append(procs, subproc) 353 } 354 for subprocs.Running() != 0 { 355 subprocs.DoWork() 356 } 357 for i := 0; i < len(procs); i++ { 358 if got := procs[i].Finish(); got != ExitSuccess { 359 t.Fatal(got) 360 } 361 if procs[i].GetOutput() == "" { 362 t.Fatal("expected different") 363 } 364 } 365 if numProcs != subprocs.Finished() { 366 t.Fatal("expected equal") 367 } 368 } 369 370 // TODO: this test could work on Windows, just not sure how to simply 371 // read stdin. 372 // Verify that a command that attempts to read stdin correctly thinks 373 // that stdin is closed. 374 func TestSubprocessTest_ReadStdin(t *testing.T) { 375 if runtime.GOOS == "windows" { 376 t.Skip("Has to be ported") 377 } 378 subprocs := newSubprocessSetTest(t) 379 subproc := subprocs.Add("cat -", false) 380 for !subproc.Done() { 381 subprocs.DoWork() 382 } 383 if subproc.Finish() != ExitSuccess { 384 t.Fatal("expected equal") 385 } 386 if subprocs.Finished() != 1 { 387 t.Fatal("expected equal") 388 } 389 }