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 "os" 14 osexec "os/exec" 15 "os/signal" 16 "syscall" 17 "testing" 18 ) 19 20 func TestEPIPE(t *testing.T) { 21 r, w, err := os.Pipe() 22 if err != nil { 23 t.Fatal(err) 24 } 25 if err := r.Close(); err != nil { 26 t.Fatal(err) 27 } 28 29 // Every time we write to the pipe we should get an EPIPE. 30 for i := 0; i < 20; i++ { 31 _, err = w.Write([]byte("hi")) 32 if err == nil { 33 t.Fatal("unexpected success of Write to broken pipe") 34 } 35 if pe, ok := err.(*os.PathError); ok { 36 err = pe.Err 37 } 38 if se, ok := err.(*os.SyscallError); ok { 39 err = se.Err 40 } 41 if err != syscall.EPIPE { 42 t.Errorf("iteration %d: got %v, expected EPIPE", i, err) 43 } 44 } 45 } 46 47 func TestStdPipe(t *testing.T) { 48 testenv.MustHaveExec(t) 49 r, w, err := os.Pipe() 50 if err != nil { 51 t.Fatal(err) 52 } 53 if err := r.Close(); err != nil { 54 t.Fatal(err) 55 } 56 // Invoke the test program to run the test and write to a closed pipe. 57 // If sig is false: 58 // writing to stdout or stderr should cause an immediate SIGPIPE; 59 // writing to descriptor 3 should fail with EPIPE and then exit 0. 60 // If sig is true: 61 // all writes should fail with EPIPE and then exit 0. 62 for _, sig := range []bool{false, true} { 63 for dest := 1; dest < 4; dest++ { 64 cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper") 65 cmd.Stdout = w 66 cmd.Stderr = w 67 cmd.ExtraFiles = []*os.File{w} 68 cmd.Env = append(os.Environ(), fmt.Sprintf("GO_TEST_STD_PIPE_HELPER=%d", dest)) 69 if sig { 70 cmd.Env = append(cmd.Env, "GO_TEST_STD_PIPE_HELPER_SIGNAL=1") 71 } 72 if err := cmd.Run(); err == nil { 73 if !sig && dest < 3 { 74 t.Errorf("unexpected success of write to closed pipe %d sig %t in child", dest, sig) 75 } 76 } else if ee, ok := err.(*osexec.ExitError); !ok { 77 t.Errorf("unexpected exec error type %T: %v", err, err) 78 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 79 t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys()) 80 } else if ws.Signaled() && ws.Signal() == syscall.SIGPIPE { 81 if sig || dest > 2 { 82 t.Errorf("unexpected SIGPIPE signal for descriptor %d sig %t", dest, sig) 83 } 84 } else { 85 t.Errorf("unexpected exit status %v for descriptor %ds sig %t", err, dest, sig) 86 } 87 } 88 } 89 } 90 91 // This is a helper for TestStdPipe. It's not a test in itself. 92 func TestStdPipeHelper(t *testing.T) { 93 if os.Getenv("GO_TEST_STD_PIPE_HELPER_SIGNAL") != "" { 94 signal.Notify(make(chan os.Signal, 1), syscall.SIGPIPE) 95 } 96 switch os.Getenv("GO_TEST_STD_PIPE_HELPER") { 97 case "1": 98 os.Stdout.Write([]byte("stdout")) 99 case "2": 100 os.Stderr.Write([]byte("stderr")) 101 case "3": 102 if _, err := os.NewFile(3, "3").Write([]byte("3")); err == nil { 103 os.Exit(3) 104 } 105 default: 106 t.Skip("skipping test helper") 107 } 108 // For stdout/stderr, we should have crashed with a broken pipe error. 109 // The caller will be looking for that exit status, 110 // so just exit normally here to cause a failure in the caller. 111 // For descriptor 3, a normal exit is expected. 112 os.Exit(0) 113 } 114