summaryrefslogtreecommitdiff
path: root/internal/tui/pidpicker/proclist_test.go
blob: d5f6cfda555916f0eb1136e7640279f948427648 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package pidpicker

import (
	"os"
	"path/filepath"
	"testing"
)

func TestScanProcessesFrom(t *testing.T) {
	root := t.TempDir()

	mkProc(t, root, "12", "12 (bash) S 1 1 1 0", "bash\x00-l\x00")
	mkProc(t, root, "99", "99 (sshd) S 1 1 1 0", "")
	mkProc(t, root, "bad", "bad data", "ignored")

	processes, err := scanProcessesFrom(root)
	if err != nil {
		t.Fatalf("scanProcessesFrom returned error: %v", err)
	}

	if len(processes) != 2 {
		t.Fatalf("expected 2 valid processes, got %d", len(processes))
	}

	if processes[0].Pid != 12 || processes[0].Comm != "bash" || processes[0].Cmdline != "bash -l" {
		t.Fatalf("unexpected first process: %+v", processes[0])
	}
	if processes[0].ParentPID != 12 {
		t.Fatalf("expected first process parent pid=12, got %d", processes[0].ParentPID)
	}
	if processes[1].Pid != 99 || processes[1].Comm != "sshd" || processes[1].Cmdline != "" {
		t.Fatalf("unexpected second process: %+v", processes[1])
	}
}

func TestParseCommFromStatInvalid(t *testing.T) {
	cases := []string{
		"",
		"100 no-parens",
		"100 () S 1 1 1 0",
		"100 (unterminated S 1 1 1 0",
	}

	for _, tc := range cases {
		if _, err := parseCommFromStat(tc); err == nil {
			t.Fatalf("expected parseCommFromStat to fail for %q", tc)
		}
	}
}

func TestNormalizeCmdline(t *testing.T) {
	got := normalizeCmdline([]byte("python\x00main.py\x00"))
	if got != "python main.py" {
		t.Fatalf("unexpected normalized cmdline: %q", got)
	}

	if normalizeCmdline(nil) != "" {
		t.Fatalf("expected empty cmdline for nil bytes")
	}
}

func TestScanThreadsFrom(t *testing.T) {
	root := t.TempDir()
	procDir := filepath.Join(root, "12")
	if err := os.MkdirAll(filepath.Join(procDir, "task", "12"), 0o755); err != nil {
		t.Fatalf("mkdir: %v", err)
	}
	if err := os.MkdirAll(filepath.Join(procDir, "task", "120"), 0o755); err != nil {
		t.Fatalf("mkdir: %v", err)
	}
	if err := os.WriteFile(filepath.Join(procDir, "cmdline"), []byte("bash\x00-l\x00"), 0o644); err != nil {
		t.Fatalf("write cmdline: %v", err)
	}
	if err := os.WriteFile(filepath.Join(procDir, "task", "12", "comm"), []byte("bash-main\n"), 0o644); err != nil {
		t.Fatalf("write comm: %v", err)
	}
	if err := os.WriteFile(filepath.Join(procDir, "task", "120", "comm"), []byte("bash-worker\n"), 0o644); err != nil {
		t.Fatalf("write comm: %v", err)
	}

	threads, err := scanThreadsFrom(root, 12)
	if err != nil {
		t.Fatalf("scanThreadsFrom returned error: %v", err)
	}
	if len(threads) != 2 {
		t.Fatalf("expected 2 threads, got %d", len(threads))
	}
	if threads[0].Pid != 12 || threads[0].Comm != "bash-main" || threads[0].Cmdline != "bash -l" {
		t.Fatalf("unexpected first thread: %+v", threads[0])
	}
	if threads[0].ParentPID != 12 {
		t.Fatalf("unexpected first thread parent pid: %d", threads[0].ParentPID)
	}
	if threads[1].Pid != 120 || threads[1].Comm != "bash-worker" || threads[1].Cmdline != "bash -l" {
		t.Fatalf("unexpected second thread: %+v", threads[1])
	}
	if threads[1].ParentPID != 12 {
		t.Fatalf("unexpected second thread parent pid: %d", threads[1].ParentPID)
	}
}

func mkProc(t *testing.T, root, pid, stat, cmdline string) {
	t.Helper()
	dir := filepath.Join(root, pid)
	if err := os.MkdirAll(dir, 0o755); err != nil {
		t.Fatalf("mkdir %s: %v", dir, err)
	}
	if err := os.WriteFile(filepath.Join(dir, "stat"), []byte(stat), 0o644); err != nil {
		t.Fatalf("write stat: %v", err)
	}
	if err := os.WriteFile(filepath.Join(dir, "cmdline"), []byte(cmdline), 0o644); err != nil {
		t.Fatalf("write cmdline: %v", err)
	}
}