1
0
mirror of https://github.com/golang/go synced 2024-11-26 02:17:58 -07:00

os/signal: fix flaky tests for NotifyContext.

Test failures started to happen sporadically on some builds after the introduction of NotifyContext.
To make these tests more robust and avoid the risk of crosstalk we run them in a separate process.

Fixes #41561.

Change-Id: Ia7af105c316afd11765358f1e5e253ccfe2adc2b
Reviewed-on: https://go-review.googlesource.com/c/go/+/270198
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Bryan C. Mills <bcmills@google.com>
Trust: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Henrique Vicente 2020-11-16 03:09:31 +01:00 committed by Ian Lance Taylor
parent 740851baca
commit ae652a4ac9

View File

@ -675,22 +675,68 @@ func TestTime(t *testing.T) {
<-done
}
func TestNotifyContext(t *testing.T) {
c, stop := NotifyContext(context.Background(), syscall.SIGINT)
defer stop()
var (
checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.")
ctxNotifyTimes = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received")
)
if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
t.Errorf("c.String() = %q, want %q", got, want)
func TestNotifyContextNotifications(t *testing.T) {
if *checkNotifyContext {
ctx, _ := NotifyContext(context.Background(), syscall.SIGINT)
// We want to make sure not to be calling Stop() internally on NotifyContext() when processing a received signal.
// Being able to wait for a number of received system signals allows us to do so.
var wg sync.WaitGroup
n := *ctxNotifyTimes
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
wg.Done()
}()
}
wg.Wait()
<-ctx.Done()
fmt.Print("received SIGINT")
// Sleep to give time to simultaneous signals to reach the process.
// These signals must be ignored given stop() is not called on this code.
// We want to guarantee a SIGINT doesn't cause a premature termination of the program.
time.Sleep(settleTime)
return
}
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
select {
case <-c.Done():
if got := c.Err(); got != context.Canceled {
t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
}
case <-time.After(time.Second):
t.Errorf("timed out waiting for context to be done after SIGINT")
t.Parallel()
testCases := []struct {
name string
n int // number of times a SIGINT should be notified.
}{
{"once", 1},
{"multiple", 10},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var subTimeout time.Duration
if deadline, ok := t.Deadline(); ok {
subTimeout := time.Until(deadline)
subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess.
}
args := []string{
"-test.v",
"-test.run=TestNotifyContextNotifications$",
"-check_notify_ctx",
fmt.Sprintf("-ctx_notify_times=%d", tc.n),
}
if subTimeout != 0 {
args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
}
out, err := exec.Command(os.Args[0], args...).CombinedOutput()
if err != nil {
t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out)
}
if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
t.Errorf("got %q, wanted %q", out, want)
}
})
}
}
@ -768,34 +814,6 @@ func TestNotifyContextPrematureCancelParent(t *testing.T) {
}
}
func TestNotifyContextSimultaneousNotifications(t *testing.T) {
c, stop := NotifyContext(context.Background(), syscall.SIGINT)
defer stop()
if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
t.Errorf("c.String() = %q, want %q", got, want)
}
var wg sync.WaitGroup
n := 10
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
wg.Done()
}()
}
wg.Wait()
select {
case <-c.Done():
if got := c.Err(); got != context.Canceled {
t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
}
case <-time.After(time.Second):
t.Errorf("expected context to be canceled")
}
}
func TestNotifyContextSimultaneousStop(t *testing.T) {
c, stop := NotifyContext(context.Background(), syscall.SIGINT)
defer stop()