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()") } } }