// Copyright 2025, IPng Networks GmbH, Pim van Pelt package vpp import ( "testing" "time" "go.fd.io/govpp/api" ) func TestNewStatsManager(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) if manager == nil { t.Fatal("NewStatsManager() returned nil") return } if manager.client != client { t.Error("StatsManager should store the provided client") } if manager.period != time.Duration(*Period)*time.Second { t.Errorf("Expected period %v, got %v", time.Duration(*Period)*time.Second, manager.period) } if manager.running { t.Error("StatsManager should not be running initially") } if manager.statsCallback != nil { t.Error("StatsManager should have nil callback initially") } } func TestStatsManagerSetStatsCallback(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) var callbackCalled bool var receivedStats *api.InterfaceStats callback := func(stats *api.InterfaceStats) { callbackCalled = true receivedStats = stats } manager.SetStatsCallback(callback) if manager.statsCallback == nil { t.Error("SetStatsCallback() should store the callback") } // Test callback execution testStats := &api.InterfaceStats{ Interfaces: []api.InterfaceCounters{ { InterfaceIndex: 1, InterfaceName: "test-interface", Rx: api.InterfaceCounterCombined{Packets: 100, Bytes: 1500}, Tx: api.InterfaceCounterCombined{Packets: 50, Bytes: 750}, }, }, } manager.statsCallback(testStats) if !callbackCalled { t.Error("Callback should have been called") } if receivedStats != testStats { t.Error("Callback should receive the same stats object") } if len(receivedStats.Interfaces) != 1 { t.Errorf("Expected 1 interface, got %d", len(receivedStats.Interfaces)) } if receivedStats.Interfaces[0].InterfaceName != "test-interface" { t.Errorf("Expected interface name 'test-interface', got %q", receivedStats.Interfaces[0].InterfaceName) } } func TestStatsManagerSetPeriod(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) newPeriod := 5 * time.Second manager.SetPeriod(newPeriod) if manager.period != newPeriod { t.Errorf("Expected period %v, got %v", newPeriod, manager.period) } } func TestStatsManagerStartStopStatsRoutine(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) if manager.running { t.Error("StatsManager should not be running initially") } manager.StartStatsRoutine() if !manager.running { t.Error("StatsManager should be running after StartStatsRoutine()") } // Test starting again (should be safe) manager.StartStatsRoutine() if !manager.running { t.Error("StatsManager should still be running after second StartStatsRoutine()") } manager.StopStatsRoutine() if manager.running { t.Error("StatsManager should not be running after StopStatsRoutine()") } } func TestStatsManagerGetInterfaceStatsWithoutConnection(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) _, err := manager.GetInterfaceStats() if err == nil { t.Error("GetInterfaceStats() should return error when not connected") } vppErr, ok := err.(*VPPError) if !ok { t.Errorf("Expected VPPError, got %T", err) } if vppErr.Message != "VPP client not connected" { t.Errorf("Expected specific error message, got: %s", vppErr.Message) } } func TestStatsCallback(t *testing.T) { var callbackInvoked bool var callbackStats *api.InterfaceStats callback := StatsCallback(func(stats *api.InterfaceStats) { callbackInvoked = true callbackStats = stats }) testStats := &api.InterfaceStats{ Interfaces: []api.InterfaceCounters{ { InterfaceIndex: 42, InterfaceName: "test-callback-interface", Rx: api.InterfaceCounterCombined{Packets: 200, Bytes: 3000}, Tx: api.InterfaceCounterCombined{Packets: 100, Bytes: 1500}, RxUnicast: api.InterfaceCounterCombined{Packets: 180, Bytes: 2700}, TxUnicast: api.InterfaceCounterCombined{Packets: 90, Bytes: 1350}, }, }, } callback(testStats) if !callbackInvoked { t.Error("Callback should have been invoked") } if callbackStats != testStats { t.Error("Callback should receive the same stats object") } if len(callbackStats.Interfaces) != 1 { t.Errorf("Expected 1 interface, got %d", len(callbackStats.Interfaces)) } iface := callbackStats.Interfaces[0] if iface.InterfaceIndex != 42 { t.Errorf("Expected interface index 42, got %d", iface.InterfaceIndex) } if iface.InterfaceName != "test-callback-interface" { t.Errorf("Expected interface name 'test-callback-interface', got %q", iface.InterfaceName) } if iface.Rx.Packets != 200 { t.Errorf("Expected RX packets 200, got %d", iface.Rx.Packets) } if iface.Tx.Bytes != 1500 { t.Errorf("Expected TX bytes 1500, got %d", iface.Tx.Bytes) } if iface.RxUnicast.Packets != 180 { t.Errorf("Expected RX unicast packets 180, got %d", iface.RxUnicast.Packets) } if iface.TxUnicast.Bytes != 1350 { t.Errorf("Expected TX unicast bytes 1350, got %d", iface.TxUnicast.Bytes) } } func TestStatsManagerQueryAndReportStatsWithoutConnection(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) // Should return false when not connected if manager.queryAndReportStats() { t.Error("queryAndReportStats() should return false when not connected") } } func TestStatsManagerWithShortPeriod(t *testing.T) { client := &VPPClient{} manager := NewStatsManager(client) // Set a very short period for testing manager.SetPeriod(10 * time.Millisecond) if manager.period != 10*time.Millisecond { t.Errorf("Expected period 10ms, got %v", manager.period) } manager.StartStatsRoutine() // Let it run briefly time.Sleep(50 * time.Millisecond) manager.StopStatsRoutine() // Should stop gracefully if manager.running { t.Error("StatsManager should have stopped") } }