19 func TestVectoredHandlerDontCrashOnLibrary(t
*testing
.T
) {
23 if runtime
.GOARCH
!= "amd64" {
24 t
.Skip("this test can only run on windows/amd64")
26 testenv
.MustHaveGoBuild(t
)
27 testenv
.MustHaveExecPath(t
, "gcc")
29 defer testprog
.Unlock()
33 dll
:= filepath
.Join(dir
, "testwinlib.dll")
34 cmd
:= exec
.Command(testenv
.GoToolPath(t
), "build", "-o", dll
, "-buildmode", "c-shared", "testdata/testwinlib/main.go")
35 out
, err
:= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
37 t
.Fatalf("failed to build go library: %s\n%s", err
, out
)
41 exe
:= filepath
.Join(dir
, "test.exe")
42 cmd
= exec
.Command("gcc", "-L"+dir
, "-I"+dir
, "-ltestwinlib", "-o", exe
, "testdata/testwinlib/main.c")
43 out
, err
= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
45 t
.Fatalf("failed to build c exe: %s\n%s", err
, out
)
49 cmd
= exec
.Command(exe
)
50 out
, err
= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
52 t
.Fatalf("failure while running executable: %s\n%s", err
, out
)
54 expectedOutput
:= "exceptionCount: 1\ncontinueCount: 1\n"
56 cleanedOut
:= strings
.ReplaceAll(string(out
), "\r\n", "\n")
57 if cleanedOut
!= expectedOutput
{
58 t
.Errorf("expected output %q, got %q", expectedOutput
, cleanedOut
)
62 func sendCtrlBreak(pid
int) error
{
63 kernel32
, err
:= syscall
.LoadDLL("kernel32.dll")
65 return fmt
.Errorf("LoadDLL: %v\n", err
)
67 generateEvent
, err
:= kernel32
.FindProc("GenerateConsoleCtrlEvent")
69 return fmt
.Errorf("FindProc: %v\n", err
)
71 result
, _
, err
:= generateEvent
.Call(syscall
.CTRL_BREAK_EVENT
, uintptr(pid
))
73 return fmt
.Errorf("GenerateConsoleCtrlEvent: %v\n", err
)
78 // TestCtrlHandler tests that Go can gracefully handle closing the console window.
79 // See https://golang.org/issues/41884.
80 func TestCtrlHandler(t
*testing
.T
) {
81 testenv
.MustHaveGoBuild(t
)
85 exe
:= filepath
.Join(t
.TempDir(), "test.exe")
86 cmd
:= exec
.Command(testenv
.GoToolPath(t
), "build", "-o", exe
, "testdata/testwinsignal/main.go")
87 out
, err
:= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
89 t
.Fatalf("failed to build go exe: %v\n%s", err
, out
)
93 cmd
= exec
.Command(exe
)
94 var stderr bytes
.Buffer
96 outPipe
, err
:= cmd
.StdoutPipe()
98 t
.Fatalf("Failed to create stdout pipe: %v", err
)
100 outReader
:= bufio
.NewReader(outPipe
)
102 // in a new command window
103 const _CREATE_NEW_CONSOLE
= 0x00000010
104 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
105 CreationFlags
: _CREATE_NEW_CONSOLE
,
108 if err
:= cmd
.Start(); err
!= nil {
109 t
.Fatalf("Start failed: %v", err
)
116 // wait for child to be ready to receive signals
117 if line
, err
:= outReader
.ReadString('\n'); err
!= nil {
118 t
.Fatalf("could not read stdout: %v", err
)
119 } else if strings
.TrimSpace(line
) != "ready" {
120 t
.Fatalf("unexpected message: %s", line
)
123 // gracefully kill pid, this closes the command window
124 if err
:= exec
.Command("taskkill.exe", "/pid", strconv
.Itoa(cmd
.Process
.Pid
)).Run(); err
!= nil {
125 t
.Fatalf("failed to kill: %v", err
)
128 // check child received, handled SIGTERM
129 if line
, err
:= outReader
.ReadString('\n'); err
!= nil {
130 t
.Fatalf("could not read stdout: %v", err
)
131 } else if expected
, got
:= syscall
.SIGTERM
.String(), strings
.TrimSpace(line
); expected
!= got
{
132 t
.Fatalf("Expected '%s' got: %s", expected
, got
)
135 // check child exited gracefully, did not timeout
136 if err
:= cmd
.Wait(); err
!= nil {
137 t
.Fatalf("Program exited with error: %v\n%s", err
, &stderr
)
141 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events.
142 // See https://golang.org/issues/35965.
143 func TestLibraryCtrlHandler(t
*testing
.T
) {
147 if runtime
.GOARCH
!= "amd64" {
148 t
.Skip("this test can only run on windows/amd64")
150 testenv
.MustHaveGoBuild(t
)
151 testenv
.MustHaveExecPath(t
, "gcc")
153 defer testprog
.Unlock()
157 dll
:= filepath
.Join(dir
, "dummy.dll")
158 cmd
:= exec
.Command(testenv
.GoToolPath(t
), "build", "-o", dll
, "-buildmode", "c-shared", "testdata/testwinlibsignal/dummy.go")
159 out
, err
:= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
161 t
.Fatalf("failed to build go library: %s\n%s", err
, out
)
165 exe
:= filepath
.Join(dir
, "test.exe")
166 cmd
= exec
.Command("gcc", "-o", exe
, "testdata/testwinlibsignal/main.c")
167 out
, err
= testenv
.CleanCmdEnv(cmd
).CombinedOutput()
169 t
.Fatalf("failed to build c exe: %s\n%s", err
, out
)
173 cmd
= exec
.Command(exe
)
174 var stderr bytes
.Buffer
176 outPipe
, err
:= cmd
.StdoutPipe()
178 t
.Fatalf("Failed to create stdout pipe: %v", err
)
180 outReader
:= bufio
.NewReader(outPipe
)
182 cmd
.SysProcAttr
= &syscall
.SysProcAttr
{
183 CreationFlags
: syscall
.CREATE_NEW_PROCESS_GROUP
,
185 if err
:= cmd
.Start(); err
!= nil {
186 t
.Fatalf("Start failed: %v", err
)
189 errCh
:= make(chan error
, 1)
191 if line
, err
:= outReader
.ReadString('\n'); err
!= nil {
192 errCh
<- fmt
.Errorf("could not read stdout: %v", err
)
193 } else if strings
.TrimSpace(line
) != "ready" {
194 errCh
<- fmt
.Errorf("unexpected message: %v", line
)
196 errCh
<- sendCtrlBreak(cmd
.Process
.Pid
)
200 if err
:= <-errCh
; err
!= nil {
203 if err
:= cmd
.Wait(); err
!= nil {
204 t
.Fatalf("Program exited with error: %v\n%s", err
, &stderr
)