项目文件夹

文件
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

120 行
3.1 KiB
Go

//go:build !windows
package tui
import (
"bytes"
"fmt"
"os"
"os/exec"
"runtime"
"testing"
"time"
)
func TestSetNiceRangeGuard(t *testing.T) {
// Out-of-range values are rejected before any syscall is attempted.
if err := setNice(os.Getpid(), 100); err == nil {
t.Error("setNice(+100) should be rejected as out of range")
}
if err := setNice(os.Getpid(), -100); err == nil {
t.Error("setNice(-100) should be rejected as out of range")
}
}
// startSleeper spawns a long-lived child to receive signals.
func startSleeper(t *testing.T) *exec.Cmd {
t.Helper()
c := exec.Command("sleep", "60")
if err := c.Start(); err != nil {
t.Skipf("cannot spawn child process: %v", err)
}
return c
}
// waitExit reports whether the child reaps within the timeout (i.e. the signal
// actually terminated it).
func waitExit(c *exec.Cmd, timeout time.Duration) bool {
done := make(chan struct{})
go func() { _ = c.Wait(); close(done) }()
select {
case <-done:
return true
case <-time.After(timeout):
return false
}
}
func TestSignalActionsTerminate(t *testing.T) {
t.Run("SIGTERM exits the process", func(t *testing.T) {
c := startSleeper(t)
if err := termProcess(c.Process.Pid); err != nil {
t.Fatalf("termProcess: %v", err)
}
if !waitExit(c, 5*time.Second) {
_ = c.Process.Kill()
t.Fatal("process did not exit after SIGTERM")
}
})
t.Run("SIGKILL exits the process", func(t *testing.T) {
c := startSleeper(t)
if err := killProcess(c.Process.Pid); err != nil {
t.Fatalf("killProcess: %v", err)
}
if !waitExit(c, 5*time.Second) {
t.Fatal("process did not exit after SIGKILL")
}
})
}
func TestSignalActionsPauseResume(t *testing.T) {
c := startSleeper(t)
defer func() {
_ = c.Process.Kill()
_ = c.Wait()
}()
if err := pauseProcess(c.Process.Pid); err != nil {
t.Fatalf("pauseProcess: %v", err)
}
if !waitForState(c.Process.Pid, 'T', 2*time.Second) {
t.Error("process did not reach the stopped (T) state after SIGSTOP")
}
if err := resumeProcess(c.Process.Pid); err != nil {
t.Fatalf("resumeProcess: %v", err)
}
}
func TestSignalDeadProcessErrors(t *testing.T) {
c := startSleeper(t)
pid := c.Process.Pid
_ = c.Process.Kill()
_ = c.Wait() // reap, so the PID is no longer signalable
if err := resumeProcess(pid); err == nil {
t.Error("signalling a reaped PID should fail")
}
}
// waitForState polls /proc for the given process state on Linux. On other
// platforms it returns true (the /proc check is not meaningful there), so the
// pause assertion is only enforced where it can be observed.
func waitForState(pid int, want byte, timeout time.Duration) bool {
if runtime.GOOS != "linux" {
return true
}
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if data, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid)); err == nil {
// Format: "pid (comm) state ..."; comm may contain spaces and
// parens, so the state is the char two positions after the last ')'.
if i := bytes.LastIndexByte(data, ')'); i >= 0 && i+2 < len(data) && data[i+2] == want {
return true
}
}
time.Sleep(10 * time.Millisecond)
}
return false
}