libgo: update to Go1.10rc2
[official-gcc.git] / libgo / go / os / signal / signal_cgo_test.go
blob84a2a08ce9b6e5a11e072f581b72cbc890e40414
1 // Copyright 2017 The Go 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.
5 // +build darwin dragonfly freebsd linux,!android netbsd openbsd
6 // +build cgo
8 // Note that this test does not work on Solaris: issue #22849.
9 // Don't run the test on Android because at least some versions of the
10 // C library do not define the posix_openpt function.
12 package signal_test
14 import (
15 "bufio"
16 "bytes"
17 "context"
18 "fmt"
19 "io"
20 "os"
21 "os/exec"
22 "os/signal/internal/pty"
23 "strconv"
24 "strings"
25 "syscall"
26 "testing"
27 "time"
30 func TestTerminalSignal(t *testing.T) {
31 const enteringRead = "test program entering read"
32 if os.Getenv("GO_TEST_TERMINAL_SIGNALS") != "" {
33 var b [1]byte
34 fmt.Println(enteringRead)
35 n, err := os.Stdin.Read(b[:])
36 if n == 1 {
37 if b[0] == '\n' {
38 // This is what we expect
39 fmt.Println("read newline")
40 } else {
41 fmt.Printf("read 1 byte: %q\n", b)
43 } else {
44 fmt.Printf("read %d bytes\n", n)
46 if err != nil {
47 fmt.Println(err)
48 os.Exit(1)
50 os.Exit(0)
53 t.Parallel()
55 // The test requires a shell that uses job control.
56 bash, err := exec.LookPath("bash")
57 if err != nil {
58 t.Skipf("could not find bash: %v", err)
61 scale := 1
62 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
63 if sc, err := strconv.Atoi(s); err == nil {
64 scale = sc
67 pause := time.Duration(scale) * 10 * time.Millisecond
68 wait := time.Duration(scale) * 5 * time.Second
70 // The test only fails when using a "slow device," in this
71 // case a pseudo-terminal.
73 master, sname, err := pty.Open()
74 if err != nil {
75 ptyErr := err.(*pty.PtyError)
76 if ptyErr.FuncName == "posix_openpt" && ptyErr.Errno == syscall.EACCES {
77 t.Skip("posix_openpt failed with EACCES, assuming chroot and skipping")
79 t.Fatal(err)
81 defer master.Close()
82 slave, err := os.OpenFile(sname, os.O_RDWR, 0)
83 if err != nil {
84 t.Fatal(err)
86 defer slave.Close()
88 // Start an interactive shell.
89 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
90 defer cancel()
91 cmd := exec.CommandContext(ctx, bash, "--norc", "--noprofile", "-i")
92 cmd.Stdin = slave
93 cmd.Stdout = slave
94 cmd.Stderr = slave
95 cmd.SysProcAttr = &syscall.SysProcAttr{
96 Setsid: true,
97 Setctty: true,
98 Ctty: int(slave.Fd()),
101 if err := cmd.Start(); err != nil {
102 t.Fatal(err)
105 if err := slave.Close(); err != nil {
106 t.Errorf("closing slave: %v", err)
109 progReady := make(chan bool)
110 sawPrompt := make(chan bool, 10)
111 const prompt = "prompt> "
113 // Read data from master in the background.
114 go func() {
115 input := bufio.NewReader(master)
116 var line, handled []byte
117 for {
118 b, err := input.ReadByte()
119 if err != nil {
120 if len(line) > 0 || len(handled) > 0 {
121 t.Logf("%q", append(handled, line...))
123 if perr, ok := err.(*os.PathError); ok {
124 err = perr.Err
126 // EOF means master is closed.
127 // EIO means child process is done.
128 // "file already closed" means deferred close of master has happened.
129 if err != io.EOF && err != syscall.EIO && !strings.Contains(err.Error(), "file already closed") {
130 t.Logf("error reading from master: %v", err)
132 return
135 line = append(line, b)
137 if b == '\n' {
138 t.Logf("%q", append(handled, line...))
139 line = nil
140 handled = nil
141 continue
144 if bytes.Contains(line, []byte(enteringRead)) {
145 close(progReady)
146 handled = append(handled, line...)
147 line = nil
148 } else if bytes.Contains(line, []byte(prompt)) && !bytes.Contains(line, []byte("PS1=")) {
149 sawPrompt <- true
150 handled = append(handled, line...)
151 line = nil
156 // Set the bash prompt so that we can see it.
157 if _, err := master.Write([]byte("PS1='" + prompt + "'\n")); err != nil {
158 t.Fatalf("setting prompt: %v", err)
160 select {
161 case <-sawPrompt:
162 case <-time.After(wait):
163 t.Fatal("timed out waiting for shell prompt")
166 // Start a small program that reads from stdin
167 // (namely the code at the top of this function).
168 if _, err := master.Write([]byte("GO_TEST_TERMINAL_SIGNALS=1 " + os.Args[0] + " -test.run=TestTerminalSignal\n")); err != nil {
169 t.Fatal(err)
172 // Wait for the program to print that it is starting.
173 select {
174 case <-progReady:
175 case <-time.After(wait):
176 t.Fatal("timed out waiting for program to start")
179 // Give the program time to enter the read call.
180 // It doesn't matter much if we occasionally don't wait long enough;
181 // we won't be testing what we want to test, but the overall test
182 // will pass.
183 time.Sleep(pause)
185 // Send a ^Z to stop the program.
186 if _, err := master.Write([]byte{26}); err != nil {
187 t.Fatalf("writing ^Z to pty: %v", err)
190 // Wait for the program to stop and return to the shell.
191 select {
192 case <-sawPrompt:
193 case <-time.After(wait):
194 t.Fatal("timed out waiting for shell prompt")
197 // Restart the stopped program.
198 if _, err := master.Write([]byte("fg\n")); err != nil {
199 t.Fatalf("writing %q to pty: %v", "fg", err)
202 // Give the process time to restart.
203 // This is potentially racy: if the process does not restart
204 // quickly enough then the byte we send will go to bash rather
205 // than the program. Unfortunately there isn't anything we can
206 // look for to know that the program is running again.
207 // bash will print the program name, but that happens before it
208 // restarts the program.
209 time.Sleep(10 * pause)
211 // Write some data for the program to read,
212 // which should cause it to exit.
213 if _, err := master.Write([]byte{'\n'}); err != nil {
214 t.Fatalf("writing %q to pty: %v", "\n", err)
217 // Wait for the program to exit.
218 select {
219 case <-sawPrompt:
220 case <-time.After(wait):
221 t.Fatal("timed out waiting for shell prompt")
224 // Exit the shell with the program's exit status.
225 if _, err := master.Write([]byte("exit $?\n")); err != nil {
226 t.Fatalf("writing %q to pty: %v", "exit", err)
229 if err = cmd.Wait(); err != nil {
230 t.Errorf("subprogram failed: %v", err)