blob: b903e41448726281398e3482738e5f596d9e1995 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -07005package exec_test
Jiri Simsa5293dcb2014-05-10 09:56:38 -07006
7import (
8 "fmt"
9 "io"
10 "log"
11 "os"
12 "os/exec"
Bogdan Capritaf2b19282015-08-28 22:40:14 -070013 "runtime"
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -070014 "strings"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070015 "sync"
Ankurff1305c2015-01-20 11:29:50 -080016 "syscall"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070017 "testing"
18 "time"
Bogdan Caprita66ca3532015-02-05 21:08:10 -080019 "unicode/utf8"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070020
Mike Burrowsccca2f42015-03-27 13:57:29 -070021 "v.io/v23/verror"
Jiri Simsaffceefa2015-02-28 11:03:34 -080022 vexec "v.io/x/ref/lib/exec"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070023 // Use mock timekeeper to avoid actually sleeping during the test.
Cosmos Nicolaou1381f8a2015-03-13 09:40:34 -070024 "v.io/x/ref/test/timekeeper"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070025)
26
Jiri Simsac199bc12014-05-30 12:52:24 -070027// We always expect there to be exactly three open file descriptors
28// when the test starts out: STDIN, STDOUT, and STDERR. This
29// assumption is tested in init below, and in the rare cases where it
Ankurff1305c2015-01-20 11:29:50 -080030// is wrong, we try to close all additional file descriptors, and bail
31// out if that fails.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070032const baselineOpenFiles = 3
33
34func init() {
Bogdan Capritaf2b19282015-08-28 22:40:14 -070035 if os.Getenv("GOMAXPROCS") == "" {
36 // Set the number of logical processors to 1 if GOMAXPROCS is
37 // not set in the environment.
38 //
39 // TODO(caprita): the default in Go 1.5 is num cpus, which
40 // causes flakiness. Figure out why.
41 runtime.GOMAXPROCS(1)
42 }
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -070043 if os.Getenv("GO_WANT_HELPER_PROCESS_EXEC") == "1" {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070044 return
45 }
Ankurff1305c2015-01-20 11:29:50 -080046 want, got := baselineOpenFiles, openFiles()
47 if want == got {
48 return
49 }
50 for i := want; i < got; i++ {
51 syscall.Close(i)
52 }
53 if want, got = baselineOpenFiles, openFiles(); want != got {
Jiri Simsac199bc12014-05-30 12:52:24 -070054 message := `Test expected to start with %d open files, found %d instead.
55This can happen if parent process has any open file descriptors,
56e.g. pipes, that are being inherited.`
57 panic(fmt.Errorf(message, want, got))
Jiri Simsa5293dcb2014-05-10 09:56:38 -070058 }
59}
60
Jiri Simsac199bc12014-05-30 12:52:24 -070061// These tests need to run a subprocess and we reuse this same test
62// binary to do so. A fake test 'TestHelperProcess' contains the code
63// we need to run in the child and we simply run this same binary with
64// a test.run= arg that runs just that test. This idea was taken from
65// the tests for os/exec.
Jiri Simsa5293dcb2014-05-10 09:56:38 -070066func helperCommand(s ...string) *exec.Cmd {
67 cs := []string{"-test.run=TestHelperProcess", "--"}
68 cs = append(cs, s...)
69 cmd := exec.Command(os.Args[0], cs...)
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -070070 cmd.Env = append([]string{"GO_WANT_HELPER_PROCESS_EXEC=1"}, os.Environ()...)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070071 return cmd
72}
73
Jiri Simsa5293dcb2014-05-10 09:56:38 -070074func openFiles() int {
75 f, err := os.Open("/dev/null")
76 if err != nil {
77 panic("Failed to open /dev/null\n")
78 }
79 n := f.Fd()
80 f.Close()
81 return int(n)
82}
83
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -070084func clean(t *testing.T, ph ...*vexec.ParentHandle) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070085 for _, p := range ph {
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -070086 alreadyClean := !p.Exists()
Jiri Simsa5293dcb2014-05-10 09:56:38 -070087 p.Clean()
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -070088 if !alreadyClean && p.Exists() {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070089 t.Errorf("child process left behind even after calling Clean")
90 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -070091 }
92 if want, got := baselineOpenFiles, openFiles(); want != got {
93 t.Errorf("Leaking file descriptors: expect %d, got %d", want, got)
94 }
95}
96
97func read(ch chan bool, r io.Reader, m string) {
98 buf := make([]byte, 4096*4)
99 n, err := r.Read(buf)
100 if err != nil {
101 log.Printf("failed to read message: error %s, expecting '%s'\n",
102 err, m)
103 ch <- false
104 return
105 }
106 g := string(buf[:n])
107 b := g == m
108 if !b {
109 log.Printf("read '%s', not '%s'\n", g, m)
110 }
111 ch <- b
112}
113
114func expectMessage(r io.Reader, m string) bool {
115 ch := make(chan bool, 1)
116 go read(ch, r, m)
117 select {
118 case b := <-ch:
119 return b
120 case <-time.After(5 * time.Second):
121 log.Printf("expectMessage: timeout\n")
122 return false
123 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700124}
125
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700126func TestConfigExchange(t *testing.T) {
127 cmd := helperCommand("testConfig")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700128 stderr, _ := cmd.StderrPipe()
Cosmos Nicolaou486d3492014-09-30 22:21:20 -0700129 cfg := vexec.NewConfig()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700130 cfg.Set("foo", "bar")
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700131 ph := vexec.NewParentHandle(cmd, vexec.ConfigOpt{Config: cfg})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700132 err := ph.Start()
133 if err != nil {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700134 t.Fatalf("testConfig: start: %v", err)
Jiri Simsac199bc12014-05-30 12:52:24 -0700135 }
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700136 serialized, err := cfg.Serialize()
137 if err != nil {
138 t.Fatalf("testConfig: failed to serialize config: %v", err)
139 }
140 if !expectMessage(stderr, serialized) {
Jiri Simsac199bc12014-05-30 12:52:24 -0700141 t.Errorf("unexpected output from child")
142 } else {
143 if err = cmd.Wait(); err != nil {
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700144 t.Errorf("testConfig: wait: %v", err)
Jiri Simsac199bc12014-05-30 12:52:24 -0700145 }
146 }
147 clean(t, ph)
148}
149
150func TestSecretExchange(t *testing.T) {
151 cmd := helperCommand("testSecret")
152 stderr, _ := cmd.StderrPipe()
153 ph := vexec.NewParentHandle(cmd, vexec.SecretOpt("dummy_secret"))
154 err := ph.Start()
155 if err != nil {
156 t.Fatalf("testSecretTest: start: %v", err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700157 }
158 if !expectMessage(stderr, "dummy_secret") {
159 t.Errorf("unexpected output from child")
160 } else {
161 if err = cmd.Wait(); err != nil {
Jiri Simsac199bc12014-05-30 12:52:24 -0700162 t.Errorf("testSecretTest: wait: %v", err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700163 }
164 }
165 clean(t, ph)
166}
167
168func TestNoVersion(t *testing.T) {
169 // Make sure that Init correctly tests for the presence of VEXEC_VERSION
Jiri Simsa84059da2014-06-02 17:22:05 -0700170 _, err := vexec.GetChildHandle()
Mike Burrowsccca2f42015-03-27 13:57:29 -0700171 if verror.ErrorID(err) != vexec.ErrNoVersion.ID {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700172 t.Errorf("Should be missing Version")
173 }
174}
175
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700176func waitForReady(t *testing.T, cmd *exec.Cmd, name string, delay int, ph *vexec.ParentHandle) error {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700177 err := ph.Start()
178 if err != nil {
179 t.Fatalf("%s: start: %v", name, err)
180 return err
181 }
182 return ph.WaitForReady(time.Duration(delay) * time.Second)
183}
184
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700185func readyHelperCmd(t *testing.T, cmd *exec.Cmd, name, result string) *vexec.ParentHandle {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700186 stderr, err := cmd.StderrPipe()
187 if err != nil {
188 t.Fatalf("%s: failed to get stderr pipe\n", name)
189 }
Jiri Simsac199bc12014-05-30 12:52:24 -0700190 ph := vexec.NewParentHandle(cmd)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700191 if err := waitForReady(t, cmd, name, 4, ph); err != nil {
192 t.Errorf("%s: WaitForReady: %v (%v)", name, err, ph)
193 return nil
194 }
195 if !expectMessage(stderr, result) {
196 t.Errorf("%s: failed to read '%s' from child\n", name, result)
197 }
198 return ph
199}
200
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700201func readyHelper(t *testing.T, name, test, result string) *vexec.ParentHandle {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700202 cmd := helperCommand(test)
203 return readyHelperCmd(t, cmd, name, result)
204}
205
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700206func testMany(t *testing.T, name, test, result string) []*vexec.ParentHandle {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700207 nprocs := 10
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700208 ph := make([]*vexec.ParentHandle, nprocs)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700209 cmd := make([]*exec.Cmd, nprocs)
210 stderr := make([]io.ReadCloser, nprocs)
211 controlReaders := make([]io.ReadCloser, nprocs)
212 var done sync.WaitGroup
213 for i := 0; i < nprocs; i++ {
214 cmd[i] = helperCommand(test)
215 // The control pipe is used to signal the child when to wake up.
216 controlRead, controlWrite, err := os.Pipe()
217 if err != nil {
218 t.Errorf("Failed to create control pipe: %v", err)
219 return nil
220 }
221 controlReaders[i] = controlRead
222 cmd[i].ExtraFiles = append(cmd[i].ExtraFiles, controlRead)
223 stderr[i], _ = cmd[i].StderrPipe()
224 tk := timekeeper.NewManualTime()
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700225 ph[i] = vexec.NewParentHandle(cmd[i], vexec.TimeKeeperOpt{TimeKeeper: tk})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700226 done.Add(1)
227 go func() {
228 // For simulated slow children, wait until the parent
229 // starts waiting, and then wake up the child.
230 if test == "testReadySlow" {
231 <-tk.Requests()
232 tk.AdvanceTime(3 * time.Second)
233 if _, err = controlWrite.Write([]byte("wake")); err != nil {
234 t.Errorf("Failed to write to control pipe: %v", err)
235 }
236 }
237 controlWrite.Close()
238 done.Done()
239 }()
240 if err := ph[i].Start(); err != nil {
241 t.Errorf("%s: Failed to start child %d: %s\n", name, i, err)
242 }
243 }
244 for i := 0; i < nprocs; i++ {
245 if err := ph[i].WaitForReady(5 * time.Second); err != nil {
246 t.Errorf("%s: Failed to wait for child %d: %s\n", name, i, err)
247 }
248 controlReaders[i].Close()
249 }
250 for i := 0; i < nprocs; i++ {
251 if !expectMessage(stderr[i], result) {
252 t.Errorf("%s: Failed to read message from child %d\n", name, i)
253 }
254 }
255 done.Wait()
256 return ph
257}
258
259func TestToReadyMany(t *testing.T) {
260 clean(t, testMany(t, "TestToReadyMany", "testReady", ".")...)
261}
262
263func TestToReadySlowMany(t *testing.T) {
264 clean(t, testMany(t, "TestToReadySlowMany", "testReadySlow", "..")...)
265}
266
267func TestToReady(t *testing.T) {
268 ph := readyHelper(t, "TestToReady", "testReady", ".")
269 clean(t, ph)
270}
271
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -0700272func TestToFail(t *testing.T) {
273 name := "testFail"
274 cmd := helperCommand(name, "failed", "to", "start")
275 ph := vexec.NewParentHandle(cmd)
276 err := waitForReady(t, cmd, name, 4, ph)
Mike Burrowsccca2f42015-03-27 13:57:29 -0700277 if err == nil || !strings.Contains(err.Error(), "failed to start") {
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -0700278 t.Errorf("unexpected error: %v", err)
279 }
280}
281
Bogdan Caprita66ca3532015-02-05 21:08:10 -0800282func TestToFailInvalidUTF8(t *testing.T) {
283 name := "testFail"
284 cmd := helperCommand(name, "invalid", "utf8", string([]byte{0xFF}), "in", string([]byte{0xFC}), "error", "message")
285 ph := vexec.NewParentHandle(cmd)
286 err := waitForReady(t, cmd, name, 4, ph)
Mike Burrowsccca2f42015-03-27 13:57:29 -0700287 if err == nil || !strings.Contains(err.Error(), "invalid utf8 "+string(utf8.RuneError)+" in "+string(utf8.RuneError)+" error message") {
Bogdan Caprita66ca3532015-02-05 21:08:10 -0800288 t.Errorf("unexpected error: %v", err)
289 }
290}
291
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700292func TestNeverReady(t *testing.T) {
293 name := "testNeverReady"
294 result := "never ready"
295 cmd := helperCommand(name)
296 stderr, _ := cmd.StderrPipe()
Jiri Simsac199bc12014-05-30 12:52:24 -0700297 ph := vexec.NewParentHandle(cmd)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700298 err := waitForReady(t, cmd, name, 1, ph)
Mike Burrowsccca2f42015-03-27 13:57:29 -0700299 if verror.ErrorID(err) != vexec.ErrTimeout.ID {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700300 t.Errorf("Failed to get timeout: got %v\n", err)
301 } else {
302 // block waiting for error from child
303 if !expectMessage(stderr, result) {
304 t.Errorf("%s: failed to read '%s' from child\n", name, result)
305 }
306 }
307 clean(t, ph)
308}
309
310func TestTooSlowToReady(t *testing.T) {
311 name := "testTooSlowToReady"
312 result := "write status_wr: broken pipe"
313 cmd := helperCommand(name)
314 // The control pipe is used to signal the child when to wake up.
315 controlRead, controlWrite, err := os.Pipe()
316 if err != nil {
317 t.Errorf("Failed to create control pipe: %v", err)
318 return
319 }
320 cmd.ExtraFiles = append(cmd.ExtraFiles, controlRead)
321 stderr, _ := cmd.StderrPipe()
322 tk := timekeeper.NewManualTime()
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700323 ph := vexec.NewParentHandle(cmd, vexec.TimeKeeperOpt{TimeKeeper: tk})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700324 defer clean(t, ph)
325 defer controlWrite.Close()
326 defer controlRead.Close()
327 // Wait for the parent to start waiting, then simulate a timeout.
328 go func() {
329 toWait := <-tk.Requests()
330 tk.AdvanceTime(toWait)
331 }()
332 err = waitForReady(t, cmd, name, 1, ph)
Mike Burrowsccca2f42015-03-27 13:57:29 -0700333 if verror.ErrorID(err) != vexec.ErrTimeout.ID {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700334 t.Errorf("Failed to get timeout: got %v\n", err)
335 } else {
336 // After the parent timed out, wake up the child and let it
337 // proceed.
338 if _, err = controlWrite.Write([]byte("wake")); err != nil {
339 t.Errorf("Failed to write to control pipe: %v", err)
340 } else {
341 // block waiting for error from child
342 if !expectMessage(stderr, result) {
343 t.Errorf("%s: failed to read '%s' from child\n", name, result)
344 }
345 }
346 }
347}
348
349func TestToReadySlow(t *testing.T) {
350 name := "TestToReadySlow"
351 cmd := helperCommand("testReadySlow")
352 // The control pipe is used to signal the child when to wake up.
353 controlRead, controlWrite, err := os.Pipe()
354 if err != nil {
355 t.Errorf("Failed to create control pipe: %v", err)
356 return
357 }
358 cmd.ExtraFiles = append(cmd.ExtraFiles, controlRead)
359 stderr, err := cmd.StderrPipe()
360 if err != nil {
361 t.Fatalf("%s: failed to get stderr pipe", name)
362 }
363 tk := timekeeper.NewManualTime()
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700364 ph := vexec.NewParentHandle(cmd, vexec.TimeKeeperOpt{TimeKeeper: tk})
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700365 defer clean(t, ph)
366 defer controlWrite.Close()
367 defer controlRead.Close()
368 // Wait for the parent to start waiting, simulate a short wait (but not
369 // enough to timeout the parent), then wake up the child.
370 go func() {
371 <-tk.Requests()
372 tk.AdvanceTime(2 * time.Second)
373 if _, err = controlWrite.Write([]byte("wake")); err != nil {
374 t.Errorf("Failed to write to control pipe: %v", err)
375 }
376 }()
377 if err := waitForReady(t, cmd, name, 4, ph); err != nil {
378 t.Errorf("%s: WaitForReady: %v (%v)", name, err, ph)
379 return
380 }
381 // After the child has replied, simulate a timeout on the server by
382 // advacing the time more; at this point, however, the timeout should no
383 // longer occur since the child already replied.
384 tk.AdvanceTime(2 * time.Second)
385 if result := ".."; !expectMessage(stderr, result) {
386 t.Errorf("%s: failed to read '%s' from child\n", name, result)
387 }
388}
389
390func TestToCompletion(t *testing.T) {
391 ph := readyHelper(t, "TestToCompletion", "testSuccess", "...ok")
David Why Use Two When One Will Do Presotto0fd29eb2015-08-31 15:43:15 -0700392 e := ph.Wait(10 * time.Second)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700393 if e != nil {
394 t.Errorf("Wait failed: err %s\n", e)
395 }
396 clean(t, ph)
397}
398
399func TestToCompletionError(t *testing.T) {
400 ph := readyHelper(t, "TestToCompletionError", "testError", "...err")
401 e := ph.Wait(time.Second)
402 if e == nil {
403 t.Errorf("Wait failed: err %s\n", e)
404 }
405 clean(t, ph)
406}
407
408func TestExtraFiles(t *testing.T) {
409 cmd := helperCommand("testExtraFiles")
410 rd, wr, err := os.Pipe()
411 if err != nil {
412 t.Fatalf("Failed to create pipe: %s\n", err)
413 }
414 cmd.ExtraFiles = append(cmd.ExtraFiles, rd)
415 msg := "hello there..."
416 fmt.Fprintf(wr, msg)
417 ph := readyHelperCmd(t, cmd, "TestExtraFiles", "child: "+msg)
418 if ph == nil {
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700419 t.Fatalf("Failed to get vexec.ParentHandle\n")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700420 }
David Why Use Two When One Will Do Presotto0fd29eb2015-08-31 15:43:15 -0700421 e := ph.Wait(10 * time.Second)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700422 if e != nil {
423 t.Errorf("Wait failed: err %s\n", e)
424 }
425 rd.Close()
426 wr.Close()
427 clean(t, ph)
428}
429
Cosmos Nicolaou4e213d72014-10-26 22:21:52 -0700430func TestExitEarly(t *testing.T) {
431 name := "exitEarly"
432 cmd := helperCommand(name)
433 tk := timekeeper.NewManualTime()
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700434 ph := vexec.NewParentHandle(cmd, vexec.TimeKeeperOpt{TimeKeeper: tk})
Cosmos Nicolaou4e213d72014-10-26 22:21:52 -0700435 err := ph.Start()
436 if err != nil {
437 t.Fatalf("%s: start: %v", name, err)
438 }
439 e := ph.Wait(time.Second)
440 if e == nil || e.Error() != "exit status 1" {
441 t.Errorf("Unexpected value for error: %v\n", e)
442 }
443 clean(t, ph)
444}
445
Bogdan Caprita650b1622014-11-21 15:11:05 -0800446func TestWaitAndCleanRace(t *testing.T) {
Bogdan Caprita90380122014-12-10 16:43:51 -0800447 name := "testReadyAndHang"
448 cmd := helperCommand(name)
Bogdan Caprita650b1622014-11-21 15:11:05 -0800449 tk := timekeeper.NewManualTime()
Jiri Simsad9a7b3c2015-08-12 16:38:27 -0700450 ph := vexec.NewParentHandle(cmd, vexec.TimeKeeperOpt{TimeKeeper: tk})
Bogdan Caprita90380122014-12-10 16:43:51 -0800451 if err := waitForReady(t, cmd, name, 1, ph); err != nil {
452 t.Errorf("%s: WaitForReady: %v (%v)", name, err, ph)
Bogdan Caprita650b1622014-11-21 15:11:05 -0800453 }
David Why Use Two When One Will Do Presotto0fd29eb2015-08-31 15:43:15 -0700454 // Drain the tk.Requests() channel since waitForReady can leave something in it.
455 select {
456 case <-tk.Requests():
457 default:
458 }
Bogdan Caprita650b1622014-11-21 15:11:05 -0800459 go func() {
460 // Wait for the ph.Wait below to block, then advance the time
461 // s.t. the Wait times out.
462 <-tk.Requests()
463 tk.AdvanceTime(2 * time.Second)
464 }()
Mike Burrowsccca2f42015-03-27 13:57:29 -0700465 if got, want := ph.Wait(time.Second), vexec.ErrTimeout.ID; got == nil || verror.ErrorID(got) != want {
Bogdan Caprita90380122014-12-10 16:43:51 -0800466 t.Errorf("Wait returned %v, wanted %v instead", got, want)
Bogdan Caprita650b1622014-11-21 15:11:05 -0800467 }
468 if got, want := ph.Clean(), "signal: killed"; got == nil || got.Error() != want {
Bogdan Caprita90380122014-12-10 16:43:51 -0800469 t.Errorf("Wait returned %v, wanted %v instead", got, want)
Bogdan Caprita650b1622014-11-21 15:11:05 -0800470 }
471}
472
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700473func verifyNoExecVariable() {
Todd Wang644ec622015-04-06 17:46:44 -0700474 version := os.Getenv(vexec.ExecVersionVariable)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700475 if len(version) != 0 {
Todd Wang644ec622015-04-06 17:46:44 -0700476 log.Fatalf("Version variable %q has a value: %s", vexec.ExecVersionVariable, version)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700477 }
478}
479
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700480// TestHelperProcess isn't a real test; it's used as a helper process
481// for the other tests.
482func TestHelperProcess(*testing.T) {
483 // Return immediately if this is not run as the child helper
484 // for the other tests.
Cosmos Nicolaouee7abc22014-05-27 10:50:03 -0700485 if os.Getenv("GO_WANT_HELPER_PROCESS_EXEC") != "1" {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700486 return
487 }
488 defer os.Exit(0)
489
Todd Wang644ec622015-04-06 17:46:44 -0700490 version := os.Getenv(vexec.ExecVersionVariable)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700491 if len(version) == 0 {
Todd Wang644ec622015-04-06 17:46:44 -0700492 log.Fatalf("Version variable %q has no value", vexec.ExecVersionVariable)
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700493 }
494
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700495 // Write errors to stderr or using log. since the parent
496 // process is reading stderr.
497 args := os.Args
498 for len(args) > 0 {
499 if args[0] == "--" {
500 args = args[1:]
501 break
502 }
503 args = args[1:]
504 }
505
506 if len(args) == 0 {
Bogdan Caprita90380122014-12-10 16:43:51 -0800507 log.Fatal("No command")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700508 }
509
510 cmd, args := args[0], args[1:]
511
512 switch cmd {
Cosmos Nicolaou4e213d72014-10-26 22:21:52 -0700513 case "exitEarly":
514 _, err := vexec.GetChildHandle()
515 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800516 log.Fatal(err)
Cosmos Nicolaou4e213d72014-10-26 22:21:52 -0700517 }
518 os.Exit(1)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700519 case "testNeverReady":
Jiri Simsa84059da2014-06-02 17:22:05 -0700520 _, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700521 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800522 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700523 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700524 verifyNoExecVariable()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700525 fmt.Fprintf(os.Stderr, "never ready")
526 case "testTooSlowToReady":
Jiri Simsa84059da2014-06-02 17:22:05 -0700527 ch, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700528 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800529 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700530 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700531 verifyNoExecVariable()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700532 // Wait for the parent to tell us when it's ok to proceed.
533 controlPipe := ch.NewExtraFile(0, "control_rd")
534 for {
535 buf := make([]byte, 100)
536 n, err := controlPipe.Read(buf)
537 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800538 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700539 }
540 if n > 0 {
541 break
542 }
543 }
544 // SetReady should return an error since the parent has
545 // timed out on us and we'd be writing to a closed pipe.
546 if err := ch.SetReady(); err != nil {
547 fmt.Fprintf(os.Stderr, "%s", err)
548 } else {
549 fmt.Fprintf(os.Stderr, "didn't get the expected error")
550 }
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -0700551 case "testFail":
552 ch, err := vexec.GetChildHandle()
553 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800554 log.Fatal(err)
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -0700555 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700556 verifyNoExecVariable()
Matt Rosencrantz119962e2015-02-12 16:08:38 -0800557 err = ch.SetFailed(fmt.Errorf("%s", strings.Join(args, " ")))
558 if err != nil {
Cosmos Nicolaou1c18c1c2014-10-08 16:37:10 -0700559 fmt.Fprintf(os.Stderr, "%s\n", err)
560 }
Matt Rosencrantz119962e2015-02-12 16:08:38 -0800561 // It's fine to call SetFailed multiple times.
562 if err2 := ch.SetFailed(fmt.Errorf("dummy")); err != err2 {
563 fmt.Fprintf(os.Stderr, "Received new error got: %v, want %v\n", err2, err)
564 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700565 case "testReady":
Jiri Simsa84059da2014-06-02 17:22:05 -0700566 ch, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700567 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800568 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700569 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700570 verifyNoExecVariable()
Matt Rosencrantz119962e2015-02-12 16:08:38 -0800571 err = ch.SetReady()
572 if err != nil {
573 fmt.Fprintf(os.Stderr, "%s\n", err)
574 }
575 // It's fine to call SetReady multiple times.
576 if err2 := ch.SetReady(); err != err2 {
577 fmt.Fprintf(os.Stderr, "Received new error got: %v, want %v\n", err2, err)
578 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700579 fmt.Fprintf(os.Stderr, ".")
580 case "testReadySlow":
Jiri Simsa84059da2014-06-02 17:22:05 -0700581 ch, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700582 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800583 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700584 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700585 verifyNoExecVariable()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700586 // Wait for the parent to tell us when it's ok to proceed.
587 controlPipe := ch.NewExtraFile(0, "control_rd")
588 for {
589 buf := make([]byte, 100)
590 n, err := controlPipe.Read(buf)
591 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800592 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700593 }
594 if n > 0 {
595 break
596 }
597 }
598 ch.SetReady()
599 fmt.Fprintf(os.Stderr, "..")
Bogdan Caprita90380122014-12-10 16:43:51 -0800600 case "testReadyAndHang":
601 ch, err := vexec.GetChildHandle()
602 if err != nil {
603 log.Fatal(err)
604 }
605 verifyNoExecVariable()
606 ch.SetReady()
607 <-time.After(time.Minute)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700608 case "testSuccess", "testError":
Jiri Simsa84059da2014-06-02 17:22:05 -0700609 ch, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700610 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800611 log.Fatal(err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700612 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700613 verifyNoExecVariable()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700614 ch.SetReady()
615 rc := make(chan int)
616 go func() {
617 if cmd == "testError" {
618 fmt.Fprintf(os.Stderr, "...err")
619 rc <- 1
620 } else {
621 fmt.Fprintf(os.Stderr, "...ok")
622 rc <- 0
623 }
624 }()
625 r := <-rc
626 os.Exit(r)
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700627 case "testConfig":
Jiri Simsa84059da2014-06-02 17:22:05 -0700628 ch, err := vexec.GetChildHandle()
Jiri Simsac199bc12014-05-30 12:52:24 -0700629 if err != nil {
630 log.Fatalf("%v", err)
631 } else {
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700632 verifyNoExecVariable()
Bogdan Capritaa4d9ee42014-06-20 16:42:53 -0700633 serialized, err := ch.Config.Serialize()
634 if err != nil {
635 log.Fatalf("%v", err)
636 }
637 fmt.Fprintf(os.Stderr, "%s", serialized)
Jiri Simsac199bc12014-05-30 12:52:24 -0700638 }
639 case "testSecret":
Jiri Simsa84059da2014-06-02 17:22:05 -0700640 ch, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700641 if err != nil {
642 log.Fatalf("%s", err)
643 } else {
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700644 verifyNoExecVariable()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700645 fmt.Fprintf(os.Stderr, "%s", ch.Secret)
646 }
647 case "testExtraFiles":
Jiri Simsa84059da2014-06-02 17:22:05 -0700648 ch, err := vexec.GetChildHandle()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700649 if err != nil {
Bogdan Caprita90380122014-12-10 16:43:51 -0800650 log.Fatalf("error.... %s\n", err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700651 }
Cosmos Nicolaoue664f3f2014-10-20 17:40:05 -0700652 verifyNoExecVariable()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700653 err = ch.SetReady()
654 rd := ch.NewExtraFile(0, "read")
655 buf := make([]byte, 1024)
656 if n, err := rd.Read(buf); err != nil {
657 fmt.Fprintf(os.Stderr, "child: error %s\n", err)
658 } else {
659 fmt.Fprintf(os.Stderr, "child: %s", string(buf[:n]))
660 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700661 }
662}