github.com/ActiveState/go@v0.0.0-20170614201249-0b81c023a722/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_test
     8  
     9  import (
    10  	"flag"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	. "os/signal"
    15  	"runtime"
    16  	"strconv"
    17  	"syscall"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
    23  	select {
    24  	case s := <-c:
    25  		if s != sig {
    26  			t.Fatalf("signal was %v, want %v", s, sig)
    27  		}
    28  	case <-time.After(1 * time.Second):
    29  		t.Fatalf("timeout waiting for %v", sig)
    30  	}
    31  }
    32  
    33  // Test that basic signal handling works.
    34  func TestSignal(t *testing.T) {
    35  	// Ask for SIGHUP
    36  	c := make(chan os.Signal, 1)
    37  	Notify(c, syscall.SIGHUP)
    38  	defer Stop(c)
    39  
    40  	// Send this process a SIGHUP
    41  	t.Logf("sighup...")
    42  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    43  	waitSig(t, c, syscall.SIGHUP)
    44  
    45  	// Ask for everything we can get.
    46  	c1 := make(chan os.Signal, 1)
    47  	Notify(c1)
    48  
    49  	// Send this process a SIGWINCH
    50  	t.Logf("sigwinch...")
    51  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
    52  	waitSig(t, c1, syscall.SIGWINCH)
    53  
    54  	// Send two more SIGHUPs, to make sure that
    55  	// they get delivered on c1 and that not reading
    56  	// from c does not block everything.
    57  	t.Logf("sighup...")
    58  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    59  	waitSig(t, c1, syscall.SIGHUP)
    60  	t.Logf("sighup...")
    61  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    62  	waitSig(t, c1, syscall.SIGHUP)
    63  
    64  	// The first SIGHUP should be waiting for us on c.
    65  	waitSig(t, c, syscall.SIGHUP)
    66  }
    67  
    68  func TestStress(t *testing.T) {
    69  	dur := 3 * time.Second
    70  	if testing.Short() {
    71  		dur = 100 * time.Millisecond
    72  	}
    73  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
    74  	done := make(chan bool)
    75  	finished := make(chan bool)
    76  	go func() {
    77  		sig := make(chan os.Signal, 1)
    78  		Notify(sig, syscall.SIGUSR1)
    79  		defer Stop(sig)
    80  	Loop:
    81  		for {
    82  			select {
    83  			case <-sig:
    84  			case <-done:
    85  				break Loop
    86  			}
    87  		}
    88  		finished <- true
    89  	}()
    90  	go func() {
    91  	Loop:
    92  		for {
    93  			select {
    94  			case <-done:
    95  				break Loop
    96  			default:
    97  				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
    98  				runtime.Gosched()
    99  			}
   100  		}
   101  		finished <- true
   102  	}()
   103  	time.Sleep(dur)
   104  	close(done)
   105  	<-finished
   106  	<-finished
   107  	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
   108  	// into subsequent TestSignal() causing failure.
   109  	// Sleep for a while to reduce the possibility of the failure.
   110  	time.Sleep(10 * time.Millisecond)
   111  }
   112  
   113  func testCancel(t *testing.T, ignore bool) {
   114  	// Send SIGWINCH. By default this signal should be ignored.
   115  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   116  	time.Sleep(100 * time.Millisecond)
   117  
   118  	// Ask to be notified on c1 when a SIGWINCH is received.
   119  	c1 := make(chan os.Signal, 1)
   120  	Notify(c1, syscall.SIGWINCH)
   121  	defer Stop(c1)
   122  
   123  	// Ask to be notified on c2 when a SIGHUP is received.
   124  	c2 := make(chan os.Signal, 1)
   125  	Notify(c2, syscall.SIGHUP)
   126  	defer Stop(c2)
   127  
   128  	// Send this process a SIGWINCH and wait for notification on c1.
   129  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   130  	waitSig(t, c1, syscall.SIGWINCH)
   131  
   132  	// Send this process a SIGHUP and wait for notification on c2.
   133  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   134  	waitSig(t, c2, syscall.SIGHUP)
   135  
   136  	// Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
   137  	if ignore {
   138  		Ignore(syscall.SIGWINCH, syscall.SIGHUP)
   139  	} else {
   140  		Reset(syscall.SIGWINCH, syscall.SIGHUP)
   141  	}
   142  
   143  	// At this point we do not expect any further signals on c1.
   144  	// However, it is just barely possible that the initial SIGWINCH
   145  	// at the start of this function was delivered after we called
   146  	// Notify on c1. In that case the waitSig for SIGWINCH may have
   147  	// picked up that initial SIGWINCH, and the second SIGWINCH may
   148  	// then have been delivered on the channel. This sequence of events
   149  	// may have caused issue 15661.
   150  	// So, read any possible signal from the channel now.
   151  	select {
   152  	case <-c1:
   153  	default:
   154  	}
   155  
   156  	// Send this process a SIGWINCH. It should be ignored.
   157  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   158  
   159  	// If ignoring, Send this process a SIGHUP. It should be ignored.
   160  	if ignore {
   161  		syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   162  	}
   163  
   164  	select {
   165  	case s := <-c1:
   166  		t.Fatalf("unexpected signal %v", s)
   167  	case <-time.After(100 * time.Millisecond):
   168  		// nothing to read - good
   169  	}
   170  
   171  	select {
   172  	case s := <-c2:
   173  		t.Fatalf("unexpected signal %v", s)
   174  	case <-time.After(100 * time.Millisecond):
   175  		// nothing to read - good
   176  	}
   177  
   178  	// Reset the signal handlers for all signals.
   179  	Reset()
   180  }
   181  
   182  // Test that Reset cancels registration for listed signals on all channels.
   183  func TestReset(t *testing.T) {
   184  	testCancel(t, false)
   185  }
   186  
   187  // Test that Ignore cancels registration for listed signals on all channels.
   188  func TestIgnore(t *testing.T) {
   189  	testCancel(t, true)
   190  }
   191  
   192  var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
   193  
   194  // Test that Stop cancels the channel's registrations.
   195  func TestStop(t *testing.T) {
   196  	sigs := []syscall.Signal{
   197  		syscall.SIGWINCH,
   198  		syscall.SIGHUP,
   199  		syscall.SIGUSR1,
   200  	}
   201  
   202  	for _, sig := range sigs {
   203  		// Send the signal.
   204  		// If it's SIGWINCH, we should not see it.
   205  		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   206  		if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
   207  			syscall.Kill(syscall.Getpid(), sig)
   208  		}
   209  		time.Sleep(100 * time.Millisecond)
   210  
   211  		// Ask for signal
   212  		c := make(chan os.Signal, 1)
   213  		Notify(c, sig)
   214  		defer Stop(c)
   215  
   216  		// Send this process that signal
   217  		syscall.Kill(syscall.Getpid(), sig)
   218  		waitSig(t, c, sig)
   219  
   220  		Stop(c)
   221  		select {
   222  		case s := <-c:
   223  			t.Fatalf("unexpected signal %v", s)
   224  		case <-time.After(100 * time.Millisecond):
   225  			// nothing to read - good
   226  		}
   227  
   228  		// Send the signal.
   229  		// If it's SIGWINCH, we should not see it.
   230  		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   231  		if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
   232  			syscall.Kill(syscall.Getpid(), sig)
   233  		}
   234  
   235  		select {
   236  		case s := <-c:
   237  			t.Fatalf("unexpected signal %v", s)
   238  		case <-time.After(100 * time.Millisecond):
   239  			// nothing to read - good
   240  		}
   241  	}
   242  }
   243  
   244  // Test that when run under nohup, an uncaught SIGHUP does not kill the program,
   245  // but a
   246  func TestNohup(t *testing.T) {
   247  	// Ugly: ask for SIGHUP so that child will not have no-hup set
   248  	// even if test is running under nohup environment.
   249  	// We have no intention of reading from c.
   250  	c := make(chan os.Signal, 1)
   251  	Notify(c, syscall.SIGHUP)
   252  
   253  	// When run without nohup, the test should crash on an uncaught SIGHUP.
   254  	// When run under nohup, the test should ignore uncaught SIGHUPs,
   255  	// because the runtime is not supposed to be listening for them.
   256  	// Either way, TestStop should still be able to catch them when it wants them
   257  	// and then when it stops wanting them, the original behavior should resume.
   258  	//
   259  	// send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
   260  	// send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
   261  	//
   262  	// Both should fail without nohup and succeed with nohup.
   263  
   264  	for i := 1; i <= 2; i++ {
   265  		out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
   266  		if err == nil {
   267  			t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
   268  		}
   269  	}
   270  
   271  	Stop(c)
   272  
   273  	// Skip the nohup test below when running in tmux on darwin, since nohup
   274  	// doesn't work correctly there. See issue #5135.
   275  	if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
   276  		t.Skip("Skipping nohup test due to running in tmux on darwin")
   277  	}
   278  
   279  	// Again, this time with nohup, assuming we can find it.
   280  	_, err := os.Stat("/usr/bin/nohup")
   281  	if err != nil {
   282  		t.Skip("cannot find nohup; skipping second half of test")
   283  	}
   284  
   285  	for i := 1; i <= 2; i++ {
   286  		os.Remove("nohup.out")
   287  		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
   288  
   289  		data, _ := ioutil.ReadFile("nohup.out")
   290  		os.Remove("nohup.out")
   291  		if err != nil {
   292  			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)
   293  		}
   294  	}
   295  }
   296  
   297  // Test that SIGCONT works (issue 8953).
   298  func TestSIGCONT(t *testing.T) {
   299  	c := make(chan os.Signal, 1)
   300  	Notify(c, syscall.SIGCONT)
   301  	defer Stop(c)
   302  	syscall.Kill(syscall.Getpid(), syscall.SIGCONT)
   303  	waitSig(t, c, syscall.SIGCONT)
   304  }