230 lines
6.8 KiB
Go
230 lines
6.8 KiB
Go
package interfaces
|
|
|
|
import (
|
|
"bytes"
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.quad4.io/Networks/Reticulum-Go/pkg/common"
|
|
)
|
|
|
|
func TestBaseInterfaceStateChanges(t *testing.T) {
|
|
bi := NewBaseInterface("test", common.IF_TYPE_TCP, false) // Start disabled
|
|
|
|
if bi.IsEnabled() {
|
|
t.Error("Newly created disabled interface reports IsEnabled() == true")
|
|
}
|
|
if bi.IsOnline() {
|
|
t.Error("Newly created disabled interface reports IsOnline() == true")
|
|
}
|
|
if bi.IsDetached() {
|
|
t.Error("Newly created interface reports IsDetached() == true")
|
|
}
|
|
|
|
bi.Enable()
|
|
if !bi.IsEnabled() {
|
|
t.Error("After Enable(), IsEnabled() == false")
|
|
}
|
|
if !bi.IsOnline() {
|
|
t.Error("After Enable(), IsOnline() == false")
|
|
}
|
|
if bi.IsDetached() {
|
|
t.Error("After Enable(), IsDetached() == true")
|
|
}
|
|
|
|
bi.Detach()
|
|
if bi.IsEnabled() {
|
|
t.Error("After Detach(), IsEnabled() == true")
|
|
}
|
|
if bi.IsOnline() {
|
|
t.Error("After Detach(), IsOnline() == true")
|
|
}
|
|
if !bi.IsDetached() {
|
|
t.Error("After Detach(), IsDetached() == false")
|
|
}
|
|
|
|
// Reset for Disable test
|
|
bi = NewBaseInterface("test2", common.IF_TYPE_UDP, true) // Start enabled
|
|
if !bi.Enabled { // Check the Enabled field directly first
|
|
t.Error("Newly created enabled interface reports Enabled == false")
|
|
}
|
|
if bi.IsEnabled() { // IsEnabled should still be false because Online is false
|
|
t.Error("Newly created enabled interface reports IsEnabled() == true before Enable() is called")
|
|
}
|
|
|
|
bi.Enable() // Explicitly enable to set Online = true
|
|
if !bi.IsEnabled() { // Now IsEnabled should be true
|
|
t.Error("After Enable() on initially enabled interface, IsEnabled() == false")
|
|
}
|
|
|
|
bi.Disable()
|
|
if bi.Enabled { // Check Enabled field after Disable()
|
|
t.Error("After Disable(), Enabled == true")
|
|
}
|
|
if bi.IsOnline() {
|
|
t.Error("After Disable(), IsOnline() == true")
|
|
}
|
|
if bi.IsDetached() { // Disable doesn't detach
|
|
t.Error("After Disable(), IsDetached() == true")
|
|
}
|
|
}
|
|
|
|
func TestBaseInterfaceGetters(t *testing.T) {
|
|
bi := NewBaseInterface("getterTest", common.IF_TYPE_AUTO, true)
|
|
|
|
if bi.GetName() != "getterTest" {
|
|
t.Errorf("GetName() = %s; want getterTest", bi.GetName())
|
|
}
|
|
if bi.GetType() != common.IF_TYPE_AUTO {
|
|
t.Errorf("GetType() = %v; want %v", bi.GetType(), common.IF_TYPE_AUTO)
|
|
}
|
|
if bi.GetMode() != common.IF_MODE_FULL {
|
|
t.Errorf("GetMode() = %v; want %v", bi.GetMode(), common.IF_MODE_FULL)
|
|
}
|
|
if bi.GetMTU() != common.DEFAULT_MTU { // Assuming default MTU
|
|
t.Errorf("GetMTU() = %d; want %d", bi.GetMTU(), common.DEFAULT_MTU)
|
|
}
|
|
}
|
|
|
|
func TestBaseInterfaceCallbacks(t *testing.T) {
|
|
bi := NewBaseInterface("callbackTest", common.IF_TYPE_TCP, true)
|
|
var wg sync.WaitGroup
|
|
var callbackCalled bool
|
|
|
|
callback := func(data []byte, iface common.NetworkInterface) {
|
|
if len(data) != 5 {
|
|
t.Errorf("Callback received data length %d; want 5", len(data))
|
|
}
|
|
if iface.GetName() != "callbackTest" {
|
|
t.Errorf("Callback received interface name %s; want callbackTest", iface.GetName())
|
|
}
|
|
callbackCalled = true
|
|
wg.Done()
|
|
}
|
|
|
|
bi.SetPacketCallback(callback)
|
|
if bi.GetPacketCallback() == nil { // Cannot directly compare functions
|
|
t.Error("GetPacketCallback() returned nil after SetPacketCallback()")
|
|
}
|
|
|
|
wg.Add(1)
|
|
go bi.ProcessIncoming([]byte{1, 2, 3, 4, 5}) // Run in goroutine as callback might block
|
|
|
|
// Wait for callback or timeout
|
|
waitTimeout(&wg, 1*time.Second, t)
|
|
|
|
if !callbackCalled {
|
|
t.Error("Packet callback was not called after ProcessIncoming")
|
|
}
|
|
}
|
|
|
|
func TestBaseInterfaceStats(t *testing.T) {
|
|
bi := NewBaseInterface("statsTest", common.IF_TYPE_UDP, true)
|
|
bi.Enable() // Need to be Online for ProcessOutgoing
|
|
|
|
data1 := []byte{1, 2, 3}
|
|
data2 := []byte{4, 5, 6, 7, 8}
|
|
|
|
bi.ProcessIncoming(data1)
|
|
if bi.RxBytes != uint64(len(data1)) {
|
|
t.Errorf("RxBytes = %d; want %d after first ProcessIncoming", bi.RxBytes, len(data1))
|
|
}
|
|
|
|
bi.ProcessIncoming(data2)
|
|
if bi.RxBytes != uint64(len(data1)+len(data2)) {
|
|
t.Errorf("RxBytes = %d; want %d after second ProcessIncoming", bi.RxBytes, len(data1)+len(data2))
|
|
}
|
|
|
|
// ProcessOutgoing only updates TxBytes in BaseInterface
|
|
err := bi.ProcessOutgoing(data1)
|
|
if err != nil {
|
|
t.Fatalf("ProcessOutgoing failed: %v", err)
|
|
}
|
|
if bi.TxBytes != uint64(len(data1)) {
|
|
t.Errorf("TxBytes = %d; want %d after first ProcessOutgoing", bi.TxBytes, len(data1))
|
|
}
|
|
|
|
err = bi.ProcessOutgoing(data2)
|
|
if err != nil {
|
|
t.Fatalf("ProcessOutgoing failed: %v", err)
|
|
}
|
|
if bi.TxBytes != uint64(len(data1)+len(data2)) {
|
|
t.Errorf("TxBytes = %d; want %d after second ProcessOutgoing", bi.TxBytes, len(data1)+len(data2))
|
|
}
|
|
}
|
|
|
|
// Helper function to wait for a WaitGroup with a timeout
|
|
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration, t *testing.T) {
|
|
c := make(chan struct{})
|
|
go func() {
|
|
defer close(c)
|
|
wg.Wait()
|
|
}()
|
|
select {
|
|
case <-c:
|
|
// Completed normally
|
|
case <-time.After(timeout):
|
|
t.Fatal("Timed out waiting for WaitGroup")
|
|
}
|
|
}
|
|
|
|
// Minimal mock interface for InterceptedInterface test
|
|
type mockInterface struct {
|
|
BaseInterface
|
|
sendCalled bool
|
|
sendData []byte
|
|
}
|
|
|
|
func (m *mockInterface) Send(data []byte, addr string) error {
|
|
m.sendCalled = true
|
|
m.sendData = data
|
|
return nil
|
|
}
|
|
|
|
func (m *mockInterface) GetType() common.InterfaceType { return common.IF_TYPE_NONE }
|
|
func (m *mockInterface) GetMode() common.InterfaceMode { return common.IF_MODE_FULL }
|
|
func (m *mockInterface) ProcessIncoming(data []byte) {}
|
|
func (m *mockInterface) ProcessOutgoing(data []byte) error { return nil }
|
|
func (m *mockInterface) SendPathRequest([]byte) error { return nil }
|
|
func (m *mockInterface) SendLinkPacket([]byte, []byte, time.Time) error { return nil }
|
|
func (m *mockInterface) Start() error { return nil }
|
|
func (m *mockInterface) Stop() error { return nil }
|
|
func (m *mockInterface) GetConn() net.Conn { return nil }
|
|
func (m *mockInterface) GetBandwidthAvailable() bool { return true }
|
|
|
|
func TestInterceptedInterface(t *testing.T) {
|
|
mockBase := &mockInterface{}
|
|
var interceptorCalled bool
|
|
var interceptedData []byte
|
|
|
|
interceptor := func(data []byte, iface common.NetworkInterface) error {
|
|
interceptorCalled = true
|
|
interceptedData = data
|
|
return nil
|
|
}
|
|
|
|
intercepted := NewInterceptedInterface(mockBase, interceptor)
|
|
|
|
testData := []byte("intercept me")
|
|
err := intercepted.Send(testData, "dummy_addr")
|
|
if err != nil {
|
|
t.Fatalf("Intercepted Send failed: %v", err)
|
|
}
|
|
|
|
if !interceptorCalled {
|
|
t.Error("Interceptor function was not called")
|
|
}
|
|
if !bytes.Equal(interceptedData, testData) {
|
|
t.Errorf("Interceptor received data %x; want %x", interceptedData, testData)
|
|
}
|
|
|
|
if !mockBase.sendCalled {
|
|
t.Error("Original Send function was not called")
|
|
}
|
|
if !bytes.Equal(mockBase.sendData, testData) {
|
|
t.Errorf("Original Send received data %x; want %x", mockBase.sendData, testData)
|
|
}
|
|
}
|