github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/signal_windows_test.go (about) 1 // Copyright 2019 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 package runtime_test 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "internal/testenv" 12 "os/exec" 13 "path/filepath" 14 "runtime" 15 "strings" 16 "syscall" 17 "testing" 18 ) 19 20 func TestVectoredHandlerExceptionInNonGoThread(t *testing.T) { 21 if *flagQuick { 22 t.Skip("-quick") 23 } 24 if strings.HasPrefix(testenv.Builder(), "windows-amd64-2012") { 25 testenv.SkipFlaky(t, 49681) 26 } 27 testenv.MustHaveGoBuild(t) 28 testenv.MustHaveCGO(t) 29 testenv.MustHaveExecPath(t, "gcc") 30 testprog.Lock() 31 defer testprog.Unlock() 32 dir := t.TempDir() 33 34 // build c program 35 dll := filepath.Join(dir, "veh.dll") 36 cmd := exec.Command("gcc", "-shared", "-o", dll, "testdata/testwinlibthrow/veh.c") 37 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 38 if err != nil { 39 t.Fatalf("failed to build c exe: %s\n%s", err, out) 40 } 41 42 // build go exe 43 exe := filepath.Join(dir, "test.exe") 44 cmd = exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinlibthrow/main.go") 45 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 46 if err != nil { 47 t.Fatalf("failed to build go library: %s\n%s", err, out) 48 } 49 50 // run test program in same thread 51 cmd = exec.Command(exe) 52 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 53 if err == nil { 54 t.Fatal("error expected") 55 } 56 if _, ok := err.(*exec.ExitError); ok && len(out) > 0 { 57 if !bytes.Contains(out, []byte("Exception 0x2a")) { 58 t.Fatalf("unexpected failure while running executable: %s\n%s", err, out) 59 } 60 } else { 61 t.Fatalf("unexpected error while running executable: %s\n%s", err, out) 62 } 63 // run test program in a new thread 64 cmd = exec.Command(exe, "thread") 65 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 66 if err == nil { 67 t.Fatal("error expected") 68 } 69 if err, ok := err.(*exec.ExitError); ok { 70 if err.ExitCode() != 42 { 71 t.Fatalf("unexpected failure while running executable: %s\n%s", err, out) 72 } 73 } else { 74 t.Fatalf("unexpected error while running executable: %s\n%s", err, out) 75 } 76 } 77 78 func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) { 79 if *flagQuick { 80 t.Skip("-quick") 81 } 82 if runtime.GOARCH == "arm" { 83 //TODO: remove this skip and update testwinlib/main.c 84 // once windows/arm supports c-shared buildmode. 85 // See go.dev/issues/43800. 86 t.Skip("this test can't run on windows/arm") 87 } 88 testenv.MustHaveGoBuild(t) 89 testenv.MustHaveCGO(t) 90 testenv.MustHaveExecPath(t, "gcc") 91 testprog.Lock() 92 defer testprog.Unlock() 93 dir := t.TempDir() 94 95 // build go dll 96 dll := filepath.Join(dir, "testwinlib.dll") 97 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", "testdata/testwinlib/main.go") 98 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 99 if err != nil { 100 t.Fatalf("failed to build go library: %s\n%s", err, out) 101 } 102 103 // build c program 104 exe := filepath.Join(dir, "test.exe") 105 cmd = exec.Command("gcc", "-L"+dir, "-I"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c") 106 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 107 if err != nil { 108 t.Fatalf("failed to build c exe: %s\n%s", err, out) 109 } 110 111 // run test program 112 cmd = exec.Command(exe) 113 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 114 if err != nil { 115 t.Fatalf("failure while running executable: %s\n%s", err, out) 116 } 117 expectedOutput := "exceptionCount: 1\ncontinueCount: 1\n" 118 // cleaning output 119 cleanedOut := strings.ReplaceAll(string(out), "\r\n", "\n") 120 if cleanedOut != expectedOutput { 121 t.Errorf("expected output %q, got %q", expectedOutput, cleanedOut) 122 } 123 } 124 125 func sendCtrlBreak(pid int) error { 126 kernel32, err := syscall.LoadDLL("kernel32.dll") 127 if err != nil { 128 return fmt.Errorf("LoadDLL: %v\n", err) 129 } 130 generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent") 131 if err != nil { 132 return fmt.Errorf("FindProc: %v\n", err) 133 } 134 result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) 135 if result == 0 { 136 return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err) 137 } 138 return nil 139 } 140 141 // TestCtrlHandler tests that Go can gracefully handle closing the console window. 142 // See https://golang.org/issues/41884. 143 func TestCtrlHandler(t *testing.T) { 144 testenv.MustHaveGoBuild(t) 145 t.Parallel() 146 147 // build go program 148 exe := filepath.Join(t.TempDir(), "test.exe") 149 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "testdata/testwinsignal/main.go") 150 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 151 if err != nil { 152 t.Fatalf("failed to build go exe: %v\n%s", err, out) 153 } 154 155 // run test program 156 cmd = exec.Command(exe) 157 var stdout strings.Builder 158 var stderr strings.Builder 159 cmd.Stdout = &stdout 160 cmd.Stderr = &stderr 161 inPipe, err := cmd.StdinPipe() 162 if err != nil { 163 t.Fatalf("Failed to create stdin pipe: %v", err) 164 } 165 // keep inPipe alive until the end of the test 166 defer inPipe.Close() 167 168 // in a new command window 169 const _CREATE_NEW_CONSOLE = 0x00000010 170 cmd.SysProcAttr = &syscall.SysProcAttr{ 171 CreationFlags: _CREATE_NEW_CONSOLE, 172 HideWindow: true, 173 } 174 if err := cmd.Start(); err != nil { 175 t.Fatalf("Start failed: %v", err) 176 } 177 defer func() { 178 cmd.Process.Kill() 179 cmd.Wait() 180 }() 181 182 // check child exited gracefully, did not timeout 183 if err := cmd.Wait(); err != nil { 184 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 185 } 186 187 // check child received, handled SIGTERM 188 if expected, got := syscall.SIGTERM.String(), strings.TrimSpace(stdout.String()); expected != got { 189 t.Fatalf("Expected '%s' got: %s", expected, got) 190 } 191 } 192 193 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events. 194 // See https://golang.org/issues/35965. 195 func TestLibraryCtrlHandler(t *testing.T) { 196 if *flagQuick { 197 t.Skip("-quick") 198 } 199 if runtime.GOARCH != "amd64" { 200 t.Skip("this test can only run on windows/amd64") 201 } 202 testenv.MustHaveGoBuild(t) 203 testenv.MustHaveCGO(t) 204 testenv.MustHaveExecPath(t, "gcc") 205 testprog.Lock() 206 defer testprog.Unlock() 207 dir := t.TempDir() 208 209 // build go dll 210 dll := filepath.Join(dir, "dummy.dll") 211 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "-buildmode", "c-shared", "testdata/testwinlibsignal/dummy.go") 212 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 213 if err != nil { 214 t.Fatalf("failed to build go library: %s\n%s", err, out) 215 } 216 217 // build c program 218 exe := filepath.Join(dir, "test.exe") 219 cmd = exec.Command("gcc", "-o", exe, "testdata/testwinlibsignal/main.c") 220 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 221 if err != nil { 222 t.Fatalf("failed to build c exe: %s\n%s", err, out) 223 } 224 225 // run test program 226 cmd = exec.Command(exe) 227 var stderr bytes.Buffer 228 cmd.Stderr = &stderr 229 outPipe, err := cmd.StdoutPipe() 230 if err != nil { 231 t.Fatalf("Failed to create stdout pipe: %v", err) 232 } 233 outReader := bufio.NewReader(outPipe) 234 235 cmd.SysProcAttr = &syscall.SysProcAttr{ 236 CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 237 } 238 if err := cmd.Start(); err != nil { 239 t.Fatalf("Start failed: %v", err) 240 } 241 242 errCh := make(chan error, 1) 243 go func() { 244 if line, err := outReader.ReadString('\n'); err != nil { 245 errCh <- fmt.Errorf("could not read stdout: %v", err) 246 } else if strings.TrimSpace(line) != "ready" { 247 errCh <- fmt.Errorf("unexpected message: %v", line) 248 } else { 249 errCh <- sendCtrlBreak(cmd.Process.Pid) 250 } 251 }() 252 253 if err := <-errCh; err != nil { 254 t.Fatal(err) 255 } 256 if err := cmd.Wait(); err != nil { 257 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 258 } 259 } 260 261 func TestIssue59213(t *testing.T) { 262 if runtime.GOOS != "windows" { 263 t.Skip("skipping windows only test") 264 } 265 if *flagQuick { 266 t.Skip("-quick") 267 } 268 testenv.MustHaveGoBuild(t) 269 testenv.MustHaveCGO(t) 270 271 goEnv := func(arg string) string { 272 cmd := testenv.Command(t, testenv.GoToolPath(t), "env", arg) 273 cmd.Stderr = new(bytes.Buffer) 274 275 line, err := cmd.Output() 276 if err != nil { 277 t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr) 278 } 279 out := string(bytes.TrimSpace(line)) 280 t.Logf("%v: %q", cmd, out) 281 return out 282 } 283 284 cc := goEnv("CC") 285 cgoCflags := goEnv("CGO_CFLAGS") 286 287 t.Parallel() 288 289 tmpdir := t.TempDir() 290 dllfile := filepath.Join(tmpdir, "test.dll") 291 exefile := filepath.Join(tmpdir, "gotest.exe") 292 293 // build go dll 294 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", dllfile, "-buildmode", "c-shared", "testdata/testwintls/main.go") 295 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 296 if err != nil { 297 t.Fatalf("failed to build go library: %s\n%s", err, out) 298 } 299 300 // build c program 301 cmd = testenv.Command(t, cc, "-o", exefile, "testdata/testwintls/main.c") 302 testenv.CleanCmdEnv(cmd) 303 cmd.Env = append(cmd.Env, "CGO_CFLAGS="+cgoCflags) 304 out, err = cmd.CombinedOutput() 305 if err != nil { 306 t.Fatalf("failed to build c exe: %s\n%s", err, out) 307 } 308 309 // run test program 310 cmd = testenv.Command(t, exefile, dllfile, "GoFunc") 311 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 312 if err != nil { 313 t.Fatalf("failed: %s\n%s", err, out) 314 } 315 }