github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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  	"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  }