github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/runtime/signal_windows_test.go (about) 1 // +build windows 2 3 package runtime_test 4 5 import ( 6 "bufio" 7 "bytes" 8 "fmt" 9 "internal/testenv" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "syscall" 16 "testing" 17 ) 18 19 func TestVectoredHandlerDontCrashOnLibrary(t *testing.T) { 20 if *flagQuick { 21 t.Skip("-quick") 22 } 23 if runtime.GOARCH != "amd64" { 24 t.Skip("this test can only run on windows/amd64") 25 } 26 testenv.MustHaveGoBuild(t) 27 testenv.MustHaveExecPath(t, "gcc") 28 testprog.Lock() 29 defer testprog.Unlock() 30 dir, err := os.MkdirTemp("", "go-build") 31 if err != nil { 32 t.Fatalf("failed to create temp directory: %v", err) 33 } 34 defer os.RemoveAll(dir) 35 36 // build go dll 37 dll := filepath.Join(dir, "testwinlib.dll") 38 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "--buildmode", "c-shared", "testdata/testwinlib/main.go") 39 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 40 if err != nil { 41 t.Fatalf("failed to build go library: %s\n%s", err, out) 42 } 43 44 // build c program 45 exe := filepath.Join(dir, "test.exe") 46 cmd = exec.Command("gcc", "-L"+dir, "-I"+dir, "-ltestwinlib", "-o", exe, "testdata/testwinlib/main.c") 47 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 48 if err != nil { 49 t.Fatalf("failed to build c exe: %s\n%s", err, out) 50 } 51 52 // run test program 53 cmd = exec.Command(exe) 54 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 55 if err != nil { 56 t.Fatalf("failure while running executable: %s\n%s", err, out) 57 } 58 expectedOutput := "exceptionCount: 1\ncontinueCount: 1\n" 59 // cleaning output 60 cleanedOut := strings.ReplaceAll(string(out), "\r\n", "\n") 61 if cleanedOut != expectedOutput { 62 t.Errorf("expected output %q, got %q", expectedOutput, cleanedOut) 63 } 64 } 65 66 func sendCtrlBreak(pid int) error { 67 kernel32, err := syscall.LoadDLL("kernel32.dll") 68 if err != nil { 69 return fmt.Errorf("LoadDLL: %v\n", err) 70 } 71 generateEvent, err := kernel32.FindProc("GenerateConsoleCtrlEvent") 72 if err != nil { 73 return fmt.Errorf("FindProc: %v\n", err) 74 } 75 result, _, err := generateEvent.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) 76 if result == 0 { 77 return fmt.Errorf("GenerateConsoleCtrlEvent: %v\n", err) 78 } 79 return nil 80 } 81 82 // TestLibraryCtrlHandler tests that Go DLL allows calling program to handle console control events. 83 // See https://golang.org/issues/35965. 84 func TestLibraryCtrlHandler(t *testing.T) { 85 if *flagQuick { 86 t.Skip("-quick") 87 } 88 if runtime.GOARCH != "amd64" { 89 t.Skip("this test can only run on windows/amd64") 90 } 91 testenv.MustHaveGoBuild(t) 92 testenv.MustHaveExecPath(t, "gcc") 93 testprog.Lock() 94 defer testprog.Unlock() 95 dir, err := os.MkdirTemp("", "go-build") 96 if err != nil { 97 t.Fatalf("failed to create temp directory: %v", err) 98 } 99 defer os.RemoveAll(dir) 100 101 // build go dll 102 dll := filepath.Join(dir, "dummy.dll") 103 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", dll, "--buildmode", "c-shared", "testdata/testwinlibsignal/dummy.go") 104 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() 105 if err != nil { 106 t.Fatalf("failed to build go library: %s\n%s", err, out) 107 } 108 109 // build c program 110 exe := filepath.Join(dir, "test.exe") 111 cmd = exec.Command("gcc", "-o", exe, "testdata/testwinlibsignal/main.c") 112 out, err = testenv.CleanCmdEnv(cmd).CombinedOutput() 113 if err != nil { 114 t.Fatalf("failed to build c exe: %s\n%s", err, out) 115 } 116 117 // run test program 118 cmd = exec.Command(exe) 119 var stderr bytes.Buffer 120 cmd.Stderr = &stderr 121 outPipe, err := cmd.StdoutPipe() 122 if err != nil { 123 t.Fatalf("Failed to create stdout pipe: %v", err) 124 } 125 outReader := bufio.NewReader(outPipe) 126 127 cmd.SysProcAttr = &syscall.SysProcAttr{ 128 CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, 129 } 130 if err := cmd.Start(); err != nil { 131 t.Fatalf("Start failed: %v", err) 132 } 133 134 errCh := make(chan error, 1) 135 go func() { 136 if line, err := outReader.ReadString('\n'); err != nil { 137 errCh <- fmt.Errorf("could not read stdout: %v", err) 138 } else if strings.TrimSpace(line) != "ready" { 139 errCh <- fmt.Errorf("unexpected message: %v", line) 140 } else { 141 errCh <- sendCtrlBreak(cmd.Process.Pid) 142 } 143 }() 144 145 if err := <-errCh; err != nil { 146 t.Fatal(err) 147 } 148 if err := cmd.Wait(); err != nil { 149 t.Fatalf("Program exited with error: %v\n%s", err, &stderr) 150 } 151 }