From fc513b6f9a60fcca26083fd7d57d6a30159e5f35 Mon Sep 17 00:00:00 2001 From: Shirou WAKAYAMA Date: Wed, 16 Sep 2015 11:58:02 +0900 Subject: [PATCH] proces[darwin]: change exec.Command to interface to enable mocking. Add common.invoker interface to mock exec.Command. common.FakeInvoker returns expected file if exists instead of invoke exec.Command. Currenly, mocking is enabled only process.Pids(). I will expand to other funcs incrementally. --- common/common.go | 48 +++++++++++++++++++++++++ process/expected/darwin/%2Fbin%2Fps-x-opid_fail | 10 ++++++ process/process.go | 9 ++++- process/process_darwin.go | 9 +++-- process/process_test.go | 21 +++++++++++ 5 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 process/expected/darwin/%2Fbin%2Fps-x-opid_fail diff --git a/common/common.go b/common/common.go index efefbf0..a7dbc28 100644 --- a/common/common.go +++ b/common/common.go @@ -9,12 +9,60 @@ package common import ( "bufio" "errors" + "io/ioutil" + "net/url" "os" + "os/exec" + "path" "reflect" + "runtime" "strconv" "strings" ) +type Invoker interface { + Command(string, ...string) ([]byte, error) +} + +type Invoke struct{} + +func (i Invoke) Command(name string, arg ...string) ([]byte, error) { + return exec.Command(name, arg...).Output() +} + +type FakeInvoke struct { + CommandExpectedDir string // CommandExpectedDir specifies dir which includes expected outputs. + Suffix string // Suffix species expected file name suffix such as "fail" + Error error // If Error specfied, return the error. +} + +// Command in FakeInvoke returns from expected file if exists. +func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { + if i.Error != nil { + return []byte{}, i.Error + } + + arch := runtime.GOOS + + fname := strings.Join(append([]string{name}, arg...), "") + fname = url.QueryEscape(fname) + var dir string + if i.CommandExpectedDir == "" { + dir = "expected" + } else { + dir = i.CommandExpectedDir + } + fpath := path.Join(dir, arch, fname) + if i.Suffix != "" { + fpath += "_" + i.Suffix + } + if PathExists(fpath) { + return ioutil.ReadFile(fpath) + } else { + return exec.Command(name, arg...).Output() + } +} + var NotImplementedError = errors.New("not implemented yet") // ReadLines reads contents from a file and splits them by new lines. diff --git a/process/expected/darwin/%2Fbin%2Fps-x-opid_fail b/process/expected/darwin/%2Fbin%2Fps-x-opid_fail new file mode 100644 index 0000000..fce59ef --- /dev/null +++ b/process/expected/darwin/%2Fbin%2Fps-x-opid_fail @@ -0,0 +1,10 @@ + PID + 245 + 247 + 248 + 249 + 254 + 262 + 264 + 265 + 267 diff --git a/process/process.go b/process/process.go index 7b4b877..abe522c 100644 --- a/process/process.go +++ b/process/process.go @@ -5,9 +5,16 @@ import ( "runtime" "time" - cpu "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/common" + "github.com/shirou/gopsutil/cpu" ) +var invoke common.Invoker + +func init() { + invoke = common.Invoke{} +} + type Process struct { Pid int32 `json:"pid"` name string diff --git a/process/process_darwin.go b/process/process_darwin.go index d979337..d4c0e2e 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -4,15 +4,14 @@ package process import ( "bytes" - "os/exec" "strconv" "strings" "syscall" "unsafe" - common "github.com/shirou/gopsutil/common" - cpu "github.com/shirou/gopsutil/cpu" - net "github.com/shirou/gopsutil/net" + "github.com/shirou/gopsutil/common" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/net" ) // copied from sys/sysctl.h @@ -391,7 +390,7 @@ func callPs(arg string, pid int32, threadOption bool) ([][]string, error) { } else { cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} } - out, err := exec.Command("/bin/ps", cmd...).Output() + out, err := invoke.Command("/bin/ps", cmd...) if err != nil { return [][]string{}, err } diff --git a/process/process_test.go b/process/process_test.go index b06df86..cdef630 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -1,13 +1,19 @@ package process import ( + "fmt" "os" "runtime" "strings" + "sync" "testing" "time" + + "github.com/shirou/gopsutil/common" ) +var mu sync.Mutex + func testGetProcess() Process { checkPid := os.Getpid() // process.test ret, _ := NewProcess(int32(checkPid)) @@ -24,6 +30,21 @@ func Test_Pids(t *testing.T) { } } +func Test_Pids_Fail(t *testing.T) { + mu.Lock() + defer mu.Unlock() + + invoke = common.FakeInvoke{Suffix: "fail", Error: fmt.Errorf("hoge")} + ret, err := Pids() + invoke = common.Invoke{} + if err != nil { + t.Errorf("error %v", err) + } + if len(ret) != 9 { + t.Errorf("wrong getted pid nums: %v/%d", ret, len(ret)) + } +} + func Test_Pid_exists(t *testing.T) { checkPid := os.Getpid()