os/signal: avoid flaky TestCtrlBreak by waiting for child stdout TestCtrlBreak relied on a fixed sleep to wait for a testing child program to be ready to receive the signal. This may cause flaky timeouts on slow machines. Instead, wait for the child to print a "ready" message like other signal tests, particularly runtime.TestLibraryCtrlHandler. Also fix a typo in the child program's file name to ensure it's easy to associate with the test. Fixes #80036 Change-Id: Ib3d8e6194538df95e76548771b401cf9b9de5674 Reviewed-on: https://go-review.googlesource.com/c/go/+/792020 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Reviewed-by: Hongxiang Jiang <hxjiang@golang.org> Auto-Submit: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/src/os/signal/signal_windows_test.go b/src/os/signal/signal_windows_test.go index 145a805..d39f6c1 100644 --- a/src/os/signal/signal_windows_test.go +++ b/src/os/signal/signal_windows_test.go
@@ -5,28 +5,30 @@ package signal import ( + "bufio" + "fmt" "internal/testenv" "os" "path/filepath" "strings" "syscall" "testing" - "time" ) -func sendCtrlBreak(t *testing.T, pid int) { +func sendCtrlBreak(pid int) error { d, e := syscall.LoadDLL("kernel32.dll") if e != nil { - t.Fatalf("LoadDLL: %v\n", e) + return fmt.Errorf("LoadDLL: %v\n", e) } p, e := d.FindProc("GenerateConsoleCtrlEvent") if e != nil { - t.Fatalf("FindProc: %v\n", e) + return fmt.Errorf("FindProc: %v\n", e) } r, _, e := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) if r == 0 { - t.Fatalf("GenerateConsoleCtrlEvent: %v\n", e) + return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", e) } + return nil } func TestCtrlBreak(t *testing.T) { @@ -35,6 +37,7 @@ package main import ( + "fmt" "log" "os" "os/signal" @@ -45,6 +48,7 @@ func main() { c := make(chan os.Signal, 10) signal.Notify(c) + fmt.Println("ready") select { case s := <-c: if s != os.Interrupt { @@ -58,7 +62,7 @@ tmp := t.TempDir() // write ctrlbreak.go - name := filepath.Join(tmp, "ctlbreak") + name := filepath.Join(tmp, "ctrlbreak") src := name + ".go" f, err := os.Create(src) if err != nil { @@ -78,8 +82,11 @@ // run it cmd := testenv.Command(t, exe) var buf strings.Builder - cmd.Stdout = &buf cmd.Stderr = &buf + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatalf("StdoutPipe failed: %v", err) + } cmd.SysProcAttr = &syscall.SysProcAttr{ CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, } @@ -87,10 +94,22 @@ if err != nil { t.Fatalf("Start failed: %v", err) } + + outReader := bufio.NewReader(stdout) + errCh := make(chan error, 1) go func() { - time.Sleep(1 * time.Second) - sendCtrlBreak(t, cmd.Process.Pid) + if line, err := outReader.ReadString('\n'); err != nil { + errCh <- fmt.Errorf("could not read stdout: %v", err) + } else if strings.TrimSpace(line) != "ready" { + errCh <- fmt.Errorf("unexpected message: %v", line) + } else { + errCh <- sendCtrlBreak(cmd.Process.Pid) + } }() + + if err := <-errCh; err != nil { + t.Fatal(err) + } err = cmd.Wait() if err != nil { t.Fatalf("Program exited with error: %v\n%v", err, buf.String())