github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/os/signal/signal_test.go (about)

     1  // Copyright 2009 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  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     6  
     7  package signal
     8  
     9  import (
    10  	"flag"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"runtime"
    15  	"strconv"
    16  	"syscall"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
    22  	select {
    23  	case s := <-c:
    24  		if s != sig {
    25  			t.Fatalf("signal was %v, want %v", s, sig)
    26  		}
    27  	case <-time.After(1 * time.Second):
    28  		t.Fatalf("timeout waiting for %v", sig)
    29  	}
    30  }
    31  
    32  // Test that basic signal handling works.
    33  func TestSignal(t *testing.T) {
    34  	// Ask for SIGHUP
    35  	c := make(chan os.Signal, 1)
    36  	Notify(c, syscall.SIGHUP)
    37  	defer Stop(c)
    38  
    39  	// Send this process a SIGHUP
    40  	t.Logf("sighup...")
    41  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    42  	waitSig(t, c, syscall.SIGHUP)
    43  
    44  	// Ask for everything we can get.
    45  	c1 := make(chan os.Signal, 1)
    46  	Notify(c1)
    47  
    48  	// Send this process a SIGWINCH
    49  	t.Logf("sigwinch...")
    50  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
    51  	waitSig(t, c1, syscall.SIGWINCH)
    52  
    53  	// Send two more SIGHUPs, to make sure that
    54  	// they get delivered on c1 and that not reading
    55  	// from c does not block everything.
    56  	t.Logf("sighup...")
    57  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    58  	waitSig(t, c1, syscall.SIGHUP)
    59  	t.Logf("sighup...")
    60  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    61  	waitSig(t, c1, syscall.SIGHUP)
    62  
    63  	// The first SIGHUP should be waiting for us on c.
    64  	waitSig(t, c, syscall.SIGHUP)
    65  }
    66  
    67  func TestStress(t *testing.T) {
    68  	dur := 3 * time.Second
    69  	if testing.Short() {
    70  		dur = 100 * time.Millisecond
    71  	}
    72  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
    73  	done := make(chan bool)
    74  	finished := make(chan bool)
    75  	go func() {
    76  		sig := make(chan os.Signal, 1)
    77  		Notify(sig, syscall.SIGUSR1)
    78  		defer Stop(sig)
    79  	Loop:
    80  		for {
    81  			select {
    82  			case <-sig:
    83  			case <-done:
    84  				break Loop
    85  			}
    86  		}
    87  		finished <- true
    88  	}()
    89  	go func() {
    90  	Loop:
    91  		for {
    92  			select {
    93  			case <-done:
    94  				break Loop
    95  			default:
    96  				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
    97  				runtime.Gosched()
    98  			}
    99  		}
   100  		finished <- true
   101  	}()
   102  	time.Sleep(dur)
   103  	close(done)
   104  	<-finished
   105  	<-finished
   106  	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
   107  	// into subsequent TestSignal() causing failure.
   108  	// Sleep for a while to reduce the possibility of the failure.
   109  	time.Sleep(10 * time.Millisecond)
   110  }
   111  
   112  var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
   113  
   114  // Test that Stop cancels the channel's registrations.
   115  func TestStop(t *testing.T) {
   116  	sigs := []syscall.Signal{
   117  		syscall.SIGWINCH,
   118  		syscall.SIGHUP,
   119  	}
   120  
   121  	for _, sig := range sigs {
   122  		// Send the signal.
   123  		// If it's SIGWINCH, we should not see it.
   124  		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   125  		if sig != syscall.SIGHUP || *sendUncaughtSighup == 1 {
   126  			syscall.Kill(syscall.Getpid(), sig)
   127  		}
   128  		time.Sleep(100 * time.Millisecond)
   129  
   130  		// Ask for signal
   131  		c := make(chan os.Signal, 1)
   132  		Notify(c, sig)
   133  		defer Stop(c)
   134  
   135  		// Send this process that signal
   136  		syscall.Kill(syscall.Getpid(), sig)
   137  		waitSig(t, c, sig)
   138  
   139  		Stop(c)
   140  		select {
   141  		case s := <-c:
   142  			t.Fatalf("unexpected signal %v", s)
   143  		case <-time.After(100 * time.Millisecond):
   144  			// nothing to read - good
   145  		}
   146  
   147  		// Send the signal.
   148  		// If it's SIGWINCH, we should not see it.
   149  		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   150  		if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
   151  			syscall.Kill(syscall.Getpid(), sig)
   152  		}
   153  
   154  		select {
   155  		case s := <-c:
   156  			t.Fatalf("unexpected signal %v", s)
   157  		case <-time.After(100 * time.Millisecond):
   158  			// nothing to read - good
   159  		}
   160  	}
   161  }
   162  
   163  // Test that when run under nohup, an uncaught SIGHUP does not kill the program,
   164  // but a
   165  func TestNohup(t *testing.T) {
   166  	// Ugly: ask for SIGHUP so that child will not have no-hup set
   167  	// even if test is running under nohup environment.
   168  	// We have no intention of reading from c.
   169  	c := make(chan os.Signal, 1)
   170  	Notify(c, syscall.SIGHUP)
   171  
   172  	// When run without nohup, the test should crash on an uncaught SIGHUP.
   173  	// When run under nohup, the test should ignore uncaught SIGHUPs,
   174  	// because the runtime is not supposed to be listening for them.
   175  	// Either way, TestStop should still be able to catch them when it wants them
   176  	// and then when it stops wanting them, the original behavior should resume.
   177  	//
   178  	// send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
   179  	// send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
   180  	//
   181  	// Both should fail without nohup and succeed with nohup.
   182  
   183  	for i := 1; i <= 2; i++ {
   184  		out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
   185  		if err == nil {
   186  			t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
   187  		}
   188  	}
   189  
   190  	Stop(c)
   191  
   192  	// Again, this time with nohup, assuming we can find it.
   193  	_, err := os.Stat("/usr/bin/nohup")
   194  	if err != nil {
   195  		t.Skip("cannot find nohup; skipping second half of test")
   196  	}
   197  
   198  	for i := 1; i <= 2; i++ {
   199  		os.Remove("nohup.out")
   200  		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
   201  
   202  		data, _ := ioutil.ReadFile("nohup.out")
   203  		os.Remove("nohup.out")
   204  		if err != nil {
   205  			t.Fatalf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", i, err, out, data)
   206  		}
   207  	}
   208  }