Skip to content

Commit 320f02c

Browse files
committed
issue25965: add test
Updates golang/go#25965
1 parent eca66af commit 320f02c

File tree

3 files changed

+325
-1
lines changed

3 files changed

+325
-1
lines changed

Diff for: issue25965/main_test.go

+303
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
package main_test
2+
3+
import (
4+
"errors"
5+
"flag"
6+
"io/ioutil"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"strconv"
11+
"syscall"
12+
"testing"
13+
"time"
14+
"unsafe"
15+
16+
"github.com/alexbrainman/winapi"
17+
)
18+
19+
var sleepBetweenRuns = flag.Duration("sleep", 0, "sleep between exe runs (defaults to no sleep)")
20+
21+
func buildGoExe(t *testing.T, tmpdir string) string {
22+
src := filepath.Join(tmpdir, "a.go")
23+
err := ioutil.WriteFile(src, []byte(`package main; func main() {}`), 0644)
24+
if err != nil {
25+
t.Fatal(err)
26+
}
27+
exe := filepath.Join(tmpdir, "a_go.exe")
28+
cmd := exec.Command("go", "build", "-o", exe, src)
29+
out, err := cmd.CombinedOutput()
30+
if err != nil {
31+
t.Fatalf("building test executable failed: %s %s", err, out)
32+
}
33+
return exe
34+
}
35+
36+
func buildCExe(t *testing.T, tmpdir string) string {
37+
src := filepath.Join(tmpdir, "a.c")
38+
err := ioutil.WriteFile(src, []byte(csrc), 0644)
39+
if err != nil {
40+
t.Fatal(err)
41+
}
42+
exe := filepath.Join(tmpdir, "a_c.exe")
43+
cmd := exec.Command("gcc", "-o", exe, src)
44+
out, err := cmd.CombinedOutput()
45+
if err != nil {
46+
t.Fatalf("building test executable failed: %s %s", err, out)
47+
}
48+
return exe
49+
}
50+
51+
func dupStdHandle(stdh int) (syscall.Handle, error) {
52+
h, _ := syscall.GetStdHandle(stdh)
53+
p, _ := syscall.GetCurrentProcess()
54+
var dup syscall.Handle
55+
err := syscall.DuplicateHandle(p, h, p, &dup, 0, true, syscall.DUPLICATE_SAME_ACCESS)
56+
if err != nil {
57+
return 0, os.NewSyscallError("DuplicateHandle", err)
58+
}
59+
return dup, nil
60+
}
61+
62+
func runExe(argv0 string) error {
63+
argv0p, err := syscall.UTF16PtrFromString(argv0)
64+
if err != nil {
65+
return err
66+
}
67+
68+
si := new(syscall.StartupInfo)
69+
si.Cb = uint32(unsafe.Sizeof(*si))
70+
si.Flags = syscall.STARTF_USESTDHANDLES
71+
si.StdInput, err = dupStdHandle(syscall.STD_INPUT_HANDLE)
72+
if err != nil {
73+
return err
74+
}
75+
si.StdOutput, err = dupStdHandle(syscall.STD_OUTPUT_HANDLE)
76+
if err != nil {
77+
return err
78+
}
79+
si.StdErr, err = dupStdHandle(syscall.STD_ERROR_HANDLE)
80+
if err != nil {
81+
return err
82+
}
83+
84+
pi := new(syscall.ProcessInformation)
85+
86+
flags := uint32(syscall.CREATE_UNICODE_ENVIRONMENT)
87+
env := []uint16{0, 0}
88+
err = syscall.CreateProcess(argv0p, nil, nil, nil, true, flags, &env[0], nil, si, pi)
89+
if err != nil {
90+
return os.NewSyscallError("CreateProcess", err)
91+
}
92+
93+
syscall.CloseHandle(pi.Thread)
94+
syscall.CloseHandle(si.StdErr)
95+
syscall.CloseHandle(si.StdOutput)
96+
syscall.CloseHandle(si.StdInput)
97+
98+
h := pi.Process
99+
100+
s, err := syscall.WaitForSingleObject(h, syscall.INFINITE)
101+
switch s {
102+
case syscall.WAIT_OBJECT_0:
103+
break
104+
case syscall.WAIT_FAILED:
105+
return os.NewSyscallError("WaitForSingleObject", err)
106+
default:
107+
return errors.New("Unexpected result from WaitForSingleObject")
108+
}
109+
110+
var ec uint32
111+
err = syscall.GetExitCodeProcess(h, &ec)
112+
if err != nil {
113+
return os.NewSyscallError("GetExitCodeProcess", err)
114+
}
115+
116+
if *sleepBetweenRuns != 0 {
117+
time.Sleep(*sleepBetweenRuns)
118+
}
119+
120+
err = syscall.CloseHandle(h)
121+
if err != nil {
122+
return os.NewSyscallError("CloseHandle", err)
123+
}
124+
return nil
125+
}
126+
127+
func deleteFile(name string) error {
128+
p, err := syscall.UTF16PtrFromString(name)
129+
if err != nil {
130+
return err
131+
}
132+
err = syscall.DeleteFile(p)
133+
if err != nil {
134+
return os.NewSyscallError("DeleteFile", err)
135+
}
136+
return nil
137+
}
138+
139+
func testGoExe(t *testing.T, dstexe, srcexe string) {
140+
for i := 0; i < 100; i++ {
141+
err := winapi.CopyFile(syscall.StringToUTF16Ptr(srcexe), syscall.StringToUTF16Ptr(dstexe), true)
142+
if err != nil {
143+
t.Errorf("iteration %d failed: %v", i, os.NewSyscallError("CopyFile", err))
144+
return
145+
}
146+
147+
err = runExe(dstexe)
148+
if err != nil {
149+
t.Errorf("iteration %d failed: %v", i, err)
150+
return
151+
}
152+
153+
err = deleteFile(dstexe)
154+
if err != nil {
155+
t.Errorf("iteration %d failed: %v", i, err)
156+
return
157+
}
158+
}
159+
}
160+
161+
func testCExe(t *testing.T, cexe, dstexe, srcexe string) {
162+
ms := strconv.FormatInt(sleepBetweenRuns.Milliseconds(), 10)
163+
cmd := exec.Command(cexe, dstexe, srcexe, ms)
164+
out, err := cmd.CombinedOutput()
165+
if err != nil {
166+
t.Fatalf("running c exe failed: %s %s", err, out)
167+
}
168+
}
169+
170+
// Test for https://golang.org/issue/25965
171+
func TestIssue25965(t *testing.T) {
172+
tmpdir, err := ioutil.TempDir("", "TestIssue25965")
173+
if err != nil {
174+
t.Fatal(err)
175+
}
176+
defer os.RemoveAll(tmpdir)
177+
178+
exe := buildGoExe(t, tmpdir)
179+
dstexe := filepath.Join(tmpdir, "a2.exe")
180+
testGoExe(t, dstexe, exe)
181+
182+
cexe := buildCExe(t, tmpdir)
183+
testCExe(t, cexe, dstexe, exe)
184+
}
185+
186+
var csrc = `
187+
#include <windows.h>
188+
#include <tchar.h>
189+
#include <stdio.h>
190+
191+
HANDLE dupStdHandle(int stdh) {
192+
HANDLE h, p, dup;
193+
h = GetStdHandle(stdh);
194+
p = GetCurrentProcess();
195+
if (!DuplicateHandle(p, h, p, &dup, 0, TRUE, DUPLICATE_SAME_ACCESS))
196+
{
197+
printf("DuplicateHandle failed (%d)\n", GetLastError());
198+
return INVALID_HANDLE_VALUE;
199+
}
200+
return dup;
201+
}
202+
203+
BOOL runExe(char* argv0, int ms) {
204+
STARTUPINFO si;
205+
PROCESS_INFORMATION pi;
206+
UINT16 env[2];
207+
HANDLE h;
208+
DWORD ec;
209+
210+
ZeroMemory(&si, sizeof(si));
211+
si.cb = sizeof(si);
212+
si.dwFlags = STARTF_USESTDHANDLES;
213+
if (!(si.hStdInput = dupStdHandle(STD_INPUT_HANDLE)))
214+
{
215+
return FALSE;
216+
}
217+
if (!(si.hStdOutput = dupStdHandle(STD_OUTPUT_HANDLE)))
218+
{
219+
return FALSE;
220+
}
221+
if (!(si.hStdError = dupStdHandle(STD_ERROR_HANDLE)))
222+
{
223+
return FALSE;
224+
}
225+
226+
ZeroMemory(&pi, sizeof(pi));
227+
228+
ZeroMemory(&env, sizeof(env));
229+
230+
if (!CreateProcess(argv0, NULL, NULL, NULL, TRUE, CREATE_UNICODE_ENVIRONMENT, &env[0], NULL, &si, &pi)) {
231+
printf("CreateProcess failed (%d)\n", GetLastError());
232+
return FALSE;
233+
}
234+
235+
CloseHandle(pi.hThread);
236+
CloseHandle(si.hStdError);
237+
CloseHandle(si.hStdOutput);
238+
CloseHandle(si.hStdInput);
239+
240+
h = pi.hProcess;
241+
242+
if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) {
243+
printf("WaitForSingleObject failed (%d)\n", GetLastError());
244+
return FALSE;
245+
}
246+
247+
if (!GetExitCodeProcess(h, &ec)) {
248+
printf("GetExitCodeProcess failed (%d)\n", GetLastError());
249+
return FALSE;
250+
}
251+
252+
if (ms > 0) {
253+
Sleep(ms);
254+
}
255+
256+
if (!CloseHandle(h)) {
257+
printf("CloseHandle failed (%d)\n", GetLastError());
258+
return FALSE;
259+
}
260+
261+
return TRUE;
262+
}
263+
264+
BOOL testGoExe(char* dstexe, char* srcexe, int ms) {
265+
int i;
266+
267+
for (i = 0; i < 100; i++) {
268+
if (!CopyFile(srcexe, dstexe, FALSE))
269+
{
270+
printf("iteration %d: CopyFile failed (%d)\n", i, GetLastError());
271+
return FALSE;
272+
}
273+
274+
if (!runExe(dstexe, ms))
275+
{
276+
printf("during iteration %d\n", i);
277+
return FALSE;
278+
}
279+
280+
if (!DeleteFile(dstexe))
281+
{
282+
printf("iteration %d: DeleteFile failed (%d)\n", i, GetLastError());
283+
return FALSE;
284+
}
285+
}
286+
return TRUE;
287+
}
288+
289+
int main(int argc, char** argv)
290+
{
291+
char *dstexe = argv[1];
292+
char *srcexe = argv[2];
293+
int ms = atoi(argv[3]);
294+
if (testGoExe(dstexe, srcexe, ms))
295+
{
296+
return 0;
297+
}
298+
else
299+
{
300+
return 1;
301+
}
302+
}
303+
`

Diff for: winapi.go

+2
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ type OSVERSIONINFOEX struct {
3636
}
3737

3838
//sys GetVersionEx(versioninfo *OSVERSIONINFOEX) (err error) = kernel32.GetVersionExW
39+
40+
//sys CopyFile(existingName *uint16, newName *uint16, failIfExists bool) (err error) = kernel32.CopyFileW

Diff for: zsyscall_windows.go

+20-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)