Source file
src/os/exec/exec_posix_test.go
1
2
3
4
5
6
7 package exec_test
8
9 import (
10 "fmt"
11 "internal/testenv"
12 "io"
13 "os"
14 "os/exec"
15 "os/signal"
16 "os/user"
17 "path/filepath"
18 "runtime"
19 "slices"
20 "strconv"
21 "strings"
22 "sync"
23 "syscall"
24 "testing"
25 "time"
26 )
27
28 func init() {
29 registerHelperCommand("pwd", cmdPwd)
30 registerHelperCommand("signaltest", cmdSignalTest)
31 }
32
33 func cmdPwd(...string) {
34 pwd, err := os.Getwd()
35 if err != nil {
36 fmt.Fprintln(os.Stderr, err)
37 os.Exit(1)
38 }
39 fmt.Println(pwd)
40 }
41
42 func TestCredentialNoSetGroups(t *testing.T) {
43 if runtime.GOOS == "android" {
44 maySkipHelperCommand("echo")
45 t.Skip("unsupported on Android")
46 }
47 t.Parallel()
48
49 u, err := user.Current()
50 if err != nil {
51 t.Fatalf("error getting current user: %v", err)
52 }
53
54 uid, err := strconv.Atoi(u.Uid)
55 if err != nil {
56 t.Fatalf("error converting Uid=%s to integer: %v", u.Uid, err)
57 }
58
59 gid, err := strconv.Atoi(u.Gid)
60 if err != nil {
61 t.Fatalf("error converting Gid=%s to integer: %v", u.Gid, err)
62 }
63
64
65 cmd := helperCommand(t, "echo", "foo")
66 cmd.SysProcAttr = &syscall.SysProcAttr{
67 Credential: &syscall.Credential{
68 Uid: uint32(uid),
69 Gid: uint32(gid),
70 NoSetGroups: true,
71 },
72 }
73
74 if err = cmd.Run(); err != nil {
75 t.Errorf("Failed to run command: %v", err)
76 }
77 }
78
79
80
81 func TestWaitid(t *testing.T) {
82 t.Parallel()
83
84 cmd := helperCommand(t, "pipetest")
85 stdin, err := cmd.StdinPipe()
86 if err != nil {
87 t.Fatal(err)
88 }
89 stdout, err := cmd.StdoutPipe()
90 if err != nil {
91 t.Fatal(err)
92 }
93 if err := cmd.Start(); err != nil {
94 t.Fatal(err)
95 }
96
97
98 const msg = "O:ping\n"
99 if _, err := io.WriteString(stdin, msg); err != nil {
100 t.Fatal(err)
101 }
102 buf := make([]byte, len(msg))
103 if _, err := io.ReadFull(stdout, buf); err != nil {
104 t.Fatal(err)
105 }
106
107
108 if err := cmd.Process.Signal(syscall.SIGSTOP); err != nil {
109 cmd.Process.Kill()
110 t.Fatal(err)
111 }
112
113 ch := make(chan error)
114 go func() {
115 ch <- cmd.Wait()
116 }()
117
118
119
120
121 if testing.Short() {
122 time.Sleep(1 * time.Millisecond)
123 } else {
124 time.Sleep(10 * time.Millisecond)
125 }
126
127
128
129
130 if err := cmd.Process.Signal(syscall.SIGCONT); err != nil {
131 t.Error(err)
132 syscall.Kill(cmd.Process.Pid, syscall.SIGCONT)
133 }
134
135
136
137 stdin.Close()
138 err = <-ch
139 if err != nil {
140 t.Fatal(err)
141 }
142 }
143
144
145
146
147 func TestImplicitPWD(t *testing.T) {
148 t.Parallel()
149
150 cwd, err := os.Getwd()
151 if err != nil {
152 t.Fatal(err)
153 }
154
155 cases := []struct {
156 name string
157 dir string
158 want string
159 }{
160 {"empty", "", cwd},
161 {"dot", ".", cwd},
162 {"dotdot", "..", filepath.Dir(cwd)},
163 {"PWD", cwd, cwd},
164 {"PWDdotdot", cwd + string(filepath.Separator) + "..", filepath.Dir(cwd)},
165 }
166
167 for _, tc := range cases {
168 tc := tc
169 t.Run(tc.name, func(t *testing.T) {
170 t.Parallel()
171
172 cmd := helperCommand(t, "pwd")
173 if cmd.Env != nil {
174 t.Fatalf("test requires helperCommand not to set Env field")
175 }
176 cmd.Dir = tc.dir
177
178 var pwds []string
179 for _, kv := range cmd.Environ() {
180 if strings.HasPrefix(kv, "PWD=") {
181 pwds = append(pwds, strings.TrimPrefix(kv, "PWD="))
182 }
183 }
184
185 wantPWDs := []string{tc.want}
186 if tc.dir == "" {
187 if _, ok := os.LookupEnv("PWD"); !ok {
188 wantPWDs = nil
189 }
190 }
191 if !slices.Equal(pwds, wantPWDs) {
192 t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t"))
193 }
194
195 cmd.Stderr = new(strings.Builder)
196 out, err := cmd.Output()
197 if err != nil {
198 t.Fatalf("%v:\n%s", err, cmd.Stderr)
199 }
200 got := strings.Trim(string(out), "\r\n")
201 t.Logf("in\n\t%s\n`pwd` reported\n\t%s", tc.dir, got)
202 if got != tc.want {
203 t.Errorf("want\n\t%s", tc.want)
204 }
205 })
206 }
207 }
208
209
210
211
212 func TestExplicitPWD(t *testing.T) {
213 t.Parallel()
214
215 maySkipHelperCommand("pwd")
216 testenv.MustHaveSymlink(t)
217
218 cwd, err := os.Getwd()
219 if err != nil {
220 t.Fatal(err)
221 }
222
223 link := filepath.Join(t.TempDir(), "link")
224 if err := os.Symlink(cwd, link); err != nil {
225 t.Fatal(err)
226 }
227
228
229
230 cases := []struct {
231 name string
232 dir string
233 pwd string
234 }{
235 {name: "original PWD", pwd: cwd},
236 {name: "link PWD", pwd: link},
237 {name: "in link with original PWD", dir: link, pwd: cwd},
238 {name: "in dir with link PWD", dir: cwd, pwd: link},
239
240
241
242
243 }
244 for _, tc := range cases {
245 tc := tc
246 t.Run(tc.name, func(t *testing.T) {
247 t.Parallel()
248
249 cmd := helperCommand(t, "pwd")
250
251
252
253 cmd.Env = append(cmd.Environ(), "PWD="+tc.pwd)
254 cmd.Dir = tc.dir
255
256 var pwds []string
257 for _, kv := range cmd.Environ() {
258 if strings.HasPrefix(kv, "PWD=") {
259 pwds = append(pwds, strings.TrimPrefix(kv, "PWD="))
260 }
261 }
262
263 wantPWDs := []string{tc.pwd}
264 if !slices.Equal(pwds, wantPWDs) {
265 t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t"))
266 }
267
268 cmd.Stderr = new(strings.Builder)
269 out, err := cmd.Output()
270 if err != nil {
271 t.Fatalf("%v:\n%s", err, cmd.Stderr)
272 }
273 got := strings.Trim(string(out), "\r\n")
274 t.Logf("in\n\t%s\nwith PWD=%s\nsubprocess os.Getwd() reported\n\t%s", tc.dir, tc.pwd, got)
275 if got != tc.pwd {
276 t.Errorf("want\n\t%s", tc.pwd)
277 }
278 })
279 }
280 }
281
282
283 func TestSIGCHLD(t *testing.T) {
284 cmd := helperCommand(t, "signaltest")
285 out, err := cmd.CombinedOutput()
286 t.Logf("%s", out)
287 if err != nil {
288 t.Error(err)
289 }
290 }
291
292
293
294
295 func cmdSignalTest(...string) {
296 chSig := make(chan os.Signal, 1)
297 signal.Notify(chSig, syscall.SIGCHLD)
298
299 var wg sync.WaitGroup
300 wg.Add(1)
301 go func() {
302 defer wg.Done()
303 c := 0
304 for range chSig {
305 c++
306 fmt.Printf("SIGCHLD %d\n", c)
307 if c > 1 {
308 fmt.Println("too many SIGCHLD signals")
309 os.Exit(1)
310 }
311 }
312 }()
313 defer func() {
314 signal.Reset(syscall.SIGCHLD)
315 close(chSig)
316 wg.Wait()
317 }()
318
319 exe, err := os.Executable()
320 if err != nil {
321 fmt.Printf("os.Executable failed: %v\n", err)
322 os.Exit(1)
323 }
324
325 cmd := exec.Command(exe, "hang", "200ms")
326 cmd.Stdout = os.Stdout
327 cmd.Stderr = os.Stderr
328 if err := cmd.Run(); err != nil {
329 fmt.Printf("failed to run child process: %v\n", err)
330 os.Exit(1)
331 }
332 }
333
View as plain text