From 384ffac87044e9d0e375587ec7630210b5f51917 Mon Sep 17 00:00:00 2001 From: Mikkel Georgsen Date: Fri, 10 Apr 2026 07:06:23 +0000 Subject: [PATCH] test(05-01): add failing tests for TesterDriver, mock drivers, FNB58 streaming --- internal/tester/driver_test.go | 249 +++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 internal/tester/driver_test.go diff --git a/internal/tester/driver_test.go b/internal/tester/driver_test.go new file mode 100644 index 0000000..4ed5de3 --- /dev/null +++ b/internal/tester/driver_test.go @@ -0,0 +1,249 @@ +package tester_test + +import ( + "testing" + "time" + + "git.georgsen.dk/hwlab/internal/tester" +) + +// Compile-time interface assertions +var ( + _ tester.TesterDriver = (*tester.MockUSBDriver)(nil) + _ tester.TesterDriver = (*tester.MockDPDriver)(nil) + _ tester.TesterDriver = (*tester.MockHDMIDriver)(nil) + _ tester.StreamingTesterDriver = (*tester.MockFNB58Driver)(nil) +) + +// ---- MockUSBDriver tests ---- + +func TestMockUSBDriver_Connect_Read_Disconnect(t *testing.T) { + d := &tester.MockUSBDriver{} + + if err := d.Connect(); err != nil { + t.Fatalf("Connect() unexpected error: %v", err) + } + + result, err := d.Read() + if err != nil { + t.Fatalf("Read() unexpected error: %v", err) + } + if result.CableType != tester.CableTypeUSB { + t.Errorf("CableType = %v, want CableTypeUSB", result.CableType) + } + if result.USBVersion != "USB 3.2 Gen 2" { + t.Errorf("USBVersion = %q, want %q", result.USBVersion, "USB 3.2 Gen 2") + } + if result.SpeedGbps != 10.0 { + t.Errorf("SpeedGbps = %v, want 10.0", result.SpeedGbps) + } + if result.MaxWatts != 100 { + t.Errorf("MaxWatts = %v, want 100", result.MaxWatts) + } + if !result.PinContinuity { + t.Error("PinContinuity = false, want true") + } + if !result.HasEMarker { + t.Error("HasEMarker = false, want true") + } + if result.ResistanceOhm != 0.12 { + t.Errorf("ResistanceOhm = %v, want 0.12", result.ResistanceOhm) + } + + if err := d.Disconnect(); err != nil { + t.Fatalf("Disconnect() unexpected error: %v", err) + } + // Idempotent second disconnect + if err := d.Disconnect(); err != nil { + t.Fatalf("second Disconnect() unexpected error: %v", err) + } +} + +func TestMockUSBDriver_ReadBeforeConnect(t *testing.T) { + d := &tester.MockUSBDriver{} + _, err := d.Read() + if err != tester.ErrNotConnected { + t.Errorf("Read() before Connect() = %v, want ErrNotConnected", err) + } +} + +// ---- MockDPDriver tests ---- + +func TestMockDPDriver_Connect_Read_Disconnect(t *testing.T) { + d := &tester.MockDPDriver{} + + if err := d.Connect(); err != nil { + t.Fatalf("Connect() unexpected error: %v", err) + } + + result, err := d.Read() + if err != nil { + t.Fatalf("Read() unexpected error: %v", err) + } + if result.CableType != tester.CableTypeDP { + t.Errorf("CableType = %v, want CableTypeDP", result.CableType) + } + if result.DPVersion != "1.4" { + t.Errorf("DPVersion = %q, want %q", result.DPVersion, "1.4") + } + if !result.PinContinuity { + t.Error("PinContinuity = false, want true") + } + + if err := d.Disconnect(); err != nil { + t.Fatalf("Disconnect() unexpected error: %v", err) + } + if err := d.Disconnect(); err != nil { + t.Fatalf("second Disconnect() unexpected error: %v", err) + } +} + +func TestMockDPDriver_ReadBeforeConnect(t *testing.T) { + d := &tester.MockDPDriver{} + _, err := d.Read() + if err != tester.ErrNotConnected { + t.Errorf("Read() before Connect() = %v, want ErrNotConnected", err) + } +} + +// ---- MockHDMIDriver tests ---- + +func TestMockHDMIDriver_Connect_Read_Disconnect(t *testing.T) { + d := &tester.MockHDMIDriver{} + + if err := d.Connect(); err != nil { + t.Fatalf("Connect() unexpected error: %v", err) + } + + result, err := d.Read() + if err != nil { + t.Fatalf("Read() unexpected error: %v", err) + } + if result.CableType != tester.CableTypeHDMI { + t.Errorf("CableType = %v, want CableTypeHDMI", result.CableType) + } + if result.HDMIVersion != "2.1" { + t.Errorf("HDMIVersion = %q, want %q", result.HDMIVersion, "2.1") + } + if !result.PinContinuity { + t.Error("PinContinuity = false, want true") + } + + if err := d.Disconnect(); err != nil { + t.Fatalf("Disconnect() unexpected error: %v", err) + } + if err := d.Disconnect(); err != nil { + t.Fatalf("second Disconnect() unexpected error: %v", err) + } +} + +func TestMockHDMIDriver_ReadBeforeConnect(t *testing.T) { + d := &tester.MockHDMIDriver{} + _, err := d.Read() + if err != tester.ErrNotConnected { + t.Errorf("Read() before Connect() = %v, want ErrNotConnected", err) + } +} + +// ---- KnownDevices tests ---- + +func TestKnownDevices_HasTesterEntries(t *testing.T) { + // This test is in usb package — placeholder to ensure cross-package compile succeeds. + // Actual KnownDevices assertions are in internal/usb/device_test.go +} + +// ---- MockFNB58Driver tests ---- + +func TestFNB58Driver_Connect_Stream_Readings(t *testing.T) { + d := &tester.MockFNB58Driver{} + + if err := d.Connect(); err != nil { + t.Fatalf("Connect() unexpected error: %v", err) + } + + ch := d.Stream() + if ch == nil { + t.Fatal("Stream() returned nil channel") + } + + var readings []tester.LiveReading + timeout := time.After(5 * time.Second) + for { + select { + case r, ok := <-ch: + if !ok { + goto done + } + readings = append(readings, r) + case <-timeout: + t.Fatal("timed out waiting for stream to close") + } + } +done: + if len(readings) != 3 { + t.Errorf("got %d readings, want 3", len(readings)) + } + for i, r := range readings { + if r.Voltage != 5.1 { + t.Errorf("reading[%d].Voltage = %v, want 5.1", i, r.Voltage) + } + if r.CurrentAmps != 3.0 { + t.Errorf("reading[%d].CurrentAmps = %v, want 3.0", i, r.CurrentAmps) + } + if r.PowerWatts != 15.3 { + t.Errorf("reading[%d].PowerWatts = %v, want 15.3", i, r.PowerWatts) + } + if r.PDProtocol != "PD3.0" { + t.Errorf("reading[%d].PDProtocol = %q, want %q", i, r.PDProtocol, "PD3.0") + } + if r.Timestamp.IsZero() { + t.Errorf("reading[%d].Timestamp is zero", i) + } + } + + if err := d.Disconnect(); err != nil { + t.Fatalf("Disconnect() unexpected error: %v", err) + } +} + +func TestStreamBeforeConnect_ReturnsClosed(t *testing.T) { + d := &tester.MockFNB58Driver{} + ch := d.Stream() + if ch == nil { + t.Fatal("Stream() returned nil, want closed channel") + } + select { + case _, ok := <-ch: + if ok { + t.Error("Stream() before Connect() should return a closed channel, got open") + } + default: + t.Error("Stream() before Connect() should return a closed (readable) channel, not blocked") + } +} + +func TestFNB58Driver_DisconnectStopsStream(t *testing.T) { + d := &tester.MockFNB58Driver{} + if err := d.Connect(); err != nil { + t.Fatalf("Connect() unexpected error: %v", err) + } + + ch := d.Stream() + + // Disconnect early — may receive 0..3 readings, then channel closes + if err := d.Disconnect(); err != nil { + t.Fatalf("Disconnect() unexpected error: %v", err) + } + + timeout := time.After(2 * time.Second) + for { + select { + case _, ok := <-ch: + if !ok { + return // channel closed — success + } + case <-timeout: + t.Fatal("channel not closed after Disconnect()") + } + } +}