package nvidia import ( "testing" "time" "git.mchus.pro/mchus/logpile/internal/models" "git.mchus.pro/mchus/logpile/internal/parser" ) func TestParseVerboseRunTestStartTimes(t *testing.T) { content := []byte(` 2026-01-22 09:11:32,458 - Testing nvswitch 2026-01-22 09:45:36,016 - Testing gpu_fieldiag `) got := parseVerboseRunTestStartTimes(content) nvs := got["nvswitch"] if nvs.IsZero() { t.Fatalf("expected nvswitch timestamp") } gpu := got["gpu_fieldiag"] if gpu.IsZero() { t.Fatalf("expected gpu_fieldiag timestamp") } if nvs.Format(time.RFC3339) != "2026-01-22T09:11:32Z" { t.Fatalf("unexpected nvswitch timestamp: %s", nvs.Format(time.RFC3339)) } if gpu.Format(time.RFC3339) != "2026-01-22T09:45:36Z" { t.Fatalf("unexpected gpu_fieldiag timestamp: %s", gpu.Format(time.RFC3339)) } } func TestParseRunLogTestStartTimes(t *testing.T) { content := []byte(` Start time Thu, 22 Jan 2026 07:42:26 Testing gpumem FAILED [ 26:12s ] Testing gpustress OK [ 7:10s ] Testing nvswitch OK [ 9:25s ] `) got := parseRunLogTestStartTimes(content) if got["gpumem"].Format(time.RFC3339) != "2026-01-22T07:42:26Z" { t.Fatalf("unexpected gpumem start: %s", got["gpumem"].Format(time.RFC3339)) } if got["gpustress"].Format(time.RFC3339) != "2026-01-22T08:08:38Z" { t.Fatalf("unexpected gpustress start: %s", got["gpustress"].Format(time.RFC3339)) } if got["nvswitch"].Format(time.RFC3339) != "2026-01-22T08:15:48Z" { t.Fatalf("unexpected nvswitch start: %s", got["nvswitch"].Format(time.RFC3339)) } } func TestApplyGPUAndNVSwitchCheckTimes(t *testing.T) { gpuTs := time.Date(2026, 1, 22, 9, 45, 36, 0, time.UTC) nvsTs := time.Date(2026, 1, 22, 9, 11, 32, 0, time.UTC) result := &models.AnalysisResult{ Hardware: &models.HardwareConfig{ GPUs: []models.GPU{ {Slot: "GPUSXM5", Status: "FAIL"}, }, PCIeDevices: []models.PCIeDevice{ {Slot: "NVSWITCH0", DeviceClass: "NVSwitch", Status: "PASS"}, {Slot: "NIC0", DeviceClass: "NetworkController", Status: "PASS"}, }, }, } ApplyGPUAndNVSwitchCheckTimes(result, componentCheckTimes{ GPUBySlot: map[string]time.Time{"GPUSXM5": gpuTs}, NVSwitchBySlot: map[string]time.Time{"NVSWITCH0": nvsTs}, }) if got := result.Hardware.GPUs[0].StatusCheckedAt; !got.Equal(gpuTs) { t.Fatalf("expected gpu status_checked_at %s, got %s", gpuTs.Format(time.RFC3339), got.Format(time.RFC3339)) } if result.Hardware.GPUs[0].StatusAtCollect == nil || !result.Hardware.GPUs[0].StatusAtCollect.At.Equal(gpuTs) { t.Fatalf("expected gpu status_at_collection.at %s", gpuTs.Format(time.RFC3339)) } if got := result.Hardware.PCIeDevices[0].StatusCheckedAt; !got.Equal(nvsTs) { t.Fatalf("expected nvswitch status_checked_at %s, got %s", nvsTs.Format(time.RFC3339), got.Format(time.RFC3339)) } if result.Hardware.PCIeDevices[0].StatusAtCollect == nil || !result.Hardware.PCIeDevices[0].StatusAtCollect.At.Equal(nvsTs) { t.Fatalf("expected nvswitch status_at_collection.at %s", nvsTs.Format(time.RFC3339)) } if !result.Hardware.PCIeDevices[1].StatusCheckedAt.IsZero() { t.Fatalf("expected non-nvswitch device status_checked_at to stay zero") } } func TestCollectGPUAndNVSwitchCheckTimes_FromVerboseRun(t *testing.T) { files := []parser.ExtractedFile{ { Path: "verbose_run.log", Content: []byte(` 2026-01-22 09:11:32,458 - Testing nvswitch 2026-01-22 09:45:36,016 - Testing gpu_fieldiag `), }, } got := CollectGPUAndNVSwitchCheckTimes(files) if got.GPUDefault.Format(time.RFC3339) != "2026-01-22T09:45:36Z" { t.Fatalf("unexpected GPU check time: %s", got.GPUDefault.Format(time.RFC3339)) } if got.NVSwitchDefault.Format(time.RFC3339) != "2026-01-22T09:11:32Z" { t.Fatalf("unexpected NVSwitch check time: %s", got.NVSwitchDefault.Format(time.RFC3339)) } } func TestCollectGPUAndNVSwitchCheckTimes_FromComponentOutputLogs(t *testing.T) { files := []parser.ExtractedFile{ { Path: "gpu_fieldiag/SXM5_SN_1653925025497/output.log", Content: []byte(` $ some command MODS start: Thu Jan 22 09:45:36 2026 `), }, { Path: "nvswitch/output.log", Content: []byte(` $ cmd devname=0000:08:00.0,NVSWITCH3 devname=0000:07:00.0,NVSWITCH2 devname=0000:06:00.0,NVSWITCH1 devname=0000:05:00.0,NVSWITCH0 MODS start: Thu Jan 22 09:11:32 2026 `), }, } got := CollectGPUAndNVSwitchCheckTimes(files) if got.GPUBySerial["1653925025497"].Format(time.RFC3339) != "2026-01-22T09:45:36Z" { t.Fatalf("unexpected GPU serial check time: %s", got.GPUBySerial["1653925025497"].Format(time.RFC3339)) } if got.GPUBySlot["GPUSXM5"].Format(time.RFC3339) != "2026-01-22T09:45:36Z" { t.Fatalf("unexpected GPU slot check time: %s", got.GPUBySlot["GPUSXM5"].Format(time.RFC3339)) } if got.NVSwitchBySlot["NVSWITCH0"].Format(time.RFC3339) != "2026-01-22T09:11:32Z" { t.Fatalf("unexpected NVSwitch0 check time: %s", got.NVSwitchBySlot["NVSWITCH0"].Format(time.RFC3339)) } if got.NVSwitchBySlot["NVSWITCH3"].Format(time.RFC3339) != "2026-01-22T09:11:32Z" { t.Fatalf("unexpected NVSwitch3 check time: %s", got.NVSwitchBySlot["NVSWITCH3"].Format(time.RFC3339)) } }