mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-09 03:09:30 +08:00
Merge branch 'master' into rc/5.2
解决合并冲突: - .github/workflows/release.yml: 统一使用codeagent-wrapper目录和输出名称 - codeagent-wrapper/main_test.go: 合并HEAD的新测试(Claude/Gemini事件解析)和master的测试改进 - 接受master的新文件: .gitignore, process_check_* 到codeagent-wrapper目录 - 确认删除: codex-wrapper/main.go (已重命名) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
217
codeagent-wrapper/process_check_test.go
Normal file
217
codeagent-wrapper/process_check_test.go
Normal file
@@ -0,0 +1,217 @@
|
||||
//go:build unix || darwin || linux
|
||||
// +build unix darwin linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestIsProcessRunning(t *testing.T) {
|
||||
t.Run("current process", func(t *testing.T) {
|
||||
if !isProcessRunning(os.Getpid()) {
|
||||
t.Fatalf("expected current process (pid=%d) to be running", os.Getpid())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fake pid", func(t *testing.T) {
|
||||
const nonexistentPID = 1 << 30
|
||||
if isProcessRunning(nonexistentPID) {
|
||||
t.Fatalf("expected pid %d to be reported as not running", nonexistentPID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("terminated process", func(t *testing.T) {
|
||||
pid := exitedProcessPID(t)
|
||||
if isProcessRunning(pid) {
|
||||
t.Fatalf("expected exited child process (pid=%d) to be reported as not running", pid)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("boundary values", func(t *testing.T) {
|
||||
if isProcessRunning(0) {
|
||||
t.Fatalf("pid 0 should never be treated as running")
|
||||
}
|
||||
if isProcessRunning(-42) {
|
||||
t.Fatalf("negative pid should never be treated as running")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("find process error", func(t *testing.T) {
|
||||
original := findProcess
|
||||
defer func() { findProcess = original }()
|
||||
|
||||
mockErr := errors.New("findProcess failure")
|
||||
findProcess = func(pid int) (*os.Process, error) {
|
||||
return nil, mockErr
|
||||
}
|
||||
|
||||
if isProcessRunning(1234) {
|
||||
t.Fatalf("expected false when os.FindProcess fails")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func exitedProcessPID(t *testing.T) int {
|
||||
t.Helper()
|
||||
|
||||
var cmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command("cmd", "/c", "exit 0")
|
||||
} else {
|
||||
cmd = exec.Command("sh", "-c", "exit 0")
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("failed to start helper process: %v", err)
|
||||
}
|
||||
pid := cmd.Process.Pid
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Fatalf("helper process did not exit cleanly: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
return pid
|
||||
}
|
||||
|
||||
func TestRunProcessCheckSmoke(t *testing.T) {
|
||||
t.Run("current process", func(t *testing.T) {
|
||||
if !isProcessRunning(os.Getpid()) {
|
||||
t.Fatalf("expected current process (pid=%d) to be running", os.Getpid())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fake pid", func(t *testing.T) {
|
||||
const nonexistentPID = 1 << 30
|
||||
if isProcessRunning(nonexistentPID) {
|
||||
t.Fatalf("expected pid %d to be reported as not running", nonexistentPID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("boundary values", func(t *testing.T) {
|
||||
if isProcessRunning(0) {
|
||||
t.Fatalf("pid 0 should never be treated as running")
|
||||
}
|
||||
if isProcessRunning(-42) {
|
||||
t.Fatalf("negative pid should never be treated as running")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("find process error", func(t *testing.T) {
|
||||
original := findProcess
|
||||
defer func() { findProcess = original }()
|
||||
|
||||
mockErr := errors.New("findProcess failure")
|
||||
findProcess = func(pid int) (*os.Process, error) {
|
||||
return nil, mockErr
|
||||
}
|
||||
|
||||
if isProcessRunning(1234) {
|
||||
t.Fatalf("expected false when os.FindProcess fails")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetProcessStartTimeReadsProcStat(t *testing.T) {
|
||||
pid := 4321
|
||||
boot := time.Unix(1_710_000_000, 0)
|
||||
startTicks := uint64(4500)
|
||||
|
||||
statFields := make([]string, 25)
|
||||
for i := range statFields {
|
||||
statFields[i] = strconv.Itoa(i + 1)
|
||||
}
|
||||
statFields[19] = strconv.FormatUint(startTicks, 10)
|
||||
statContent := fmt.Sprintf("%d (%s) %s", pid, "cmd with space", strings.Join(statFields, " "))
|
||||
|
||||
stubReadFile(t, func(path string) ([]byte, error) {
|
||||
switch path {
|
||||
case fmt.Sprintf("/proc/%d/stat", pid):
|
||||
return []byte(statContent), nil
|
||||
case "/proc/stat":
|
||||
return []byte(fmt.Sprintf("cpu 0 0 0 0\nbtime %d\n", boot.Unix())), nil
|
||||
default:
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
})
|
||||
|
||||
got := getProcessStartTime(pid)
|
||||
want := boot.Add(time.Duration(startTicks/100) * time.Second)
|
||||
if !got.Equal(want) {
|
||||
t.Fatalf("getProcessStartTime() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProcessStartTimeInvalidData(t *testing.T) {
|
||||
pid := 99
|
||||
stubReadFile(t, func(path string) ([]byte, error) {
|
||||
switch path {
|
||||
case fmt.Sprintf("/proc/%d/stat", pid):
|
||||
return []byte("garbage"), nil
|
||||
case "/proc/stat":
|
||||
return []byte("btime not-a-number\n"), nil
|
||||
default:
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
})
|
||||
|
||||
if got := getProcessStartTime(pid); !got.IsZero() {
|
||||
t.Fatalf("invalid /proc data should return zero time, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBootTimeParsesBtime(t *testing.T) {
|
||||
const bootSec = 1_711_111_111
|
||||
stubReadFile(t, func(path string) ([]byte, error) {
|
||||
if path != "/proc/stat" {
|
||||
return nil, os.ErrNotExist
|
||||
}
|
||||
content := fmt.Sprintf("intr 0\nbtime %d\n", bootSec)
|
||||
return []byte(content), nil
|
||||
})
|
||||
|
||||
got := getBootTime()
|
||||
want := time.Unix(bootSec, 0)
|
||||
if !got.Equal(want) {
|
||||
t.Fatalf("getBootTime() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBootTimeInvalidData(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
content string
|
||||
}{
|
||||
{"missing", "cpu 0 0 0 0"},
|
||||
{"malformed", "btime abc"},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stubReadFile(t, func(string) ([]byte, error) {
|
||||
return []byte(tt.content), nil
|
||||
})
|
||||
if got := getBootTime(); !got.IsZero() {
|
||||
t.Fatalf("getBootTime() unexpected value for %s: %v", tt.name, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func stubReadFile(t *testing.T, fn func(string) ([]byte, error)) {
|
||||
t.Helper()
|
||||
original := readFileFn
|
||||
readFileFn = fn
|
||||
t.Cleanup(func() {
|
||||
readFileFn = original
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user