Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(pkg/process): add more unit test cases #356

Merged
merged 1 commit into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions pkg/dmesg/dmesg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,178 @@ func TestParseCtime(t *testing.T) {
})
}
}

func TestLogLineIsEmpty(t *testing.T) {
tests := []struct {
name string
line LogLine
want bool
}{
{
name: "completely empty",
line: LogLine{},
want: true,
},
{
name: "only timestamp",
line: LogLine{
Timestamp: time.Now(),
},
want: false,
},
{
name: "only facility",
line: LogLine{
Facility: "kern",
},
want: false,
},
{
name: "only level",
line: LogLine{
Level: "info",
},
want: false,
},
{
name: "only content",
line: LogLine{
Content: "test message",
},
want: false,
},
{
name: "only error",
line: LogLine{
Error: "test error",
},
want: false,
},
{
name: "all fields empty strings",
line: LogLine{
Facility: "",
Level: "",
Content: "",
Error: "",
},
want: true,
},
{
name: "all fields populated",
line: LogLine{
Timestamp: time.Now(),
Facility: "kern",
Level: "info",
Content: "test message",
Error: "",
},
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.line.IsEmpty(); got != tt.want {
t.Errorf("LogLine.IsEmpty() = %v, want %v", got, tt.want)
}
})
}
}

