36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
//go:build linux
|
|
|
|
package proc
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pranshuparmar/witr/pkg/model"
|
|
)
|
|
|
|
func findListedPID(procs []model.Process, pid int) *model.Process {
|
|
for i := range procs {
|
|
if procs[i].PID == pid {
|
|
return &procs[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TestListProcessesSelf checks the /proc-based ListProcesses (which replaced the
|
|
// `ps -axo` fork) produces self with plausible columns, and that the values it
|
|
// computes from /proc track what ps reports.
|
|
func TestListProcessesSelf(t *testing.T) {
|
|
procs, err := ListProcesses()
|
|
if err != nil {
|
|
t.Fatalf("ListProcesses: %v", err)
|
|
}
|
|
|
|
me := findListedPID(procs, os.Getpid())
|
|
if me == nil {
|
|
t.Fatalf("ListProcesses did not include self (pid %d)", os.Getpid())
|
|
}
|
|
if me.MemoryRSS == 0 {
|
|
t.Error("self RSS should be > 0")
|
|
}
|
|
if me.Command == "" {
|
|
t.Error("self Command should be non-empty")
|
|
}
|
|
if me.User == "" {
|
|
t.Error("self User should be non-empty")
|
|
}
|
|
if me.PPID == 0 {
|
|
t.Error("self PPID should be non-zero")
|
|
}
|
|
if me.CPUPercent < 0 || me.CPUPercent > float64(runtime.NumCPU())*100+1 {
|
|
t.Errorf("self CPU%% = %v is implausible", me.CPUPercent)
|
|
}
|
|
|
|
// RSS parity with ps (50% tolerance — resident memory drifts between the two reads).
|
|
if out, err := exec.Command("ps", "-p", strconv.Itoa(os.Getpid()), "-o", "rss=").Output(); err == nil {
|
|
if psKB, perr := strconv.ParseFloat(strings.TrimSpace(string(out)), 64); perr == nil && psKB > 0 {
|
|
psRSS := psKB * 1024
|
|
if ratio := float64(me.MemoryRSS) / psRSS; ratio < 0.5 || ratio > 2.0 {
|
|
t.Errorf("RSS parity off: witr=%d bytes, ps=%.0f bytes (ratio %.2f)", me.MemoryRSS, psRSS, ratio)
|
|
}
|
|
}
|
|
}
|
|
|
|
// PID 1 is long-lived, so its lifetime-average CPU% should closely track ps's
|
|
// (same formula, same /proc source) — a gross formula error would blow past 1%.
|
|
if p1 := findListedPID(procs, 1); p1 != nil {
|
|
if out, err := exec.Command("ps", "-p", "1", "-o", "%cpu=").Output(); err == nil {
|
|
if psCPU, perr := strconv.ParseFloat(strings.TrimSpace(string(out)), 64); perr == nil {
|
|
if diff := p1.CPUPercent - psCPU; diff > 1.0 || diff < -1.0 {
|
|
t.Errorf("PID 1 CPU%% parity: witr=%.3f, ps=%.3f", p1.CPUPercent, psCPU)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|