| package pstore |
| |
| import ( |
| "fmt" |
| "testing" |
| "testing/fstest" |
| "time" |
| ) |
| |
| func TestParseHeader(t *testing.T) { |
| var cases = []struct { |
| input string |
| expectedOut *pstoreDmesgHeader |
| }{ |
| {"Panic#2 Part30", &pstoreDmesgHeader{"Panic", 2, 30}}, |
| {"Oops#1 Part5", &pstoreDmesgHeader{"Oops", 1, 5}}, |
| // Random kernel output that is similar, but definitely not a dump header |
| {"<4>[2501503.489317] Oops: 0010 [#1] SMP NOPTI", nil}, |
| } |
| for i, c := range cases { |
| t.Run(fmt.Sprintf("Test#%d", i+1), func(t *testing.T) { |
| out, err := parseDmesgHeader(c.input) |
| switch { |
| case err != nil && c.expectedOut != nil: |
| t.Errorf("Failed parsing %q: %v", c.input, err) |
| case err == nil && c.expectedOut == nil: |
| t.Errorf("Successfully parsed %q, expected error", c.input) |
| case err != nil && c.expectedOut == nil: |
| case err == nil && c.expectedOut != nil: |
| if out.Part != c.expectedOut.Part { |
| t.Errorf("Expected part to be %d, got %d", c.expectedOut.Part, out.Part) |
| } |
| if out.Counter != c.expectedOut.Counter { |
| t.Errorf("Expected counter to be %d, got %d", c.expectedOut.Counter, out.Counter) |
| } |
| if out.Reason != c.expectedOut.Reason { |
| t.Errorf("Expected reason to be %q, got %q", c.expectedOut.Reason, out.Reason) |
| } |
| } |
| }) |
| } |
| } |
| |
| func TestGetKmsgDumps(t *testing.T) { |
| testTime1 := time.Date(2022, 06, 13, 1, 2, 3, 4, time.UTC) |
| testTime2 := time.Date(2020, 06, 13, 1, 2, 3, 4, time.UTC) |
| testTime3 := time.Date(2010, 06, 13, 1, 2, 3, 4, time.UTC) |
| cases := []struct { |
| name string |
| inputFS fstest.MapFS |
| expectErr bool |
| expectedDumps []KmsgDump |
| }{ |
| {"EmptyPstore", map[string]*fstest.MapFile{}, false, []KmsgDump{}}, |
| {"SingleDumpSingleFile", map[string]*fstest.MapFile{ |
| "dmesg-efi-165467917816002": {ModTime: testTime1, Data: []byte("Panic#2 Part1\ntest1\ntest2")}, |
| "yolo-efi-165467917816002": {ModTime: testTime1, Data: []byte("something totally unrelated")}, |
| }, false, []KmsgDump{{ |
| Reason: "Panic", |
| OccurredAt: testTime1, |
| Counter: 2, |
| Lines: []string{ |
| "test1", |
| "test2", |
| }, |
| }}}, |
| {"SingleDumpMultipleFiles", map[string]*fstest.MapFile{ |
| "dmesg-efi-165467917816002": {ModTime: testTime1, Data: []byte("Panic#2 Part1\ntest2\ntest3")}, |
| "dmesg-efi-165467917817002": {ModTime: testTime2, Data: []byte("Panic#2 Part2\ntest1")}, |
| }, false, []KmsgDump{{ |
| Reason: "Panic", |
| OccurredAt: testTime1, |
| Counter: 2, |
| Lines: []string{ |
| "test1", |
| "test2", |
| "test3", |
| }, |
| }}}, |
| {"MultipleDumpsMultipleFiles", map[string]*fstest.MapFile{ |
| "dmesg-efi-165467917816002": {ModTime: testTime1, Data: []byte("Panic#2 Part1\ntest2\ntest3")}, |
| "dmesg-efi-165467917817002": {ModTime: testTime2, Data: []byte("Panic#2 Part2\ntest1")}, |
| "dmesg-efi-265467917816002": {ModTime: testTime3, Data: []byte("Oops#1 Part1\noops3")}, |
| "dmesg-efi-265467917817002": {ModTime: testTime2, Data: []byte("Oops#1 Part2\noops1\noops2")}, |
| }, false, []KmsgDump{{ |
| Reason: "Panic", |
| OccurredAt: testTime1, |
| Counter: 2, |
| Lines: []string{ |
| "test1", |
| "test2", |
| "test3", |
| }, |
| }, { |
| Reason: "Oops", |
| OccurredAt: testTime3, |
| Counter: 1, |
| Lines: []string{ |
| "oops1", |
| "oops2", |
| "oops3", |
| }, |
| }}}, |
| } |
| for _, c := range cases { |
| t.Run(c.name, func(t *testing.T) { |
| dumps, err := getKmsgDumpsFromFS(c.inputFS) |
| switch { |
| case err == nil && c.expectErr: |
| t.Error("Expected error, but got none") |
| return |
| case err != nil && !c.expectErr: |
| t.Errorf("Got unexpected error: %v", err) |
| return |
| case err != nil && c.expectErr: |
| // Got expected error |
| return |
| case err == nil && !c.expectErr: |
| if len(dumps) != len(c.expectedDumps) { |
| t.Fatalf("Expected %d dumps, got %d", len(c.expectedDumps), len(dumps)) |
| } |
| for i, dump := range dumps { |
| if dump.OccurredAt != c.expectedDumps[i].OccurredAt { |
| t.Errorf("Dump %d expected to have occurred at %v, got %v", i, c.expectedDumps[i].OccurredAt, dump.OccurredAt) |
| } |
| if dump.Reason != c.expectedDumps[i].Reason { |
| t.Errorf("Expected reason in dump %d to be %v, got %v", i, c.expectedDumps[i].Reason, dump.Reason) |
| } |
| if dump.Counter != c.expectedDumps[i].Counter { |
| t.Errorf("Expected counter in dump %d to be %d, got %d", i, c.expectedDumps[i].Counter, dump.Counter) |
| } |
| if len(dump.Lines) != len(c.expectedDumps[i].Lines) { |
| t.Errorf("Expected number of lines in dump %d to be %d, got %d", i, len(c.expectedDumps[i].Lines), len(dump.Lines)) |
| } |
| for j := range dump.Lines { |
| if dump.Lines[j] != c.expectedDumps[i].Lines[j] { |
| t.Errorf("Expected line %d in dump %d to be %q, got %q", i, j, c.expectedDumps[i].Lines[j], dump.Lines[j]) |
| } |
| } |
| } |
| } |
| }) |
| } |
| } |