staros/process_test.go

814 lines
22 KiB
Go
Raw Normal View History

2020-02-11 10:53:25 +08:00
package staros
import (
"bytes"
2020-07-17 09:56:23 +08:00
"context"
"encoding/base64"
"encoding/binary"
"errors"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
2020-02-11 10:53:25 +08:00
"testing"
2020-07-17 09:56:23 +08:00
"time"
"unicode/utf16"
2020-02-11 10:53:25 +08:00
)
func testCommandArgs(script string) (string, []string) {
if runtime.GOOS == "windows" {
return "cmd.exe", []string{"/c", script}
}
return "sh", []string{"-c", script}
}
func testWindowsPowerShellArgs(script string) (string, []string) {
utf16Script := utf16.Encode([]rune(script))
encoded := make([]byte, len(utf16Script)*2)
for i, r := range utf16Script {
binary.LittleEndian.PutUint16(encoded[i*2:], uint16(r))
}
return "powershell.exe", []string{"-NoProfile", "-EncodedCommand", base64.StdEncoding.EncodeToString(encoded)}
}
type closeTrackingWriteCloser struct {
closed bool
}
func (w *closeTrackingWriteCloser) Write(data []byte) (int, error) {
return len(data), nil
}
func (w *closeTrackingWriteCloser) Close() error {
w.closed = true
return nil
}
func TestStarCmdCapturesOutputAndExitCode(t *testing.T) {
script := "printf 'hello'; printf 'err' 1>&2"
command, args := testCommandArgs(script)
if runtime.GOOS == "windows" {
command, args = testWindowsPowerShellArgs("[Console]::Out.Write('hello'); [Console]::Error.Write('err')")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
<-cmd.Stoped()
out, outErr := cmd.AllOutPut()
if out != "hello" {
t.Fatalf("expected stdout %q, got %q", "hello", out)
}
if outErr == nil || outErr.Error() != "err" {
t.Fatalf("expected stderr error %q, got %v", "err", outErr)
}
if got := cmd.ExitCode(); got != 0 {
t.Fatalf("expected exit code 0, got %d", got)
}
}
func TestStarCmdWaitReturnsProcessError(t *testing.T) {
command, args := testCommandArgs("exit 7")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Wait(); !errors.Is(err, errCommandProcessNotStarted) {
t.Fatalf("expected errCommandProcessNotStarted before start, got %v", err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.Wait(); err == nil {
t.Fatal("expected wait error for non-zero exit")
}
if got := cmd.ExitCode(); got != 7 {
t.Fatalf("expected exit code 7, got %d", got)
}
if err := cmd.Wait(); err == nil {
t.Fatal("expected repeated Wait to keep final process error")
}
}
func TestStarCmdWaitTimeoutAndContext(t *testing.T) {
command, args := testCommandArgs("sleep 1")
if runtime.GOOS == "windows" {
command, args = testCommandArgs("ping -n 2 127.0.0.1 >nul")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.WaitTimeout(10 * time.Millisecond); !errors.Is(err, ERR_TIMEOUT) {
t.Fatalf("expected ERR_TIMEOUT, got %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
if err := cmd.WaitContext(ctx); !errors.Is(err, context.DeadlineExceeded) {
t.Fatalf("expected context deadline, got %v", err)
}
if err := cmd.WaitTimeout(3 * time.Second); err != nil {
t.Fatalf("expected command to finish, got %v", err)
}
}
func TestStarCmdWaitReturnsResultAfterProcessDone(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.Wait(); err != nil {
t.Fatal(err)
}
if err := cmd.WaitTimeout(0); err != nil {
t.Fatalf("expected finished command to beat zero timeout, got %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := cmd.WaitContext(ctx); err != nil {
t.Fatalf("expected finished command to beat canceled context, got %v", err)
}
}
func TestStarCmdWaitContextFinishedCommandWinsOverCanceledContext(t *testing.T) {
t.Run("success", func(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.Wait(); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := cmd.WaitContext(ctx); err != nil {
t.Fatalf("finished successful command should win over canceled context, got %v", err)
}
})
t.Run("failed", func(t *testing.T) {
command, args := testCommandArgs("exit 7")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
waitErr := cmd.Wait()
if waitErr == nil {
t.Fatal("expected command wait error")
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := cmd.WaitContext(ctx); err == nil || err.Error() != waitErr.Error() {
t.Fatalf("finished failed command should win over canceled context, got %v, want %v", err, waitErr)
}
})
}
func TestStarCmdStoppedAlias(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
select {
case <-cmd.Stopped():
case <-time.After(time.Second):
t.Fatal("Stopped should close after command exits")
}
select {
case <-cmd.Stoped():
case <-time.After(time.Second):
t.Fatal("Stoped compatibility alias should close after command exits")
}
}
func TestStarCmdStopedPublishesFinalExitCode(t *testing.T) {
command, args := testCommandArgs("exit 7")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
<-cmd.Stoped()
if cmd.IsRunning() {
t.Fatal("command should not be running after Stoped closes")
}
if got := cmd.ExitCode(); got != 7 {
t.Fatalf("expected exit code 7, got %d", got)
}
2020-02-11 10:53:25 +08:00
}
2020-07-17 09:56:23 +08:00
func TestStarCmdRejectsRepeatedStart(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
2020-07-17 09:56:23 +08:00
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.Start(); !errors.Is(err, errCommandAlreadyStarted) {
t.Fatalf("expected errCommandAlreadyStarted, got %v", err)
}
<-cmd.Stoped()
if err := cmd.Start(); !errors.Is(err, errCommandAlreadyStarted) {
t.Fatalf("expected errCommandAlreadyStarted after exit, got %v", err)
}
}
func TestStarCmdStartFailureClosesStoped(t *testing.T) {
cmd, err := Command("__staros_missing_command__")
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err == nil {
t.Fatal("expected start failure")
}
select {
case <-cmd.Stoped():
case <-time.After(time.Second):
t.Fatal("Stoped should close after start failure")
}
if cmd.IsRunning() {
t.Fatal("command should not be running after start failure")
}
if got := cmd.ExitCode(); got != -1 {
t.Fatalf("expected exit code -1 after start failure, got %d", got)
}
}
func TestStarCmdCapturesLargeOutput(t *testing.T) {
expected := strings.Repeat("x", 256*1024)
script := "awk 'BEGIN{for(i=0;i<262144;i++) printf \"x\"}'"
command, args := testCommandArgs(script)
if runtime.GOOS == "windows" {
command, args = testWindowsPowerShellArgs("[Console]::Out.Write(('x' * 262144))")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
<-cmd.Stoped()
out, err := cmd.AllOutPut()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal([]byte(out), []byte(expected)) {
t.Fatalf("expected %d stdout bytes, got %d", len(expected), len(out))
}
}
func TestStarCmdStreamsOutput(t *testing.T) {
script := "printf 'out'; printf 'err' 1>&2"
command, args := testCommandArgs(script)
if runtime.GOOS == "windows" {
command, args = testWindowsPowerShellArgs("[Console]::Out.Write('out'); [Console]::Error.Write('err')")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
stdout := cmd.StdoutChan()
stderr := cmd.StderrChan()
output := cmd.OutputChan()
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
var stdoutData, stderrData string
var outputData []StarCmdOutput
for stdout != nil || stderr != nil || output != nil {
select {
case data, ok := <-stdout:
if !ok {
stdout = nil
continue
}
stdoutData += string(data)
case data, ok := <-stderr:
if !ok {
stderr = nil
continue
}
stderrData += string(data)
case data, ok := <-output:
if !ok {
output = nil
continue
}
outputData = append(outputData, data)
case <-time.After(3 * time.Second):
t.Fatal("stream output timed out")
}
}
if stdoutData != "out" {
t.Fatalf("expected streamed stdout %q, got %q", "out", stdoutData)
}
if stderrData != "err" {
t.Fatalf("expected streamed stderr %q, got %q", "err", stderrData)
}
var seenStdout, seenStderr bool
for _, item := range outputData {
switch item.Stream {
case StarCmdOutputStdout:
seenStdout = seenStdout || string(item.Data) == "out"
case StarCmdOutputStderr:
seenStderr = seenStderr || string(item.Data) == "err"
default:
t.Fatalf("unknown output stream %v", item.Stream)
}
}
if !seenStdout || !seenStderr {
t.Fatalf("expected combined output stream to include stdout and stderr, got %#v", outputData)
}
}
2020-07-17 09:56:23 +08:00
func TestStarCmdStreamNilReturnsClosedChannels(t *testing.T) {
var cmd *StarCmd
select {
case _, ok := <-cmd.StdoutChan():
if ok {
t.Fatal("nil stdout stream should be closed")
}
case <-time.After(time.Second):
t.Fatal("nil stdout stream should close immediately")
}
select {
case _, ok := <-cmd.StderrChan():
if ok {
t.Fatal("nil stderr stream should be closed")
}
case <-time.After(time.Second):
t.Fatal("nil stderr stream should close immediately")
}
select {
case _, ok := <-cmd.OutputChan():
if ok {
t.Fatal("nil output stream should be closed")
}
case <-time.After(time.Second):
t.Fatal("nil output stream should close immediately")
}
}
func TestStarCmdRedirectOutputWriterKeepsCapture(t *testing.T) {
script := "printf 'out'; printf 'err' 1>&2"
command, args := testCommandArgs(script)
if runtime.GOOS == "windows" {
command, args = testWindowsPowerShellArgs("[Console]::Out.Write('out'); [Console]::Error.Write('err')")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
var redirected bytes.Buffer
if err := cmd.RedirectOutput(&redirected); err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
<-cmd.Stopped()
if got := redirected.String(); got != "outerr" && got != "errout" {
t.Fatalf("expected redirected stdout/stderr bytes, got %q", got)
}
if out := cmd.AllStdOut(); out != "out" {
t.Fatalf("expected captured stdout %q, got %q", "out", out)
}
if err := cmd.AllStdErr(); err == nil || err.Error() != "err" {
t.Fatalf("expected captured stderr %q, got %v", "err", err)
}
}
func TestStarCmdRedirectFiles(t *testing.T) {
dir := t.TempDir()
stdoutFile := filepath.Join(dir, "stdout.txt")
stderrFile := filepath.Join(dir, "stderr.txt")
script := "printf 'out'; printf 'err' 1>&2"
command, args := testCommandArgs(script)
if runtime.GOOS == "windows" {
command, args = testWindowsPowerShellArgs("[Console]::Out.Write('out'); [Console]::Error.Write('err')")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.RedirectStdoutFile(stdoutFile); err != nil {
t.Fatal(err)
}
if err := cmd.RedirectStderrFile(stderrFile); err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
<-cmd.Stopped()
stdoutData, err := ioutil.ReadFile(stdoutFile)
if err != nil {
t.Fatal(err)
}
stderrData, err := ioutil.ReadFile(stderrFile)
if err != nil {
t.Fatal(err)
}
if string(stdoutData) != "out" {
t.Fatalf("expected stdout file %q, got %q", "out", string(stdoutData))
}
if string(stderrData) != "err" {
t.Fatalf("expected stderr file %q, got %q", "err", string(stderrData))
}
}
func TestStarCmdRedirectStdin(t *testing.T) {
command, args := testCommandArgs("cat")
if runtime.GOOS == "windows" {
command, args = testCommandArgs("more")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.RedirectStdin(strings.NewReader("hello\n")); err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
<-cmd.Stopped()
if out := cmd.AllStdOut(); !strings.Contains(out, "hello") {
t.Fatalf("expected redirected stdin in stdout, got %q", out)
}
if err := cmd.AllStdErr(); err != nil {
t.Fatalf("redirected stdin should not create command error, got %v", err)
}
if err := cmd.WriteCmdE("again"); !errors.Is(err, errCommandStdinUnavailable) {
t.Fatalf("expected errCommandStdinUnavailable after stdin redirect, got %v", err)
}
}
func TestStarCmdRedirectStdinClosesManagedPipe(t *testing.T) {
command, args := testCommandArgs("cat")
if runtime.GOOS == "windows" {
command, args = testCommandArgs("more")
}
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
tracker := &closeTrackingWriteCloser{}
cmd.lock.Lock()
cmd.infile = tracker
cmd.inclosed = false
cmd.lock.Unlock()
if err := cmd.RedirectStdin(strings.NewReader("hello\n")); err != nil {
t.Fatal(err)
}
if !tracker.closed {
t.Fatal("RedirectStdin should close the previously managed stdin pipe")
}
if err := cmd.WriteCmdE("again"); !errors.Is(err, errCommandStdinUnavailable) {
t.Fatalf("expected managed stdin to be unavailable after redirect, got %v", err)
}
}
func TestStarCmdDetachClosesManagedPipe(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
tracker := &closeTrackingWriteCloser{}
cmd.lock.Lock()
original := cmd.infile
cmd.infile = tracker
cmd.inclosed = false
cmd.lock.Unlock()
if original != nil {
_ = original.Close()
}
if err := cmd.DetachE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if err != nil {
t.Fatal(err)
}
if !tracker.closed {
t.Fatal("DetachE should close the managed stdin pipe")
}
if err := cmd.WriteCmdE("again"); !errors.Is(err, errCommandStdinClosed) {
t.Fatalf("expected detached stdin to be closed, got %v", err)
}
}
func TestStarCmdRedirectRejectsInvalidState(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.RedirectStdout(nil); !errors.Is(err, errCommandRedirectNil) {
t.Fatalf("expected errCommandRedirectNil, got %v", err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.RedirectStdout(&bytes.Buffer{}); !errors.Is(err, errCommandAlreadyStarted) {
t.Fatalf("expected errCommandAlreadyStarted, got %v", err)
}
<-cmd.Stopped()
}
func TestStarCmdCloseStdinLetsCommandExit(t *testing.T) {
script := "cat"
if runtime.GOOS == "windows" {
script = "more"
}
command, args := testCommandArgs(script)
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.WriteCmdE("hello"); err != nil {
t.Fatal(err)
}
if err := cmd.CloseStdinE(); err != nil {
t.Fatal(err)
}
select {
case <-cmd.Stoped():
case <-time.After(3 * time.Second):
t.Fatal("command should exit after stdin closes")
}
if out := cmd.AllStdOut(); !strings.Contains(out, "hello") {
t.Fatalf("expected echoed stdin, got %q", out)
}
if err := cmd.CloseStdinE(); !errors.Is(err, errCommandStdinClosed) {
t.Fatalf("expected errCommandStdinClosed, got %v", err)
}
if err := cmd.WriteCmdE("again"); !errors.Is(err, errCommandStdinClosed) {
t.Fatalf("expected errCommandStdinClosed after close, got %v", err)
}
}
func TestStarCmdWriteStdinRawDoesNotAppendNewline(t *testing.T) {
script := "cat"
if runtime.GOOS == "windows" {
script = "more"
}
command, args := testCommandArgs(script)
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.WriteStdinStringE("raw"); err != nil {
t.Fatal(err)
}
if err := cmd.WriteStdinE([]byte("-bytes")); err != nil {
t.Fatal(err)
}
if err := cmd.CloseStdinE(); err != nil {
t.Fatal(err)
}
if err := cmd.WaitTimeout(3 * time.Second); err != nil {
t.Fatal(err)
}
if out := cmd.AllStdOut(); !strings.Contains(out, "raw-bytes") {
t.Fatalf("expected raw stdin without inserted newline, got %q", out)
}
}
func TestStarCmdNilGuards(t *testing.T) {
var cmd *StarCmd
if cmd.IsRunning() {
t.Fatal("nil StarCmd should not be running")
}
if got := cmd.GetPid(); got != -1 {
t.Fatalf("expected nil pid -1, got %d", got)
}
if err := cmd.Release(); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.SetKeepCaps(); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.ReleaseE(); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.DetachE(); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.SetRunUserE(0, 0, nil); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.WriteCmdE("noop"); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.WriteStdinE([]byte("noop")); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.WriteStdinStringE("noop"); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.WriteStdinLineE("noop"); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.Wait(); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
if err := cmd.CloseStdinE(); !errors.Is(err, errNilCommand) {
t.Fatalf("expected errNilCommand, got %v", err)
}
}
func TestStarCmdReleaseUsesStartLifecycle(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.ReleaseE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if err != nil {
t.Fatal(err)
}
<-cmd.Stoped()
if got := cmd.ExitCode(); got != 0 {
t.Fatalf("expected exit code 0, got %d", got)
}
if err := cmd.ReleaseE(); !errors.Is(err, errCommandAlreadyReleased) {
t.Fatalf("expected errCommandAlreadyReleased, got %v", err)
}
}
func TestStarCmdReleaseAfterStartKeepsLifecycle(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.ReleaseE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if err != nil {
t.Fatal(err)
}
<-cmd.Stoped()
if got := cmd.ExitCode(); got != 0 {
t.Fatalf("expected exit code 0, got %d", got)
}
}
func TestStarCmdDetachRejectsRepeatedDetach(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.DetachE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if err != nil {
t.Fatal(err)
}
if err := cmd.DetachE(); !errors.Is(err, errCommandAlreadyDetached) {
t.Fatalf("expected errCommandAlreadyDetached, got %v", err)
}
if err := cmd.Start(); !errors.Is(err, errCommandDetached) {
t.Fatalf("expected errCommandDetached, got %v", err)
}
}
func TestStarCmdDetachPublishesWaitResult(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.DetachE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if err != nil {
t.Fatal(err)
}
if err := cmd.WaitTimeout(0); err != nil {
t.Fatalf("detached command should publish final wait result, got %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
if err := cmd.WaitContext(ctx); err != nil {
t.Fatalf("detached command should beat canceled context, got %v", err)
}
waitErr := make(chan error, 1)
go func() {
waitErr <- cmd.Wait()
}()
select {
case err := <-waitErr:
if err != nil {
t.Fatalf("detached command wait got %v", err)
}
case <-time.After(time.Second):
t.Fatal("detached command wait did not observe final result")
}
}
func TestStarCmdDetachDoesNotCaptureOutput(t *testing.T) {
script := "printf 'detached'; printf 'err' 1>&2"
if runtime.GOOS == "windows" {
script = "<nul set /p =detached & <nul set /p =err 1>&2"
}
command, args := testCommandArgs(script)
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.DetachE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if err != nil {
t.Fatal(err)
}
<-cmd.Stoped()
if out := cmd.AllStdOut(); out != "" {
t.Fatalf("detached command should not be captured, got stdout %q", out)
}
if err := cmd.AllStdErr(); err != nil {
t.Fatalf("detached command should not capture stderr, got %v", err)
}
}
func TestStarCmdDetachRejectsStartedCommand(t *testing.T) {
command, args := testCommandArgs("exit 0")
cmd, err := Command(command, args...)
if err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
if err := cmd.DetachE(); errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
} else if !errors.Is(err, errCommandAlreadyStarted) {
t.Fatalf("expected errCommandAlreadyStarted, got %v", err)
}
<-cmd.Stoped()
}
func TestFindProcessByPidCurrentProcess(t *testing.T) {
pid := os.Getpid()
process, err := FindProcessByPid(int64(pid))
if errors.Is(err, ERR_UNSUPPORTED) {
t.Skip(err)
}
if err != nil {
t.Fatal(err)
}
if process.Pid != int64(pid) {
t.Fatalf("expected pid %d, got %d", pid, process.Pid)
}
}
func TestStopedNilReturnsClosedChannel(t *testing.T) {
var cmd *StarCmd
select {
case <-cmd.Stoped():
case <-time.After(time.Second):
t.Fatal("nil Stoped channel should already be closed")
}
select {
case <-cmd.Stopped():
case <-time.After(time.Second):
t.Fatal("nil Stopped channel should already be closed")
}
2020-07-17 09:56:23 +08:00
}