]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: allow TestCtrlHandler to run in ConPTY
authorNuno Cruces <ncruces@users.noreply.github.com>
Thu, 17 Mar 2022 16:38:00 +0000 (16:38 +0000)
committerAlex Brainman <alex.brainman@gmail.com>
Fri, 18 Mar 2022 07:37:53 +0000 (07:37 +0000)
Fixes #51602. Previous test would not run in a pseudo-console (ConPTY).

New test avoids taskkill entirely by having the child request its own
console window be closed.

Verified that this runs locally (within a real console), over SSH
(within a pseudo-console), and that it breaks if #41884 were reverted.

Change-Id: If868b92ec36647e5d0e4107e29a2a6e048d35ced
GitHub-Last-Rev: b1421e4bed2dc729c266928f002b39374d7e391a
GitHub-Pull-Request: golang/go#51681
Reviewed-on: https://go-review.googlesource.com/c/go/+/392874
Reviewed-by: Bryan Mills <bcmills@google.com>
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Trust: Alex Brainman <alex.brainman@gmail.com>

src/runtime/signal_windows_test.go
src/runtime/testdata/testwinsignal/main.go

index ebe94797fb4503cff1596645350893fdea18dd63..add23cd2926996a03e7d559eac87dca788352c81 100644 (file)
@@ -10,7 +10,6 @@ import (
        "os/exec"
        "path/filepath"
        "runtime"
-       "strconv"
        "strings"
        "syscall"
        "testing"
@@ -92,13 +91,16 @@ func TestCtrlHandler(t *testing.T) {
 
        // run test program
        cmd = exec.Command(exe)
+       var stdout bytes.Buffer
        var stderr bytes.Buffer
+       cmd.Stdout = &stdout
        cmd.Stderr = &stderr
-       outPipe, err := cmd.StdoutPipe()
+       inPipe, err := cmd.StdinPipe()
        if err != nil {
-               t.Fatalf("Failed to create stdout pipe: %v", err)
+               t.Fatalf("Failed to create stdin pipe: %v", err)
        }
-       outReader := bufio.NewReader(outPipe)
+       // keep inPipe alive until the end of the test
+       defer inPipe.Close()
 
        // in a new command window
        const _CREATE_NEW_CONSOLE = 0x00000010
@@ -114,29 +116,15 @@ func TestCtrlHandler(t *testing.T) {
                cmd.Wait()
        }()
 
-       // wait for child to be ready to receive signals
-       if line, err := outReader.ReadString('\n'); err != nil {
-               t.Fatalf("could not read stdout: %v", err)
-       } else if strings.TrimSpace(line) != "ready" {
-               t.Fatalf("unexpected message: %s", line)
-       }
-
-       // gracefully kill pid, this closes the command window
-       if err := exec.Command("taskkill.exe", "/pid", strconv.Itoa(cmd.Process.Pid)).Run(); err != nil {
-               t.Fatalf("failed to kill: %v", err)
+       // check child exited gracefully, did not timeout
+       if err := cmd.Wait(); err != nil {
+               t.Fatalf("Program exited with error: %v\n%s", err, &stderr)
        }
 
        // check child received, handled SIGTERM
-       if line, err := outReader.ReadString('\n'); err != nil {
-               t.Fatalf("could not read stdout: %v", err)
-       } else if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(line); expected != got {
+       if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(stdout.String()); expected != got {
                t.Fatalf("Expected '%s' got: %s", expected, got)
        }
-
-       // check child exited gracefully, did not timeout
-       if err := cmd.Wait(); err != nil {
-               t.Fatalf("Program exited with error: %v\n%s", err, &stderr)
-       }
 }
 
 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events.
index 1e7c9475fd663153b941f1075ed8828feb791fb7..e1136f3887929b84ea172578e9018b3df63e082f 100644 (file)
@@ -2,18 +2,52 @@ package main
 
 import (
        "fmt"
+       "io"
+       "log"
        "os"
        "os/signal"
+       "syscall"
        "time"
 )
 
 func main() {
+       // Ensure that this process terminates when the test times out,
+       // even if the expected signal never arrives.
+       go func() {
+               io.Copy(io.Discard, os.Stdin)
+               log.Fatal("stdin is closed; terminating")
+       }()
+
+       // Register to receive all signals.
        c := make(chan os.Signal, 1)
        signal.Notify(c)
 
-       fmt.Println("ready")
+       // Get console window handle.
+       kernel32 := syscall.NewLazyDLL("kernel32.dll")
+       getConsoleWindow := kernel32.NewProc("GetConsoleWindow")
+       hwnd, _, err := getConsoleWindow.Call()
+       if hwnd == 0 {
+               log.Fatal("no associated console: ", err)
+       }
+
+       // Send message to close the console window.
+       const _WM_CLOSE = 0x0010
+       user32 := syscall.NewLazyDLL("user32.dll")
+       postMessage := user32.NewProc("PostMessageW")
+       ok, _, err := postMessage.Call(hwnd, _WM_CLOSE, 0, 0)
+       if ok == 0 {
+               log.Fatal("post message failed: ", err)
+       }
+
        sig := <-c
 
+       // Allow some time for the handler to complete if it's going to.
+       //
+       // (In https://go.dev/issue/41884 the handler returned immediately,
+       // which caused Windows to terminate the program before the goroutine
+       // that received the SIGTERM had a chance to actually clean up.)
        time.Sleep(time.Second)
+
+       // Print the signal's name: "terminated" makes the test succeed.
        fmt.Println(sig)
 }