| | |
| | |
| | |
| |
|
| | |
| |
|
| | package exec_test |
| |
|
| | import ( |
| | "fmt" |
| | "internal/testenv" |
| | "io" |
| | "os" |
| | "os/exec" |
| | "os/signal" |
| | "os/user" |
| | "path/filepath" |
| | "runtime" |
| | "slices" |
| | "strconv" |
| | "strings" |
| | "sync" |
| | "syscall" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | func init() { |
| | registerHelperCommand("pwd", cmdPwd) |
| | registerHelperCommand("signaltest", cmdSignalTest) |
| | } |
| |
|
| | func cmdPwd(...string) { |
| | pwd, err := os.Getwd() |
| | if err != nil { |
| | fmt.Fprintln(os.Stderr, err) |
| | os.Exit(1) |
| | } |
| | fmt.Println(pwd) |
| | } |
| |
|
| | func TestCredentialNoSetGroups(t *testing.T) { |
| | if runtime.GOOS == "android" { |
| | maySkipHelperCommand("echo") |
| | t.Skip("unsupported on Android") |
| | } |
| | t.Parallel() |
| |
|
| | u, err := user.Current() |
| | if err != nil { |
| | t.Fatalf("error getting current user: %v", err) |
| | } |
| |
|
| | uid, err := strconv.Atoi(u.Uid) |
| | if err != nil { |
| | t.Fatalf("error converting Uid=%s to integer: %v", u.Uid, err) |
| | } |
| |
|
| | gid, err := strconv.Atoi(u.Gid) |
| | if err != nil { |
| | t.Fatalf("error converting Gid=%s to integer: %v", u.Gid, err) |
| | } |
| |
|
| | |
| | cmd := helperCommand(t, "echo", "foo") |
| | cmd.SysProcAttr = &syscall.SysProcAttr{ |
| | Credential: &syscall.Credential{ |
| | Uid: uint32(uid), |
| | Gid: uint32(gid), |
| | NoSetGroups: true, |
| | }, |
| | } |
| |
|
| | if err = cmd.Run(); err != nil { |
| | t.Errorf("Failed to run command: %v", err) |
| | } |
| | } |
| |
|
| | |
| | |
| | func TestWaitid(t *testing.T) { |
| | t.Parallel() |
| |
|
| | cmd := helperCommand(t, "pipetest") |
| | stdin, err := cmd.StdinPipe() |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | stdout, err := cmd.StdoutPipe() |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | if err := cmd.Start(); err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | |
| | const msg = "O:ping\n" |
| | if _, err := io.WriteString(stdin, msg); err != nil { |
| | t.Fatal(err) |
| | } |
| | buf := make([]byte, len(msg)) |
| | if _, err := io.ReadFull(stdout, buf); err != nil { |
| | t.Fatal(err) |
| | } |
| | |
| |
|
| | if err := cmd.Process.Signal(syscall.SIGSTOP); err != nil { |
| | cmd.Process.Kill() |
| | t.Fatal(err) |
| | } |
| |
|
| | ch := make(chan error) |
| | go func() { |
| | ch <- cmd.Wait() |
| | }() |
| |
|
| | |
| | |
| | |
| | if testing.Short() { |
| | time.Sleep(1 * time.Millisecond) |
| | } else { |
| | time.Sleep(10 * time.Millisecond) |
| | } |
| |
|
| | |
| | |
| | |
| | if err := cmd.Process.Signal(syscall.SIGCONT); err != nil { |
| | t.Error(err) |
| | syscall.Kill(cmd.Process.Pid, syscall.SIGCONT) |
| | } |
| |
|
| | |
| | |
| | stdin.Close() |
| | err = <-ch |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func TestImplicitPWD(t *testing.T) { |
| | t.Parallel() |
| |
|
| | cwd, err := os.Getwd() |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | cases := []struct { |
| | name string |
| | dir string |
| | want string |
| | }{ |
| | {"empty", "", cwd}, |
| | {"dot", ".", cwd}, |
| | {"dotdot", "..", filepath.Dir(cwd)}, |
| | {"PWD", cwd, cwd}, |
| | {"PWDdotdot", cwd + string(filepath.Separator) + "..", filepath.Dir(cwd)}, |
| | } |
| |
|
| | for _, tc := range cases { |
| | t.Run(tc.name, func(t *testing.T) { |
| | t.Parallel() |
| |
|
| | cmd := helperCommand(t, "pwd") |
| | if cmd.Env != nil { |
| | t.Fatalf("test requires helperCommand not to set Env field") |
| | } |
| | cmd.Dir = tc.dir |
| |
|
| | var pwds []string |
| | for _, kv := range cmd.Environ() { |
| | if strings.HasPrefix(kv, "PWD=") { |
| | pwds = append(pwds, strings.TrimPrefix(kv, "PWD=")) |
| | } |
| | } |
| |
|
| | wantPWDs := []string{tc.want} |
| | if tc.dir == "" { |
| | if _, ok := os.LookupEnv("PWD"); !ok { |
| | wantPWDs = nil |
| | } |
| | } |
| | if !slices.Equal(pwds, wantPWDs) { |
| | t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t")) |
| | } |
| |
|
| | cmd.Stderr = new(strings.Builder) |
| | out, err := cmd.Output() |
| | if err != nil { |
| | t.Fatalf("%v:\n%s", err, cmd.Stderr) |
| | } |
| | got := strings.Trim(string(out), "\r\n") |
| | t.Logf("in\n\t%s\n`pwd` reported\n\t%s", tc.dir, got) |
| | if got != tc.want { |
| | t.Errorf("want\n\t%s", tc.want) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func TestExplicitPWD(t *testing.T) { |
| | t.Parallel() |
| |
|
| | maySkipHelperCommand("pwd") |
| | testenv.MustHaveSymlink(t) |
| |
|
| | cwd, err := os.Getwd() |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | link := filepath.Join(t.TempDir(), "link") |
| | if err := os.Symlink(cwd, link); err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | |
| | |
| | cases := []struct { |
| | name string |
| | dir string |
| | pwd string |
| | }{ |
| | {name: "original PWD", pwd: cwd}, |
| | {name: "link PWD", pwd: link}, |
| | {name: "in link with original PWD", dir: link, pwd: cwd}, |
| | {name: "in dir with link PWD", dir: cwd, pwd: link}, |
| | |
| | |
| | |
| | |
| | } |
| | for _, tc := range cases { |
| | t.Run(tc.name, func(t *testing.T) { |
| | t.Parallel() |
| |
|
| | cmd := helperCommand(t, "pwd") |
| | |
| | |
| | |
| | cmd.Env = append(cmd.Environ(), "PWD="+tc.pwd) |
| | cmd.Dir = tc.dir |
| |
|
| | var pwds []string |
| | for _, kv := range cmd.Environ() { |
| | if strings.HasPrefix(kv, "PWD=") { |
| | pwds = append(pwds, strings.TrimPrefix(kv, "PWD=")) |
| | } |
| | } |
| |
|
| | wantPWDs := []string{tc.pwd} |
| | if !slices.Equal(pwds, wantPWDs) { |
| | t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t")) |
| | } |
| |
|
| | cmd.Stderr = new(strings.Builder) |
| | out, err := cmd.Output() |
| | if err != nil { |
| | t.Fatalf("%v:\n%s", err, cmd.Stderr) |
| | } |
| | got := strings.Trim(string(out), "\r\n") |
| | t.Logf("in\n\t%s\nwith PWD=%s\nsubprocess os.Getwd() reported\n\t%s", tc.dir, tc.pwd, got) |
| | if got != tc.pwd { |
| | t.Errorf("want\n\t%s", tc.pwd) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | func TestSIGCHLD(t *testing.T) { |
| | cmd := helperCommand(t, "signaltest") |
| | out, err := cmd.CombinedOutput() |
| | t.Logf("%s", out) |
| | if err != nil { |
| | t.Error(err) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func cmdSignalTest(...string) { |
| | chSig := make(chan os.Signal, 1) |
| | signal.Notify(chSig, syscall.SIGCHLD) |
| |
|
| | var wg sync.WaitGroup |
| | wg.Add(1) |
| | go func() { |
| | defer wg.Done() |
| | c := 0 |
| | for range chSig { |
| | c++ |
| | fmt.Printf("SIGCHLD %d\n", c) |
| | if c > 1 { |
| | fmt.Println("too many SIGCHLD signals") |
| | os.Exit(1) |
| | } |
| | } |
| | }() |
| | defer func() { |
| | signal.Reset(syscall.SIGCHLD) |
| | close(chSig) |
| | wg.Wait() |
| | }() |
| |
|
| | exe, err := os.Executable() |
| | if err != nil { |
| | fmt.Printf("os.Executable failed: %v\n", err) |
| | os.Exit(1) |
| | } |
| |
|
| | cmd := exec.Command(exe, "hang", "200ms") |
| | cmd.Stdout = os.Stdout |
| | cmd.Stderr = os.Stderr |
| | if err := cmd.Run(); err != nil { |
| | fmt.Printf("failed to run child process: %v\n", err) |
| | os.Exit(1) |
| | } |
| | } |
| |
|