Source file
src/os/exec/lp_windows_test.go
1
2
3
4
5
6
7
8 package exec_test
9
10 import (
11 "fmt"
12 "internal/testenv"
13 "io"
14 "os"
15 "os/exec"
16 "path/filepath"
17 "strconv"
18 "strings"
19 "testing"
20 )
21
22 func installExe(t *testing.T, dest, src string) {
23 fsrc, err := os.Open(src)
24 if err != nil {
25 t.Fatal("os.Open failed: ", err)
26 }
27 defer fsrc.Close()
28 fdest, err := os.Create(dest)
29 if err != nil {
30 t.Fatal("os.Create failed: ", err)
31 }
32 defer fdest.Close()
33 _, err = io.Copy(fdest, fsrc)
34 if err != nil {
35 t.Fatal("io.Copy failed: ", err)
36 }
37 }
38
39 func installBat(t *testing.T, dest string) {
40 f, err := os.Create(dest)
41 if err != nil {
42 t.Fatalf("failed to create batch file: %v", err)
43 }
44 defer f.Close()
45 fmt.Fprintf(f, "@echo %s\n", dest)
46 }
47
48 func installProg(t *testing.T, dest, srcExe string) {
49 err := os.MkdirAll(filepath.Dir(dest), 0700)
50 if err != nil {
51 t.Fatal("os.MkdirAll failed: ", err)
52 }
53 if strings.ToLower(filepath.Ext(dest)) == ".bat" {
54 installBat(t, dest)
55 return
56 }
57 installExe(t, dest, srcExe)
58 }
59
60 type lookPathTest struct {
61 rootDir string
62 PATH string
63 PATHEXT string
64 files []string
65 searchFor string
66 fails bool
67 }
68
69 func (test lookPathTest) runProg(t *testing.T, env []string, args ...string) (string, error) {
70 cmd := exec.Command(args[0], args[1:]...)
71 cmd.Env = env
72 cmd.Dir = test.rootDir
73 args[0] = filepath.Base(args[0])
74 cmdText := fmt.Sprintf("%q command", strings.Join(args, " "))
75 out, err := cmd.CombinedOutput()
76 if (err != nil) != test.fails {
77 if test.fails {
78 t.Fatalf("test=%+v: %s succeeded, but expected to fail", test, cmdText)
79 }
80 t.Fatalf("test=%+v: %s failed, but expected to succeed: %v - %v", test, cmdText, err, string(out))
81 }
82 if err != nil {
83 return "", fmt.Errorf("test=%+v: %s failed: %v - %v", test, cmdText, err, string(out))
84 }
85
86 p := string(out)
87
88 for len(p) > 0 && (p[len(p)-1] == '\n' || p[len(p)-1] == '\r') {
89 p = p[:len(p)-1]
90 }
91 if !filepath.IsAbs(p) {
92 return p, nil
93 }
94 if p[:len(test.rootDir)] != test.rootDir {
95 t.Fatalf("test=%+v: %s output is wrong: %q must have %q prefix", test, cmdText, p, test.rootDir)
96 }
97 return p[len(test.rootDir)+1:], nil
98 }
99
100 func updateEnv(env []string, name, value string) []string {
101 for i, e := range env {
102 if strings.HasPrefix(strings.ToUpper(e), name+"=") {
103 env[i] = name + "=" + value
104 return env
105 }
106 }
107 return append(env, name+"="+value)
108 }
109
110 func createEnv(dir, PATH, PATHEXT string) []string {
111 env := os.Environ()
112 env = updateEnv(env, "PATHEXT", PATHEXT)
113
114 dirs := filepath.SplitList(PATH)
115 for i := range dirs {
116 dirs[i] = filepath.Join(dir, dirs[i])
117 }
118 path := strings.Join(dirs, ";")
119 env = updateEnv(env, "PATH", os.Getenv("SystemRoot")+"/System32;"+path)
120 return env
121 }
122
123
124
125 func createFiles(t *testing.T, dir string, files []string, srcPath string) {
126 for _, f := range files {
127 installProg(t, filepath.Join(dir, f), srcPath)
128 }
129 }
130
131 func (test lookPathTest) run(t *testing.T, tmpdir, printpathExe string) {
132 test.rootDir = tmpdir
133 createFiles(t, test.rootDir, test.files, printpathExe)
134 env := createEnv(test.rootDir, test.PATH, test.PATHEXT)
135
136
137
138 should, errCmd := test.runProg(t, env, "cmd", "/c", test.searchFor)
139
140 env = append(env, "GO_WANT_HELPER_PROCESS=1")
141 have, errLP := test.runProg(t, env, os.Args[0], "-test.run=TestHelperProcess", "--", "lookpath", test.searchFor)
142
143 if errCmd == nil && errLP == nil {
144
145 if should != have {
146 t.Fatalf("test=%+v:\ncmd /c ran: %s\nlookpath found: %s", test, should, have)
147 }
148 return
149 }
150 if errCmd != nil && errLP != nil {
151
152 return
153 }
154 if errCmd != nil {
155 t.Fatal(errCmd)
156 }
157 if errLP != nil {
158 t.Fatal(errLP)
159 }
160 }
161
162 var lookPathTests = []lookPathTest{
163 {
164 PATHEXT: `.COM;.EXE;.BAT`,
165 PATH: `p1;p2`,
166 files: []string{`p1\a.exe`, `p2\a.exe`, `p2\a`},
167 searchFor: `a`,
168 },
169 {
170 PATHEXT: `.COM;.EXE;.BAT`,
171 PATH: `p1.dir;p2.dir`,
172 files: []string{`p1.dir\a`, `p2.dir\a.exe`},
173 searchFor: `a`,
174 },
175 {
176 PATHEXT: `.COM;.EXE;.BAT`,
177 PATH: `p1;p2`,
178 files: []string{`p1\a.exe`, `p2\a.exe`},
179 searchFor: `a.exe`,
180 },
181 {
182 PATHEXT: `.COM;.EXE;.BAT`,
183 PATH: `p1;p2`,
184 files: []string{`p1\a.exe`, `p2\b.exe`},
185 searchFor: `b`,
186 },
187 {
188 PATHEXT: `.COM;.EXE;.BAT`,
189 PATH: `p1;p2`,
190 files: []string{`p1\b`, `p2\a`},
191 searchFor: `a`,
192 fails: true,
193 },
194
195
196
197
198 {
199 PATHEXT: `.COM;.EXE;.BAT`,
200 PATH: `p1;p2`,
201 files: []string{`p1\a.exe`, `p2\a.exe`},
202 searchFor: `p2\a`,
203 },
204
205
206
207
208 {
209 PATHEXT: `.COM;.EXE;.BAT`,
210 PATH: `p1;p2`,
211 files: []string{`p1\b.exe`, `p2\a.exe`},
212 searchFor: `p2\b`,
213 fails: true,
214 },
215
216
217
218
219 {
220 PATHEXT: `.COM;.EXE;.BAT`,
221 PATH: `p1;p2`,
222 files: []string{`a`, `p1\a.exe`, `p2\a.exe`},
223 searchFor: `a`,
224 },
225
226
227
228
229 {
230 PATHEXT: `.COM;.EXE;.BAT`,
231 PATH: `p1;p2`,
232 files: []string{`p1\a.exe`, `p2\a.exe`},
233 searchFor: `a`,
234 },
235
236
237
238
239
240 {
241 PATHEXT: `.COM;.EXE;.BAT`,
242 PATH: `p1;p2`,
243 files: []string{`p1\a.exe`, `p2\a.exe`},
244 searchFor: `b`,
245 fails: true,
246 },
247
248
249
250 {
251 PATHEXT: `.COM;.EXE;.BAT`,
252 PATH: `p1;p2`,
253 files: []string{`p1\a.exe`, `p2\a.exe`},
254 searchFor: `a.exe`,
255 },
256 {
257 PATHEXT: `.COM;.EXE;.BAT`,
258 PATH: `p1;p2`,
259 files: []string{`p1\a.exe`, `p2\a.exe`},
260 searchFor: `a.com`,
261 fails: true,
262 },
263 {
264 PATHEXT: `.COM;.EXE;.BAT`,
265 PATH: `p1`,
266 files: []string{`p1\a.exe.exe`},
267 searchFor: `a.exe`,
268 },
269 {
270 PATHEXT: `.COM;.BAT`,
271 PATH: `p1;p2`,
272 files: []string{`p1\a.exe`, `p2\a.exe`},
273 searchFor: `a.exe`,
274 },
275
276
277
278
279
280
281 {
282 PATHEXT: `.COM;.EXE`,
283 PATH: `p1;p2`,
284 files: []string{`p1\a.bat`, `p2\a.exe`},
285 searchFor: `a`,
286 },
287 {
288 PATHEXT: `.COM;.EXE;.BAT`,
289 PATH: `p1;p2`,
290 files: []string{`p1\a.bat`, `p2\a.exe`},
291 searchFor: `a`,
292 },
293 {
294 PATHEXT: `.COM;.EXE;.BAT`,
295 PATH: `p1;p2`,
296 files: []string{`p1\a.bat`, `p1\a.exe`, `p2\a.bat`, `p2\a.exe`},
297 searchFor: `a`,
298 },
299 {
300 PATHEXT: `.COM`,
301 PATH: `p1;p2`,
302 files: []string{`p1\a.bat`, `p2\a.exe`},
303 searchFor: `a`,
304 fails: true,
305 },
306 }
307
308 func TestLookPath(t *testing.T) {
309 tmp := t.TempDir()
310 printpathExe := buildPrintPathExe(t, tmp)
311
312
313 for i, test := range lookPathTests {
314 t.Run(fmt.Sprint(i), func(t *testing.T) {
315 dir := filepath.Join(tmp, "d"+strconv.Itoa(i))
316 err := os.Mkdir(dir, 0700)
317 if err != nil {
318 t.Fatal("Mkdir failed: ", err)
319 }
320 test.run(t, dir, printpathExe)
321 })
322 }
323 }
324
325 type commandTest struct {
326 PATH string
327 files []string
328 dir string
329 arg0 string
330 want string
331 fails bool
332 }
333
334 func (test commandTest) isSuccess(rootDir, output string, err error) error {
335 if err != nil {
336 return fmt.Errorf("test=%+v: exec: %v %v", test, err, output)
337 }
338 path := output
339 if path[:len(rootDir)] != rootDir {
340 return fmt.Errorf("test=%+v: %q must have %q prefix", test, path, rootDir)
341 }
342 path = path[len(rootDir)+1:]
343 if path != test.want {
344 return fmt.Errorf("test=%+v: want %q, got %q", test, test.want, path)
345 }
346 return nil
347 }
348
349 func (test commandTest) runOne(rootDir string, env []string, dir, arg0 string) error {
350 cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess", "--", "exec", dir, arg0)
351 cmd.Dir = rootDir
352 cmd.Env = env
353 output, err := cmd.CombinedOutput()
354 err = test.isSuccess(rootDir, string(output), err)
355 if (err != nil) != test.fails {
356 if test.fails {
357 return fmt.Errorf("test=%+v: succeeded, but expected to fail", test)
358 }
359 return err
360 }
361 return nil
362 }
363
364 func (test commandTest) run(t *testing.T, rootDir, printpathExe string) {
365 createFiles(t, rootDir, test.files, printpathExe)
366 PATHEXT := `.COM;.EXE;.BAT`
367 env := createEnv(rootDir, test.PATH, PATHEXT)
368 env = append(env, "GO_WANT_HELPER_PROCESS=1")
369 err := test.runOne(rootDir, env, test.dir, test.arg0)
370 if err != nil {
371 t.Error(err)
372 }
373 }
374
375 var commandTests = []commandTest{
376
377 {
378
379 files: []string{`a.exe`},
380 arg0: `a.exe`,
381 want: `a.exe`,
382 },
383 {
384
385 PATH: `p2;p`,
386 files: []string{`a.exe`, `p\a.exe`, `p2\a.exe`},
387 arg0: `a.exe`,
388 want: `a.exe`,
389 },
390 {
391
392 PATH: `p2;p`,
393 files: []string{`a.exe`, `p\a.exe`, `p2\a.exe`},
394 arg0: `a`,
395 want: `a.exe`,
396 },
397
398 {
399
400 files: []string{`p\a.exe`},
401 arg0: `p\a.exe`,
402 want: `p\a.exe`,
403 },
404 {
405
406 files: []string{`p\a.exe`},
407 arg0: `.\p\a.exe`,
408 want: `p\a.exe`,
409 },
410 {
411
412 PATH: `p2`,
413 files: []string{`p\a.exe`, `p2\a.exe`},
414 arg0: `p\a.exe`,
415 want: `p\a.exe`,
416 },
417 {
418
419 PATH: `p2`,
420 files: []string{`p\a.exe`, `p2\a.exe`},
421 arg0: `p\a`,
422 want: `p\a.exe`,
423 },
424
425 {
426
427 files: []string{`p\a.exe`},
428 dir: `p`,
429 arg0: `a.exe`,
430 want: `p\a.exe`,
431 fails: true,
432 },
433 {
434
435
436 files: []string{`a.exe`, `p\not_important_file`},
437 dir: `p`,
438 arg0: `a.exe`,
439 want: `a.exe`,
440 fails: true,
441 },
442 {
443
444
445
446 files: []string{`a.exe`, `p\a.exe`},
447 dir: `p`,
448 arg0: `a.exe`,
449 want: `p\a.exe`,
450 },
451 {
452
453 PATH: `p2;p`,
454 files: []string{`a.exe`, `p\a.exe`, `p2\a.exe`},
455 dir: `p`,
456 arg0: `a.exe`,
457 want: `p\a.exe`,
458 },
459 {
460
461 PATH: `p2;p`,
462 files: []string{`a.exe`, `p\a.exe`, `p2\a.exe`},
463 dir: `p`,
464 arg0: `a`,
465 want: `p\a.exe`,
466 },
467 {
468
469
470 PATH: `p2;p`,
471 files: []string{`p\a.exe`, `p2\a.exe`},
472 dir: `p`,
473 arg0: `a.exe`,
474 want: `p2\a.exe`,
475 },
476
477 {
478
479 files: []string{`p\a.exe`},
480 dir: `p`,
481 arg0: `.\a.exe`,
482 want: `p\a.exe`,
483 },
484 {
485
486 PATH: `p2`,
487 files: []string{`p\a.exe`, `p2\a.exe`},
488 dir: `p`,
489 arg0: `.\a.exe`,
490 want: `p\a.exe`,
491 },
492 {
493
494 PATH: `p2`,
495 files: []string{`p\a.exe`, `p2\a.exe`},
496 dir: `p`,
497 arg0: `.\a`,
498 want: `p\a.exe`,
499 },
500 }
501
502 func TestCommand(t *testing.T) {
503 tmp := t.TempDir()
504 printpathExe := buildPrintPathExe(t, tmp)
505
506
507 for i, test := range commandTests {
508 dir := filepath.Join(tmp, "d"+strconv.Itoa(i))
509 err := os.Mkdir(dir, 0700)
510 if err != nil {
511 t.Fatal("Mkdir failed: ", err)
512 }
513 test.run(t, dir, printpathExe)
514 }
515 }
516
517
518
519
520 func buildPrintPathExe(t *testing.T, dir string) string {
521 const name = "printpath"
522 srcname := name + ".go"
523 err := os.WriteFile(filepath.Join(dir, srcname), []byte(printpathSrc), 0644)
524 if err != nil {
525 t.Fatalf("failed to create source: %v", err)
526 }
527 if err != nil {
528 t.Fatalf("failed to execute template: %v", err)
529 }
530 outname := name + ".exe"
531 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", outname, srcname)
532 cmd.Dir = dir
533 out, err := cmd.CombinedOutput()
534 if err != nil {
535 t.Fatalf("failed to build executable: %v - %v", err, string(out))
536 }
537 return filepath.Join(dir, outname)
538 }
539
540 const printpathSrc = `
541 package main
542
543 import (
544 "os"
545 "syscall"
546 "unicode/utf16"
547 "unsafe"
548 )
549
550 func getMyName() (string, error) {
551 var sysproc = syscall.MustLoadDLL("kernel32.dll").MustFindProc("GetModuleFileNameW")
552 b := make([]uint16, syscall.MAX_PATH)
553 r, _, err := sysproc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
554 n := uint32(r)
555 if n == 0 {
556 return "", err
557 }
558 return string(utf16.Decode(b[0:n])), nil
559 }
560
561 func main() {
562 path, err := getMyName()
563 if err != nil {
564 os.Stderr.Write([]byte("getMyName failed: " + err.Error() + "\n"))
565 os.Exit(1)
566 }
567 os.Stdout.Write([]byte(path))
568 }
569 `
570
View as plain text