func TestParseISOtimeWithErrorEdgeCases(t *testing.T) {
tests := []struct {
name string
line []byte
want time.Time
wantLine []byte
wantErr bool
}{
{
name: "microseconds overflow",
line: []byte("2024-11-15T12:02:03,9999999+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "invalid timezone offset",
line: []byte("2024-11-15T12:02:03,561522+25:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "invalid month",
line: []byte("2024-13-15T12:02:03,561522+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "invalid day",
line: []byte("2024-11-32T12:02:03,561522+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "invalid hour",
line: []byte("2024-11-15T24:02:03,561522+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "invalid minute",
line: []byte("2024-11-15T12:60:03,561522+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "invalid second",
line: []byte("2024-11-15T12:02:60,561522+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "missing T separator",
line: []byte("2024-11-15 12:02:03,561522+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "missing microseconds",
line: []byte("2024-11-15T12:02:03+00:00 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
{
name: "missing timezone",
line: []byte("2024-11-15T12:02:03,561522 abc"),
want: time.Time{},
wantLine: nil,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, line, err := ParseISOtimeWithError(tt.line)
if (err != nil) != tt.wantErr {
t.Errorf("ParseISOtimeWithError() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !got.Equal(tt.want) {
t.Errorf("ParseISOtimeWithError() got = %v, want %v", got, tt.want)
}
if !bytes.Equal(line, tt.wantLine) {
t.Errorf("ParseISOtimeWithError() line = %v, want %v", string(line), string(tt.wantLine))
}
})
}
}
163 changes: 154 additions & 9 deletions pkg/dmesg/watcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,57 @@ func TestParseDmesgLine(t *testing.T) {
},
wantTime: true,
},
{
name: "nvidia xid error",
input: "kern :warn : 2025-01-21T04:26:49,803751+00:00 NVRM: Xid (PCI:0000:38:00): 13, pid='<unknown>', name=<unknown>, Graphics SM Global Exception on (GPC 9, TPC 1, SM 1): Multiple Warp Errors",
want: LogLine{
Timestamp: time.Date(2025, time.January, 21, 4, 26, 49, 803751000, time.UTC),
Facility: "kern",
Level: "warn",
Content: "NVRM: Xid (PCI:0000:38:00): 13, pid='<unknown>', name=<unknown>, Graphics SM Global Exception on (GPC 9, TPC 1, SM 1): Multiple Warp Errors",
},
wantTime: true,
},
{
name: "multiple colons in message",
input: "kern :info : 2025-01-21T04:26:49,803751+00:00 systemd[1]: Starting: test:service:name",
want: LogLine{
Timestamp: time.Date(2025, time.January, 21, 4, 26, 49, 803751000, time.UTC),
Facility: "kern",
Level: "info",
Content: "systemd[1]: Starting: test:service:name",
},
wantTime: true,
},
{
name: "empty level with facility",
input: "kern : : 2025-01-21T04:26:49,803751+00:00 test message",
want: LogLine{
Timestamp: time.Date(2025, time.January, 21, 4, 26, 49, 803751000, time.UTC),
Facility: "kern",
Content: "test message",
},
wantTime: true,
},
{
name: "invalid timestamp format but valid prefix",
input: "kern :warn : 2025-01-21T04:26:49 test message",
want: LogLine{
Timestamp: time.Time{},
Content: "kern :warn : 2025-01-21T04:26:49 test message",
},
wantTime: false,
},
{
name: "timestamp parse error",
input: "kern :warn : 2025-01-21T25:61:99,803751+00:00 test message",
want: LogLine{
Facility: "",
Level: "",
Content: "kern :warn : 2025-01-21T25:61:99,803751+00:00 test message",
},
wantTime: false,
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -417,25 +468,119 @@ func TestWatchMultipleCommands(t *testing.T) {
}

func TestWatchWithError(t *testing.T) {
tests := []struct {
name string
commands [][]string
wantError bool
wantContent string
}{
{
name: "invalid command",
commands: [][]string{{"invalidcommand"}},
wantError: true,
wantContent: "not found",
},
{
name: "command with no output",
commands: [][]string{{"true"}},
wantError: false,
wantContent: "",
},
{
name: "command that fails",
commands: [][]string{{"false"}},
wantError: false,
wantContent: "",
},
{
name: "multiple failing commands",
commands: [][]string{
{"false"},
{"invalidcommand"},
{"cat", "nonexistentfile"},
},
wantError: true,
wantContent: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w, err := NewWatcherWithCommands(tt.commands)
if err != nil {
if !tt.wantError {
t.Errorf("NewWatcherWithCommands() error = %v, wantError %v", err, tt.wantError)
}
return
}
defer w.Close()

ch := w.Watch()
var errorSeen bool
var lastError string

for line := range ch {
if line.Error != "" {
errorSeen = true
lastError = line.Error
}
}

if tt.wantError && !errorSeen {
t.Error("expected to see an error line but got none")
}
if tt.wantContent != "" && !strings.Contains(lastError, tt.wantContent) {
t.Errorf("expected error to contain %q, got %q", tt.wantContent, lastError)
}
})
}
}

func TestWatcherCloseMultipleTimes(t *testing.T) {
w, err := NewWatcherWithCommands([][]string{{"sleep", "10"}})
if err != nil {
t.Fatalf("failed to create watcher: %v", err)
}

ch := w.Watch()
// Give some time for goroutines to start
time.Sleep(100 * time.Millisecond)

// Call Close multiple times to ensure it's safe
for i := 0; i < 3; i++ {
w.Close()
}

// Wait for channel to close
for range ch {
// Drain the channel
}

// Verify channel is closed by trying to read again
_, ok := <-ch
if ok {
t.Error("channel should be closed")
}
}

func TestWatchWithLongOutput(t *testing.T) {
// Generate a command that produces a lot of output
w, err := NewWatcherWithCommands([][]string{
{"cat nonexistentfile"},
{"bash", "-c", "for i in {1..1000}; do echo $i; done"},
})
if err != nil {
t.Fatalf("failed to create watcher: %v", err)
}
defer w.Close()

ch := w.Watch()

var errorSeen bool
for line := range ch {
if strings.Contains(line.Content, "No such file or directory") {
errorSeen = true
}
count := 0
for range ch {
count++
}

if !errorSeen {
t.Error("expected to see an error line")
if count == 0 {
t.Error("expected to receive some output")
}
}

Expand Down
Loading