mirror of
https://github.com/shirou/gopsutil.git
synced 2025-04-26 13:48:59 +08:00

e2c79a1 started to blindly set the process name to the full path (instead of the basename) of the cmdline exectuable if the process name from the process comm was truncated on linux. Python psutil never did that, and this is just wrong for python (or any executable interpreted script) where the process name is not the interpreter binary but the script itself. A new test to check process name value against psutil value is added here, which would hopefully catch any potential future changes in psutil. Reverts #542 Fixes #1485
865 lines
20 KiB
Go
865 lines
20 KiB
Go
package process
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"path/filepath"
|
|
"reflect"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/shirou/gopsutil/v3/internal/common"
|
|
)
|
|
|
|
var mu sync.Mutex
|
|
|
|
func skipIfNotImplementedErr(t *testing.T, err error) {
|
|
if errors.Is(err, common.ErrNotImplementedError) {
|
|
t.Skip("not implemented")
|
|
}
|
|
}
|
|
|
|
func testGetProcess() Process {
|
|
checkPid := os.Getpid() // process.test
|
|
ret, _ := NewProcess(int32(checkPid))
|
|
return *ret
|
|
}
|
|
|
|
func Test_Pids(t *testing.T) {
|
|
ret, err := Pids()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
if len(ret) == 0 {
|
|
t.Errorf("could not get pids %v", ret)
|
|
}
|
|
}
|
|
|
|
func Test_Pid_exists(t *testing.T) {
|
|
checkPid := os.Getpid()
|
|
|
|
ret, err := PidExists(int32(checkPid))
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
|
|
if ret == false {
|
|
t.Errorf("could not get process exists: %v", ret)
|
|
}
|
|
}
|
|
|
|
func Test_NewProcess(t *testing.T) {
|
|
checkPid := os.Getpid()
|
|
|
|
ret, err := NewProcess(int32(checkPid))
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
empty := &Process{}
|
|
if runtime.GOOS != "windows" { // Windows pid is 0
|
|
if empty == ret {
|
|
t.Errorf("error %v", ret)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_Process_memory_maps(t *testing.T) {
|
|
checkPid := os.Getpid()
|
|
|
|
ret, err := NewProcess(int32(checkPid))
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
|
|
// ungrouped memory maps
|
|
mmaps, err := ret.MemoryMaps(false)
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("memory map get error %v", err)
|
|
}
|
|
empty := MemoryMapsStat{}
|
|
for _, m := range *mmaps {
|
|
if m == empty {
|
|
t.Errorf("memory map get error %v", m)
|
|
}
|
|
}
|
|
|
|
// grouped memory maps
|
|
mmaps, err = ret.MemoryMaps(true)
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("memory map get error %v", err)
|
|
}
|
|
if len(*mmaps) != 1 {
|
|
t.Errorf("grouped memory maps length (%v) is not equal to 1", len(*mmaps))
|
|
}
|
|
if (*mmaps)[0] == empty {
|
|
t.Errorf("memory map is empty")
|
|
}
|
|
}
|
|
|
|
func Test_Process_MemoryInfo(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.MemoryInfo()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting memory info error %v", err)
|
|
}
|
|
empty := MemoryInfoStat{}
|
|
if v == nil || *v == empty {
|
|
t.Errorf("could not get memory info %v", v)
|
|
}
|
|
}
|
|
|
|
func Test_Process_CmdLine(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.Cmdline()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting cmdline error %v", err)
|
|
}
|
|
if !strings.Contains(v, "process.test") {
|
|
t.Errorf("invalid cmd line %v", v)
|
|
}
|
|
}
|
|
|
|
func Test_Process_CmdLineSlice(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.CmdlineSlice()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("getting cmdline slice error %v", err)
|
|
}
|
|
if !reflect.DeepEqual(v, os.Args) {
|
|
t.Errorf("returned cmdline slice not as expected:\nexp: %v\ngot: %v", os.Args, v)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Ppid(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.Ppid()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting ppid error %v", err)
|
|
}
|
|
if v == 0 {
|
|
t.Errorf("return value is 0 %v", v)
|
|
}
|
|
expected := os.Getppid()
|
|
if v != int32(expected) {
|
|
t.Errorf("return value is %v, expected %v", v, expected)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Status(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.Status()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting status error %v", err)
|
|
}
|
|
if len(v) == 0 {
|
|
t.Errorf("could not get state")
|
|
}
|
|
if v[0] != Running && v[0] != Sleep {
|
|
t.Errorf("got wrong state, %v", v)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Terminal(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
_, err := p.Terminal()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting terminal error %v", err)
|
|
}
|
|
}
|
|
|
|
func Test_Process_IOCounters(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.IOCounters()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting iocounter error %v", err)
|
|
return
|
|
}
|
|
empty := &IOCountersStat{}
|
|
if v == empty {
|
|
t.Errorf("error %v", v)
|
|
}
|
|
}
|
|
|
|
func Test_Process_NumCtx(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
_, err := p.NumCtxSwitches()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting numctx error %v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func Test_Process_Nice(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
n, err := p.Nice()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting nice error %v", err)
|
|
}
|
|
if runtime.GOOS != "windows" && n != 0 && n != 20 && n != 8 {
|
|
t.Errorf("invalid nice: %d", n)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Groups(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
v, err := p.Groups()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting groups error %v", err)
|
|
}
|
|
if len(v) == 0 {
|
|
t.Skip("Groups is empty")
|
|
}
|
|
if v[0] < 0 {
|
|
t.Errorf("invalid Groups: %v", v)
|
|
}
|
|
}
|
|
|
|
func Test_Process_NumThread(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
n, err := p.NumThreads()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting NumThread error %v", err)
|
|
}
|
|
if n < 0 {
|
|
t.Errorf("invalid NumThread: %d", n)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Threads(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
n, err := p.NumThreads()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting NumThread error %v", err)
|
|
}
|
|
if n < 0 {
|
|
t.Errorf("invalid NumThread: %d", n)
|
|
}
|
|
|
|
ts, err := p.Threads()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting Threads error %v", err)
|
|
}
|
|
if len(ts) != int(n) {
|
|
t.Errorf("unexpected number of threads: %v vs %v", len(ts), n)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Name(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
n, err := p.Name()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting name error %v", err)
|
|
}
|
|
if !strings.Contains(n, "process.test") {
|
|
t.Errorf("invalid Name %s", n)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Long_Name_With_Spaces(t *testing.T) {
|
|
tmpdir, err := ioutil.TempDir("", "")
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp dir %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpdir) // clean up
|
|
tmpfilepath := filepath.Join(tmpdir, "loooong name with spaces.go")
|
|
tmpfile, err := os.Create(tmpfilepath)
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp file %v", err)
|
|
}
|
|
|
|
tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
|
|
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
|
|
tmpfile.Close()
|
|
t.Fatalf("unable to write temp file %v", err)
|
|
}
|
|
if err := tmpfile.Close(); err != nil {
|
|
t.Fatalf("unable to close temp file %v", err)
|
|
}
|
|
|
|
err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
|
|
if err != nil {
|
|
t.Fatalf("unable to build temp file %v", err)
|
|
}
|
|
|
|
cmd := exec.Command(tmpfile.Name() + ".exe")
|
|
|
|
assert.Nil(t, cmd.Start())
|
|
time.Sleep(100 * time.Millisecond)
|
|
p, err := NewProcess(int32(cmd.Process.Pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
|
|
n, err := p.Name()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("getting name error %v", err)
|
|
}
|
|
basename := filepath.Base(tmpfile.Name() + ".exe")
|
|
if basename != n {
|
|
t.Fatalf("%s != %s", basename, n)
|
|
}
|
|
cmd.Process.Kill()
|
|
}
|
|
|
|
func Test_Process_Long_Name(t *testing.T) {
|
|
tmpdir, err := ioutil.TempDir("", "")
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp dir %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpdir) // clean up
|
|
tmpfilepath := filepath.Join(tmpdir, "looooooooooooooooooooong.go")
|
|
tmpfile, err := os.Create(tmpfilepath)
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp file %v", err)
|
|
}
|
|
|
|
tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
|
|
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
|
|
tmpfile.Close()
|
|
t.Fatalf("unable to write temp file %v", err)
|
|
}
|
|
if err := tmpfile.Close(); err != nil {
|
|
t.Fatalf("unable to close temp file %v", err)
|
|
}
|
|
|
|
err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
|
|
if err != nil {
|
|
t.Fatalf("unable to build temp file %v", err)
|
|
}
|
|
|
|
cmd := exec.Command(tmpfile.Name() + ".exe")
|
|
|
|
assert.Nil(t, cmd.Start())
|
|
time.Sleep(100 * time.Millisecond)
|
|
p, err := NewProcess(int32(cmd.Process.Pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
|
|
n, err := p.Name()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("getting name error %v", err)
|
|
}
|
|
basename := filepath.Base(tmpfile.Name() + ".exe")
|
|
if basename != n {
|
|
t.Fatalf("%s != %s", basename, n)
|
|
}
|
|
cmd.Process.Kill()
|
|
}
|
|
|
|
func Test_Process_Name_Against_Python(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("only applies to posix")
|
|
}
|
|
py3Path, err := exec.LookPath("python3")
|
|
if err != nil {
|
|
t.Skipf("python3 not found: %s", err)
|
|
}
|
|
if out, err := exec.Command(py3Path, "-c", "import psutil").CombinedOutput(); err != nil {
|
|
t.Skipf("psutil not found for %s: %s", py3Path, out)
|
|
}
|
|
|
|
tmpdir, err := ioutil.TempDir("", "")
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp dir %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpdir) // clean up
|
|
tmpfilepath := filepath.Join(tmpdir, "looooooooooooooooooooong.py")
|
|
tmpfile, err := os.Create(tmpfilepath)
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp file %v", err)
|
|
}
|
|
tmpfilecontent := []byte("#!" + py3Path + "\nimport psutil, time\nprint(psutil.Process().name(), flush=True)\nwhile True:\n\ttime.sleep(1)")
|
|
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
|
|
tmpfile.Close()
|
|
t.Fatalf("unable to write temp file %v", err)
|
|
}
|
|
if err := tmpfile.Chmod(0o744); err != nil {
|
|
t.Fatalf("unable to chmod u+x temp file %v", err)
|
|
}
|
|
if err := tmpfile.Close(); err != nil {
|
|
t.Fatalf("unable to close temp file %v", err)
|
|
}
|
|
cmd := exec.Command(tmpfilepath)
|
|
outPipe, _ := cmd.StdoutPipe()
|
|
scanner := bufio.NewScanner(outPipe)
|
|
cmd.Start()
|
|
defer cmd.Process.Kill()
|
|
scanner.Scan()
|
|
pyName := scanner.Text() // first line printed by py3 script, its name
|
|
t.Logf("pyName %s", pyName)
|
|
p, err := NewProcess(int32(cmd.Process.Pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("getting process error %v", err)
|
|
}
|
|
name, err := p.Name()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("getting name error %v", err)
|
|
}
|
|
if pyName != name {
|
|
t.Fatalf("psutil and gopsutil process.Name() results differ: expected %s, got %s", pyName, name)
|
|
}
|
|
}
|
|
|
|
func Test_Process_Exe(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
n, err := p.Exe()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting Exe error %v", err)
|
|
}
|
|
if !strings.Contains(n, "process.test") {
|
|
t.Errorf("invalid Exe %s", n)
|
|
}
|
|
}
|
|
|
|
func Test_Process_CpuPercent(t *testing.T) {
|
|
p := testGetProcess()
|
|
_, err := p.Percent(0)
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
duration := time.Duration(1000) * time.Microsecond
|
|
time.Sleep(duration)
|
|
percent, err := p.Percent(0)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
|
|
numcpu := runtime.NumCPU()
|
|
// if percent < 0.0 || percent > 100.0*float64(numcpu) { // TODO
|
|
if percent < 0.0 {
|
|
t.Fatalf("CPUPercent value is invalid: %f, %d", percent, numcpu)
|
|
}
|
|
}
|
|
|
|
func Test_Process_CpuPercentLoop(t *testing.T) {
|
|
p := testGetProcess()
|
|
numcpu := runtime.NumCPU()
|
|
|
|
for i := 0; i < 2; i++ {
|
|
duration := time.Duration(100) * time.Microsecond
|
|
percent, err := p.Percent(duration)
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
// if percent < 0.0 || percent > 100.0*float64(numcpu) { // TODO
|
|
if percent < 0.0 {
|
|
t.Fatalf("CPUPercent value is invalid: %f, %d", percent, numcpu)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Test_Process_CreateTime(t *testing.T) {
|
|
if os.Getenv("CIRCLECI") == "true" {
|
|
t.Skip("Skip CI")
|
|
}
|
|
|
|
p := testGetProcess()
|
|
|
|
c, err := p.CreateTime()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("error %v", err)
|
|
}
|
|
|
|
if c < 1420000000 {
|
|
t.Errorf("process created time is wrong.")
|
|
}
|
|
|
|
gotElapsed := time.Since(time.Unix(int64(c/1000), 0))
|
|
maxElapsed := time.Duration(20 * time.Second)
|
|
|
|
if gotElapsed >= maxElapsed {
|
|
t.Errorf("this process has not been running for %v", gotElapsed)
|
|
}
|
|
}
|
|
|
|
func Test_Parent(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
c, err := p.Parent()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("error %v", err)
|
|
}
|
|
if c == nil {
|
|
t.Fatalf("could not get parent")
|
|
}
|
|
if c.Pid == 0 {
|
|
t.Fatalf("wrong parent pid")
|
|
}
|
|
}
|
|
|
|
func Test_Connections(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0") // dynamically get a random open port from OS
|
|
if err != nil {
|
|
t.Fatalf("unable to resolve localhost: %v", err)
|
|
}
|
|
l, err := net.ListenTCP(addr.Network(), addr)
|
|
if err != nil {
|
|
t.Fatalf("unable to listen on %v: %v", addr, err)
|
|
}
|
|
defer l.Close()
|
|
|
|
tcpServerAddr := l.Addr().String()
|
|
tcpServerAddrIP := strings.Split(tcpServerAddr, ":")[0]
|
|
tcpServerAddrPort, err := strconv.ParseUint(strings.Split(tcpServerAddr, ":")[1], 10, 32)
|
|
if err != nil {
|
|
t.Fatalf("unable to parse tcpServerAddr port: %v", err)
|
|
}
|
|
|
|
serverEstablished := make(chan struct{})
|
|
go func() { // TCP listening goroutine
|
|
conn, err := l.Accept()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
serverEstablished <- struct{}{}
|
|
_, err = ioutil.ReadAll(conn)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
|
|
conn, err := net.Dial("tcp", tcpServerAddr)
|
|
if err != nil {
|
|
t.Fatalf("unable to dial %v: %v", tcpServerAddr, err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Rarely the call to net.Dial returns before the server connection is
|
|
// established. Wait so that the test doesn't fail.
|
|
<-serverEstablished
|
|
|
|
c, err := p.Connections()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("error %v", err)
|
|
}
|
|
if len(c) == 0 {
|
|
t.Fatal("no connections found")
|
|
}
|
|
|
|
serverConnections := 0
|
|
for _, connection := range c {
|
|
if connection.Laddr.IP == tcpServerAddrIP && connection.Laddr.Port == uint32(tcpServerAddrPort) && connection.Raddr.Port != 0 {
|
|
if connection.Status != "ESTABLISHED" {
|
|
t.Fatalf("expected server connection to be ESTABLISHED, have %+v", connection)
|
|
}
|
|
serverConnections++
|
|
}
|
|
}
|
|
|
|
clientConnections := 0
|
|
for _, connection := range c {
|
|
if connection.Raddr.IP == tcpServerAddrIP && connection.Raddr.Port == uint32(tcpServerAddrPort) {
|
|
if connection.Status != "ESTABLISHED" {
|
|
t.Fatalf("expected client connection to be ESTABLISHED, have %+v", connection)
|
|
}
|
|
clientConnections++
|
|
}
|
|
}
|
|
|
|
if serverConnections != 1 { // two established connections, one for the server, the other for the client
|
|
t.Fatalf("expected 1 server connection, have %d.\nDetails: %+v", serverConnections, c)
|
|
}
|
|
|
|
if clientConnections != 1 { // two established connections, one for the server, the other for the client
|
|
t.Fatalf("expected 1 server connection, have %d.\nDetails: %+v", clientConnections, c)
|
|
}
|
|
}
|
|
|
|
func Test_Children(t *testing.T) {
|
|
p := testGetProcess()
|
|
|
|
var cmd *exec.Cmd
|
|
if runtime.GOOS == "windows" {
|
|
cmd = exec.Command("ping", "localhost", "-n", "4")
|
|
} else {
|
|
cmd = exec.Command("sleep", "3")
|
|
}
|
|
assert.Nil(t, cmd.Start())
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
c, err := p.Children()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("error %v", err)
|
|
}
|
|
if len(c) == 0 {
|
|
t.Fatalf("children is empty")
|
|
}
|
|
found := false
|
|
for _, child := range c {
|
|
if child.Pid == int32(cmd.Process.Pid) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("could not find child %d", cmd.Process.Pid)
|
|
}
|
|
}
|
|
|
|
func Test_Username(t *testing.T) {
|
|
myPid := os.Getpid()
|
|
currentUser, _ := user.Current()
|
|
myUsername := currentUser.Username
|
|
|
|
process, _ := NewProcess(int32(myPid))
|
|
pidUsername, err := process.Username()
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Equal(t, myUsername, pidUsername)
|
|
|
|
t.Log(pidUsername)
|
|
}
|
|
|
|
func Test_CPUTimes(t *testing.T) {
|
|
pid := os.Getpid()
|
|
process, err := NewProcess(int32(pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
|
|
spinSeconds := 0.2
|
|
cpuTimes0, err := process.Times()
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
|
|
// Spin for a duration of spinSeconds
|
|
t0 := time.Now()
|
|
tGoal := t0.Add(time.Duration(spinSeconds*1000) * time.Millisecond)
|
|
assert.Nil(t, err)
|
|
for time.Now().Before(tGoal) {
|
|
// This block intentionally left blank
|
|
}
|
|
|
|
cpuTimes1, err := process.Times()
|
|
assert.Nil(t, err)
|
|
|
|
if cpuTimes0 == nil || cpuTimes1 == nil {
|
|
t.FailNow()
|
|
}
|
|
measuredElapsed := cpuTimes1.Total() - cpuTimes0.Total()
|
|
message := fmt.Sprintf("Measured %fs != spun time of %fs\ncpuTimes0=%v\ncpuTimes1=%v",
|
|
measuredElapsed, spinSeconds, cpuTimes0, cpuTimes1)
|
|
assert.True(t, measuredElapsed > float64(spinSeconds)/5, message)
|
|
assert.True(t, measuredElapsed < float64(spinSeconds)*5, message)
|
|
}
|
|
|
|
func Test_OpenFiles(t *testing.T) {
|
|
fp, err := os.Open("process_test.go")
|
|
assert.Nil(t, err)
|
|
defer func() {
|
|
err := fp.Close()
|
|
assert.Nil(t, err)
|
|
}()
|
|
|
|
pid := os.Getpid()
|
|
p, err := NewProcess(int32(pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
|
|
v, err := p.OpenFiles()
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
assert.NotEmpty(t, v) // test always open files.
|
|
|
|
for _, vv := range v {
|
|
assert.NotEqual(t, "", vv.Path)
|
|
}
|
|
}
|
|
|
|
func Test_Kill(t *testing.T) {
|
|
var cmd *exec.Cmd
|
|
if runtime.GOOS == "windows" {
|
|
cmd = exec.Command("ping", "localhost", "-n", "4")
|
|
} else {
|
|
cmd = exec.Command("sleep", "3")
|
|
}
|
|
assert.Nil(t, cmd.Start())
|
|
time.Sleep(100 * time.Millisecond)
|
|
p, err := NewProcess(int32(cmd.Process.Pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
err = p.Kill()
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
cmd.Wait()
|
|
}
|
|
|
|
func Test_IsRunning(t *testing.T) {
|
|
var cmd *exec.Cmd
|
|
if runtime.GOOS == "windows" {
|
|
cmd = exec.Command("ping", "localhost", "-n", "2")
|
|
} else {
|
|
cmd = exec.Command("sleep", "1")
|
|
}
|
|
cmd.Start()
|
|
p, err := NewProcess(int32(cmd.Process.Pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
running, err := p.IsRunning()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("IsRunning error: %v", err)
|
|
}
|
|
if !running {
|
|
t.Fatalf("process should be found running")
|
|
}
|
|
cmd.Wait()
|
|
running, err = p.IsRunning()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("IsRunning error: %v", err)
|
|
}
|
|
if running {
|
|
t.Fatalf("process should NOT be found running")
|
|
}
|
|
}
|
|
|
|
func Test_Process_Environ(t *testing.T) {
|
|
tmpdir, err := ioutil.TempDir("", "")
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp dir %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpdir) // clean up
|
|
tmpfilepath := filepath.Join(tmpdir, "test.go")
|
|
tmpfile, err := os.Create(tmpfilepath)
|
|
if err != nil {
|
|
t.Fatalf("unable to create temp file %v", err)
|
|
}
|
|
|
|
tmpfilecontent := []byte("package main\nimport(\n\"time\"\n)\nfunc main(){\nfor range time.Tick(time.Second) {}\n}")
|
|
if _, err := tmpfile.Write(tmpfilecontent); err != nil {
|
|
tmpfile.Close()
|
|
t.Fatalf("unable to write temp file %v", err)
|
|
}
|
|
if err := tmpfile.Close(); err != nil {
|
|
t.Fatalf("unable to close temp file %v", err)
|
|
}
|
|
|
|
err = exec.Command("go", "build", "-o", tmpfile.Name()+".exe", tmpfile.Name()).Run()
|
|
if err != nil {
|
|
t.Fatalf("unable to build temp file %v", err)
|
|
}
|
|
|
|
cmd := exec.Command(tmpfile.Name() + ".exe")
|
|
|
|
cmd.Env = []string{"testkey=envvalue"}
|
|
|
|
assert.Nil(t, cmd.Start())
|
|
defer cmd.Process.Kill()
|
|
time.Sleep(100 * time.Millisecond)
|
|
p, err := NewProcess(int32(cmd.Process.Pid))
|
|
skipIfNotImplementedErr(t, err)
|
|
assert.Nil(t, err)
|
|
|
|
envs, err := p.Environ()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Errorf("getting environ error %v", err)
|
|
}
|
|
var envvarFound bool
|
|
for _, envvar := range envs {
|
|
if envvar == "testkey=envvalue" {
|
|
envvarFound = true
|
|
break
|
|
}
|
|
}
|
|
if !envvarFound {
|
|
t.Error("environment variable not found")
|
|
}
|
|
}
|
|
|
|
func Test_Process_Cwd(t *testing.T) {
|
|
myPid := os.Getpid()
|
|
currentWorkingDirectory, _ := os.Getwd()
|
|
|
|
process, _ := NewProcess(int32(myPid))
|
|
pidCwd, err := process.Cwd()
|
|
skipIfNotImplementedErr(t, err)
|
|
if err != nil {
|
|
t.Fatalf("getting cwd error %v", err)
|
|
}
|
|
pidCwd = strings.TrimSuffix(pidCwd, string(os.PathSeparator))
|
|
assert.Equal(t, currentWorkingDirectory, pidCwd)
|
|
|
|
t.Log(pidCwd)
|
|
}
|
|
|
|
func BenchmarkNewProcess(b *testing.B) {
|
|
checkPid := os.Getpid()
|
|
for i := 0; i < b.N; i++ {
|
|
NewProcess(int32(checkPid))
|
|
}
|
|
}
|
|
|
|
func BenchmarkProcessName(b *testing.B) {
|
|
p := testGetProcess()
|
|
for i := 0; i < b.N; i++ {
|
|
p.Name()
|
|
}
|
|
}
|
|
|
|
func BenchmarkProcessPpid(b *testing.B) {
|
|
p := testGetProcess()
|
|
for i := 0; i < b.N; i++ {
|
|
p.Ppid()
|
|
}
|
|
}
|