github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/os/pipe_test.go (about) 1 // Copyright 2015 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. 4 5 // Test broken pipes on Unix systems. 6 // +build !windows,!plan9,!nacl 7 8 package os_test 9 10 import ( 11 "fmt" 12 "internal/testenv" 13 "io/ioutil" 14 "os" 15 osexec "os/exec" 16 "os/signal" 17 "runtime" 18 "strconv" 19 "strings" 20 "syscall" 21 "testing" 22 "time" 23 ) 24 25 func TestEPIPE(t *testing.T) { 26 r, w, err := os.Pipe() 27 if err != nil { 28 t.Fatal(err) 29 } 30 if err := r.Close(); err != nil { 31 t.Fatal(err) 32 } 33 34 // Every time we write to the pipe we should get an EPIPE. 35 for i := 0; i < 20; i++ { 36 _, err = w.Write([]byte("hi")) 37 if err == nil { 38 t.Fatal("unexpected success of Write to broken pipe") 39 } 40 if pe, ok := err.(*os.PathError); ok { 41 err = pe.Err 42 } 43 if se, ok := err.(*os.SyscallError); ok { 44 err = se.Err 45 } 46 if err != syscall.EPIPE { 47 t.Errorf("iteration %d: got %v, expected EPIPE", i, err) 48 } 49 } 50 } 51 52 func TestStdPipe(t *testing.T) { 53 testenv.MustHaveExec(t) 54 r, w, err := os.Pipe() 55 if err != nil { 56 t.Fatal(err) 57 } 58 if err := r.Close(); err != nil { 59 t.Fatal(err) 60 } 61 // Invoke the test program to run the test and write to a closed pipe. 62 // If sig is false: 63 // writing to stdout or stderr should cause an immediate SIGPIPE; 64 // writing to descriptor 3 should fail with EPIPE and then exit 0. 65 // If sig is true: 66 // all writes should fail with EPIPE and then exit 0. 67 for _, sig := range []bool{false, true} { 68 for dest := 1; dest < 4; dest++ { 69 cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper") 70 cmd.Stdout = w 71 cmd.Stderr = w 72 cmd.ExtraFiles = []*os.File{w} 73 cmd.Env = append(os.Environ(), fmt.Sprintf("GO_TEST_STD_PIPE_HELPER=%d", dest)) 74 if sig { 75 cmd.Env = append(cmd.Env, "GO_TEST_STD_PIPE_HELPER_SIGNAL=1") 76 } 77 if err := cmd.Run(); err == nil { 78 if !sig && dest < 3 { 79 t.Errorf("unexpected success of write to closed pipe %d sig %t in child", dest, sig) 80 } 81 } else if ee, ok := err.(*osexec.ExitError); !ok { 82 t.Errorf("unexpected exec error type %T: %v", err, err) 83 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 84 t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys()) 85 } else if ws.Signaled() && ws.Signal() == syscall.SIGPIPE { 86 if sig || dest > 2 { 87 t.Errorf("unexpected SIGPIPE signal for descriptor %d sig %t", dest, sig) 88 } 89 } else { 90 t.Errorf("unexpected exit status %v for descriptor %d sig %t", err, dest, sig) 91 } 92 } 93 } 94 } 95 96 // This is a helper for TestStdPipe. It's not a test in itself. 97 func TestStdPipeHelper(t *testing.T) { 98 if os.Getenv("GO_TEST_STD_PIPE_HELPER_SIGNAL") != "" { 99 signal.Notify(make(chan os.Signal, 1), syscall.SIGPIPE) 100 } 101 switch os.Getenv("GO_TEST_STD_PIPE_HELPER") { 102 case "1": 103 os.Stdout.Write([]byte("stdout")) 104 case "2": 105 os.Stderr.Write([]byte("stderr")) 106 case "3": 107 if _, err := os.NewFile(3, "3").Write([]byte("3")); err == nil { 108 os.Exit(3) 109 } 110 default: 111 t.Skip("skipping test helper") 112 } 113 // For stdout/stderr, we should have crashed with a broken pipe error. 114 // The caller will be looking for that exit status, 115 // so just exit normally here to cause a failure in the caller. 116 // For descriptor 3, a normal exit is expected. 117 os.Exit(0) 118 } 119 120 func testClosedPipeRace(t *testing.T, read bool) { 121 switch runtime.GOOS { 122 case "freebsd": 123 t.Skip("FreeBSD does not use the poller; issue 19093") 124 } 125 126 limit := 1 127 if !read { 128 // Get the amount we have to write to overload a pipe 129 // with no reader. 130 limit = 65537 131 if b, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size"); err == nil { 132 if i, err := strconv.Atoi(strings.TrimSpace(string(b))); err == nil { 133 limit = i + 1 134 } 135 } 136 t.Logf("using pipe write limit of %d", limit) 137 } 138 139 r, w, err := os.Pipe() 140 if err != nil { 141 t.Fatal(err) 142 } 143 defer r.Close() 144 defer w.Close() 145 146 // Close the read end of the pipe in a goroutine while we are 147 // writing to the write end, or vice-versa. 148 go func() { 149 // Give the main goroutine a chance to enter the Read or 150 // Write call. This is sloppy but the test will pass even 151 // if we close before the read/write. 152 time.Sleep(20 * time.Millisecond) 153 154 var err error 155 if read { 156 err = r.Close() 157 } else { 158 err = w.Close() 159 } 160 if err != nil { 161 t.Error(err) 162 } 163 }() 164 165 b := make([]byte, limit) 166 if read { 167 _, err = r.Read(b[:]) 168 } else { 169 _, err = w.Write(b[:]) 170 } 171 if err == nil { 172 t.Error("I/O on closed pipe unexpectedly succeeded") 173 } else if pe, ok := err.(*os.PathError); !ok { 174 t.Errorf("I/O on closed pipe returned unexpected error type %T; expected os.PathError", pe) 175 } else if pe.Err != os.ErrClosed { 176 t.Errorf("got error %q but expected %q", pe.Err, os.ErrClosed) 177 } else { 178 t.Logf("I/O returned expected error %q", err) 179 } 180 } 181 182 func TestClosedPipeRaceRead(t *testing.T) { 183 testClosedPipeRace(t, true) 184 } 185 186 func TestClosedPipeRaceWrite(t *testing.T) { 187 testClosedPipeRace(t, false) 188 } 189 190 // Issue 20915: Reading on nonblocking fd should not return "waiting 191 // for unsupported file type." Currently it returns EAGAIN; it is 192 // possible that in the future it will simply wait for data. 193 func TestReadNonblockingFd(t *testing.T) { 194 if os.Getenv("GO_WANT_READ_NONBLOCKING_FD") == "1" { 195 fd := int(os.Stdin.Fd()) 196 syscall.SetNonblock(fd, true) 197 defer syscall.SetNonblock(fd, false) 198 _, err := os.Stdin.Read(make([]byte, 1)) 199 if err != nil { 200 if perr, ok := err.(*os.PathError); !ok || perr.Err != syscall.EAGAIN { 201 t.Fatalf("read on nonblocking stdin got %q, should have gotten EAGAIN", err) 202 } 203 } 204 os.Exit(0) 205 } 206 207 testenv.MustHaveExec(t) 208 r, w, err := os.Pipe() 209 if err != nil { 210 t.Fatal(err) 211 } 212 defer r.Close() 213 defer w.Close() 214 cmd := osexec.Command(os.Args[0], "-test.run="+t.Name()) 215 cmd.Env = append(os.Environ(), "GO_WANT_READ_NONBLOCKING_FD=1") 216 cmd.Stdin = r 217 output, err := cmd.CombinedOutput() 218 t.Logf("%s", output) 219 if err != nil { 220 t.Errorf("child process failed: %v", err) 221 } 222 }