Linux runs exactly as before when both bridge interfaces exist. When they do not, the lone default-route interface is monitored, and a single interface now draws a single UI pane instead of an empty second one. macOS is newly supported: a running VPN tunnel (utun) is monitored as the primary pane alongside the physical default-route interface, or the physical interface alone when no VPN is up. Detection lives in internal/netdetect: interface/route data types, pure selection logic keyed on OS name, and route parsers, all unit-tested on Linux for both platforms. Only the real route query and the per-platform TCP dial binding (source address on Linux, IP_BOUND_IF on macOS) are build-tagged. Ping argument construction is a pure, OS-keyed function. NewMonitor now takes a list of interfaces. Model: opus-4-8
139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
package monitor
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// TestNewMonitor tests the creation of a new Monitor
|
|
func TestNewMonitor(t *testing.T) {
|
|
// Test that we can create a monitor without errors
|
|
mon := NewMonitor([]IfaceSpec{
|
|
{Name: "test0", Label: "Test Interface A"},
|
|
{Name: "test1", Label: "Test Interface B"},
|
|
}, "/tmp/test.log")
|
|
|
|
if mon == nil {
|
|
t.Fatal("NewMonitor returned nil")
|
|
}
|
|
|
|
// Check interfaces
|
|
if len(mon.interfaces) != 2 {
|
|
t.Fatalf("Expected 2 interfaces, got %d", len(mon.interfaces))
|
|
}
|
|
|
|
if mon.interfaces[0].Name != "test0" {
|
|
t.Errorf("Expected interfaces[0].Name to be 'test0', got '%s'", mon.interfaces[0].Name)
|
|
}
|
|
|
|
if mon.interfaces[1].Name != "test1" {
|
|
t.Errorf("Expected interfaces[1].Name to be 'test1', got '%s'", mon.interfaces[1].Name)
|
|
}
|
|
|
|
// Check default configuration
|
|
if mon.ICMPTimeout.Milliseconds() != 500 {
|
|
t.Errorf("Expected ICMPTimeout to be 500ms, got %dms", mon.ICMPTimeout.Milliseconds())
|
|
}
|
|
|
|
if mon.PacketLossPings != 20 {
|
|
t.Errorf("Expected PacketLossPings to be 20, got %d", mon.PacketLossPings)
|
|
}
|
|
|
|
// Check that host lists are initialized
|
|
if mon.reachabilityHosts == nil {
|
|
t.Error("reachabilityHosts is nil")
|
|
}
|
|
|
|
if mon.packetLossHosts == nil {
|
|
t.Error("packetLossHosts is nil")
|
|
}
|
|
|
|
if mon.tcpHosts == nil {
|
|
t.Error("tcpHosts is nil")
|
|
}
|
|
}
|
|
|
|
// TestNewMonitorSingle verifies a monitor can be built with one interface.
|
|
func TestNewMonitorSingle(t *testing.T) {
|
|
mon := NewMonitor([]IfaceSpec{{Name: "eth0", Label: "default route"}}, "")
|
|
|
|
if len(mon.interfaces) != 1 {
|
|
t.Fatalf("Expected 1 interface, got %d", len(mon.interfaces))
|
|
}
|
|
|
|
if mon.interfaces[0].Name != "eth0" {
|
|
t.Errorf("Expected interfaces[0].Name to be 'eth0', got '%s'", mon.interfaces[0].Name)
|
|
}
|
|
}
|
|
|
|
// TestAddHosts tests adding hosts to the monitor
|
|
func TestAddHosts(t *testing.T) {
|
|
mon := NewMonitor([]IfaceSpec{
|
|
{Name: "test0", Label: "Test A"},
|
|
{Name: "test1", Label: "Test B"},
|
|
}, "")
|
|
|
|
// Test adding reachability hosts
|
|
mon.AddReachabilityHost("8.8.8.8")
|
|
mon.AddReachabilityHost("google.com")
|
|
|
|
if len(mon.reachabilityHosts) != 2 {
|
|
t.Errorf("Expected 2 reachability hosts, got %d", len(mon.reachabilityHosts))
|
|
}
|
|
|
|
// Test adding packet loss hosts
|
|
mon.AddPacketLossHost("github.com")
|
|
|
|
if len(mon.packetLossHosts) != 1 {
|
|
t.Errorf("Expected 1 packet loss host, got %d", len(mon.packetLossHosts))
|
|
}
|
|
|
|
// Test adding TCP hosts
|
|
mon.AddTCPHost("google.com:443")
|
|
mon.AddTCPHost("github.com:443")
|
|
|
|
if len(mon.tcpHosts) != 2 {
|
|
t.Errorf("Expected 2 TCP hosts, got %d", len(mon.tcpHosts))
|
|
}
|
|
}
|
|
|
|
// TestMinMaxAvgStd tests the statistics calculation function
|
|
func TestMinMaxAvgStd(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []float64
|
|
wantMin, wantMax, wantAvg float64
|
|
}{
|
|
{
|
|
name: "empty slice",
|
|
data: []float64{},
|
|
wantMin: 0, wantMax: 0, wantAvg: 0,
|
|
},
|
|
{
|
|
name: "single value",
|
|
data: []float64{5.0},
|
|
wantMin: 5.0, wantMax: 5.0, wantAvg: 5.0,
|
|
},
|
|
{
|
|
name: "multiple values",
|
|
data: []float64{1.0, 2.0, 3.0, 4.0, 5.0},
|
|
wantMin: 1.0, wantMax: 5.0, wantAvg: 3.0,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
min, max, avg, _ := MinMaxAvgStd(tt.data)
|
|
|
|
if min != tt.wantMin {
|
|
t.Errorf("MinMaxAvgStd() min = %v, want %v", min, tt.wantMin)
|
|
}
|
|
if max != tt.wantMax {
|
|
t.Errorf("MinMaxAvgStd() max = %v, want %v", max, tt.wantMax)
|
|
}
|
|
if avg != tt.wantAvg {
|
|
t.Errorf("MinMaxAvgStd() avg = %v, want %v", avg, tt.wantAvg)
|
|
}
|
|
})
|
|
}
|
|
}
|