-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d81b2de
commit 955a084
Showing
3 changed files
with
247 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ $(BIN): *.go | |
test: | ||
@go vet | ||
@staticcheck | ||
@go test ./... -v | ||
|
||
.PHONY: clean | ||
clean: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io/fs" | ||
"testing" | ||
"testing/fstest" | ||
) | ||
|
||
// MockProcPidFS mimics the behavior of RealProcPidFS using MapFS. | ||
type MockProcPidFS struct { | ||
fs fs.FS | ||
pid int | ||
} | ||
|
||
// ReadFile reads the mock file system. | ||
func (p *MockProcPidFS) ReadFile(path string) ([]byte, error) { | ||
return fs.ReadFile(p.fs, path) // Uses MapFS to read files | ||
} | ||
|
||
// ReadLink reads the mock file system. | ||
func (p *MockProcPidFS) ReadLink(path string) (string, error) { | ||
// Workaround for https://github.com/golang/go/issues/49580 | ||
file, err := p.fs.Open(path) | ||
if err != nil { | ||
return "", &fs.PathError{Op: "open", Path: path, Err: err} | ||
} | ||
defer file.Close() | ||
|
||
stat, err := file.Stat() | ||
if err != nil { | ||
return "", &fs.PathError{Op: "stat", Path: path, Err: err} | ||
} | ||
|
||
if stat.Mode()&fs.ModeSymlink == 0 { | ||
return "", &fs.PathError{Op: "readlink", Path: path, Err: fmt.Errorf("not a symlink")} | ||
} | ||
|
||
// Since fstest.MapFS stores symlink target in `Data`, retrieve it directly | ||
if mapFile, ok := p.fs.(fstest.MapFS)[path]; ok { | ||
return string(mapFile.Data), nil | ||
} | ||
return "", &fs.PathError{Op: "readlink", Path: path, Err: fmt.Errorf("symlink target not found")} | ||
} | ||
|
||
func (p *MockProcPidFS) PID() int { | ||
return -1 | ||
} | ||
|
||
// Open is needed for fs.FS interface, we can mock it. | ||
func (p *MockProcPidFS) Open(name string) (fs.File, error) { | ||
return nil, fmt.Errorf("mock Open method") | ||
} | ||
|
||
// Close mimics closing a file, just return nil for the mock. | ||
func (p *MockProcPidFS) Close() error { | ||
return nil // No real file to close | ||
} | ||
|
||
// MockProcFS using fstest.MapFS with support for symlinks | ||
func mockProcFS(pid int, files map[string]string, symlinks map[string]string) fs.FS { | ||
mockFS := fstest.MapFS{} | ||
|
||
// Add regular files to the mockFS | ||
for path, content := range files { | ||
mockFS[path] = &fstest.MapFile{ | ||
Data: []byte(content), | ||
} | ||
} | ||
|
||
// Add symlinks to the mockFS | ||
for path, target := range symlinks { | ||
mockFS[path] = &fstest.MapFile{ | ||
Mode: fs.ModeSymlink, | ||
Data: []byte(target), | ||
} | ||
} | ||
|
||
return &MockProcPidFS{fs: mockFS, pid: pid} // Pass the correct pid here | ||
} | ||
|
||
// Test getDeleted | ||
func TestGetDeleted(t *testing.T) { | ||
mockFS := mockProcFS(1234, map[string]string{ | ||
"maps": "00400000-00452000 r-xp 00000000 fd:00 12345 /bin/bash (deleted)\n", | ||
}, nil) | ||
|
||
procFS := &MockProcPidFS{ | ||
fs: mockFS, | ||
pid: 1234, | ||
} | ||
|
||
files, err := getDeleted(procFS) | ||
if err != nil { | ||
t.Fatalf("Expected no error, got %v", err) | ||
} | ||
if len(files) != 1 || files[0] != "/bin/bash" { | ||
t.Errorf("Expected ['/bin/bash'], got %v", files) | ||
} | ||
} | ||
|
||
// Test getService | ||
func TestGetService(t *testing.T) { | ||
mockFS := mockProcFS(1234, map[string]string{ | ||
"cgroup": "/proc/777/cgroup:0::/system.slice/sshd.service\n", | ||
}, nil) | ||
|
||
procFS := &MockProcPidFS{ | ||
pid: 1234, | ||
fs: mockFS, | ||
} | ||
|
||
service := getService(procFS, false) | ||
if service != "sshd" { | ||
t.Errorf("Expected 'sshd', got '%s'", service) | ||
} | ||
} | ||
|
||
// Test getCommand | ||
func TestGetCommand(t *testing.T) { | ||
mockFS := mockProcFS(1234, map[string]string{ | ||
"cmdline": "/bin/bash\x00--version\x00\x00", | ||
}, nil) | ||
|
||
procFS := &MockProcPidFS{ | ||
pid: 1234, | ||
fs: mockFS, | ||
} | ||
|
||
command, err := getCommand(procFS, true, "bash") | ||
if err != nil { | ||
t.Fatalf("Expected no error, got %v", err) | ||
} | ||
expected := "/bin/bash --version" | ||
if command != expected { | ||
t.Errorf("Expected '%s', got '%s'", expected, command) | ||
} | ||
} | ||
|
||
// Test parseStatusField | ||
func TestParseStatusField(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
data string | ||
field string | ||
expected string | ||
}{ | ||
{ | ||
name: "Valid Uid Field", | ||
data: "Name:\tbash\nPPid:\t1\nUid:\t1000\t1001\t1002\t1003\n", | ||
field: "Uid", | ||
expected: "1000\t1001\t1002\t1003", | ||
}, | ||
{ | ||
name: "Valid Name Field", | ||
data: "Name:\tbash\nPPid:\t1\nUid:\t1000\t1000\t1000\t1000\n", | ||
field: "Name", | ||
expected: "bash", | ||
}, | ||
{ | ||
name: "Valid PPid Field", | ||
data: "Name:\tbash\nPPid:\t1\nUid:\t1000\t1000\t1000\t1000\n", | ||
field: "PPid", | ||
expected: "1", | ||
}, | ||
{ | ||
name: "Missing Field", | ||
data: "Name:\tbash\nPPid:\t1\nUid:\t1000\t1000\t1000\t1000\n", | ||
field: "Gid", | ||
expected: "", | ||
}, | ||
{ | ||
name: "Empty Data", | ||
data: "", | ||
field: "Uid", | ||
expected: "", | ||
}, | ||
{ | ||
name: "Malformed Data", | ||
data: "Name\nPPid:\t1\nUid:\t1000\n", | ||
field: "Uid", | ||
expected: "1000", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
field := parseStatusField(tt.data, tt.field) | ||
if field != tt.expected { | ||
t.Errorf("For field '%s': expected '%s', got '%s'", tt.field, tt.expected, field) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// Test getProcessInfo | ||
func TestGetProcessInfo(t *testing.T) { | ||
mockFS := mockProcFS(1234, map[string]string{ | ||
"cgroup": "/proc/777/cgroup:0::/system.slice/sshd.service\n", | ||
"cmdline": "/bin/bash\x00--version\x00\x00", | ||
"maps": "00400000-00452000 r-xp 00000000 fd:00 12345 /bin/bash (deleted)\n", | ||
"status": "Name:\tbash\nPPid:\t1\nUid:\t1000\t1000\t1000\t1000\n", | ||
}, nil) | ||
|
||
procFS := &MockProcPidFS{ | ||
pid: 1234, | ||
fs: mockFS, | ||
} | ||
|
||
info, err := getProcessInfo(procFS, true, false) | ||
if err != nil { | ||
t.Fatalf("Expected no error, got %v", err) | ||
} | ||
if info.Pid != 1234 { | ||
t.Errorf("Expected PID 1234, got %d", info.Pid) | ||
} | ||
if info.Ppid != 1 { | ||
t.Errorf("Expected PPID 1, got %d", info.Ppid) | ||
} | ||
if info.Uid != 1000 { | ||
t.Errorf("Expected UID 1000, got %d", info.Uid) | ||
} | ||
if info.Command != "/bin/bash --version" { | ||
t.Errorf("Expected command '/bin/bash --version', got '%s'", info.Command) | ||
} | ||
if len(info.Deleted) != 1 || info.Deleted[0] != "/bin/bash" { | ||
t.Errorf("Expected Deleted ['/bin/bash'], got %v", info.Deleted) | ||
} | ||
} | ||
|
||
// Test getUser | ||
func TestGetUser(t *testing.T) { | ||
user := getUser(0) | ||
if user != "root" { | ||
t.Errorf("Expected 'root', got '%s'", user) | ||
} | ||
|
||
user = getUser(99999) // Invalid UID | ||
if user != "-" { | ||
t.Errorf("Expected '-', got '%s'", user) | ||
} | ||
} |