//go:build linux package transport import ( "net" "reflect" "testing" "omnisocketgo/cmd/internal/latencylog" "omnisocketgo/cmd/internal/protocol" ) func TestUDPLinuxTimestampingRecordsKernelEvents(t *testing.T) { tests := []struct { name string msg protocol.Message }{ { name: "text", msg: protocol.Message{ Type: protocol.MessageTypeText, ID: 41, From: "peer-a", To: "peer-b", Body: []byte("hello over udp"), }, }, { name: "file", msg: protocol.Message{ Type: protocol.MessageTypeFile, ID: 42, From: "peer-a", To: "peer-b", FileName: "payload.bin", Body: []byte{0x00, 0x01, 0x02, 0xff}, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { senderLogger := &recordingLogger{} receiverLogger := &recordingLogger{} serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") if err != nil { t.Fatalf("ResolveUDPAddr() error = %v", err) } serverRaw, err := net.ListenUDP("udp", serverAddr) if err != nil { t.Fatalf("ListenUDP() error = %v", err) } receiver, err := NewUDPConn( serverRaw, nil, WithUDPLogger(receiverLogger, latencylog.NodeRolePeer, "peer-b"), ) if err != nil { _ = serverRaw.Close() t.Fatalf("NewUDPConn(receiver) error = %v", err) } t.Cleanup(func() { _ = receiver.Close() }) peerRaw, err := net.DialUDP("udp", nil, serverRaw.LocalAddr().(*net.UDPAddr)) if err != nil { t.Fatalf("DialUDP() error = %v", err) } sender, err := NewUDPConn( peerRaw, nil, WithUDPLogger(senderLogger, latencylog.NodeRolePeer, "peer-a"), ) if err != nil { _ = peerRaw.Close() t.Fatalf("NewUDPConn(sender) error = %v", err) } t.Cleanup(func() { _ = sender.Close() }) sendErr := make(chan error, 1) go func() { sendErr <- sender.Send(tt.msg) }() got, _, err := receiver.Receive() if err != nil { t.Fatalf("Receive() error = %v", err) } if err := <-sendErr; err != nil { t.Fatalf("Send() error = %v", err) } if !reflect.DeepEqual(got, tt.msg) { t.Fatalf("message mismatch: got %+v want %+v", got, tt.msg) } assertHasEvent(t, senderLogger.Events(), latencylog.EventATXSched, tt.msg.ID) assertHasEvent(t, senderLogger.Events(), latencylog.EventATXSoftware, tt.msg.ID) assertHasEvent(t, receiverLogger.Events(), latencylog.EventBRXSoftware, tt.msg.ID) }) } } func TestUDPLinuxTimestampingDebugLoggerCapturesDatagramAndErrqueueEvents(t *testing.T) { debugLogger := &recordingTXTimestampDebugLogger{} senderLogger := &recordingLogger{} receiverLogger := &recordingLogger{} serverAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") if err != nil { t.Fatalf("ResolveUDPAddr() error = %v", err) } serverRaw, err := net.ListenUDP("udp", serverAddr) if err != nil { t.Fatalf("ListenUDP() error = %v", err) } receiver, err := NewUDPConn( serverRaw, nil, WithUDPLogger(receiverLogger, latencylog.NodeRolePeer, "peer-b"), ) if err != nil { _ = serverRaw.Close() t.Fatalf("NewUDPConn(receiver) error = %v", err) } t.Cleanup(func() { _ = receiver.Close() }) peerRaw, err := net.DialUDP("udp", nil, serverRaw.LocalAddr().(*net.UDPAddr)) if err != nil { t.Fatalf("DialUDP() error = %v", err) } sender, err := NewUDPConn( peerRaw, nil, WithUDPLogger(senderLogger, latencylog.NodeRolePeer, "peer-a"), WithUDPTXTimestampDebugLogger(debugLogger), ) if err != nil { _ = peerRaw.Close() t.Fatalf("NewUDPConn(sender) error = %v", err) } t.Cleanup(func() { _ = sender.Close() }) msg := protocol.Message{ Type: protocol.MessageTypeText, ID: 99, From: "peer-a", To: "peer-b", Body: []byte("hello udp debug"), } sendErr := make(chan error, 1) go func() { sendErr <- sender.Send(msg) }() got, _, err := receiver.Receive() if err != nil { t.Fatalf("Receive() error = %v", err) } if err := <-sendErr; err != nil { t.Fatalf("Send() error = %v", err) } if !reflect.DeepEqual(got, msg) { t.Fatalf("message mismatch: got %+v want %+v", got, msg) } assertHasEvent(t, senderLogger.Events(), latencylog.EventATXSched, msg.ID) assertHasEvent(t, senderLogger.Events(), latencylog.EventATXSoftware, msg.ID) assertHasEvent(t, receiverLogger.Events(), latencylog.EventBRXSoftware, msg.ID) sendChunkRecords := debugRecordsByType(debugLogger.Records(), txTimestampDebugRecordTypeSendChunk) errqueueRecords := debugRecordsByType(debugLogger.Records(), txTimestampDebugRecordTypeErrqueueEvent) if len(sendChunkRecords) == 0 { t.Fatal("send_chunk debug records = 0, want at least 1") } if len(errqueueRecords) == 0 { t.Fatal("errqueue_event debug records = 0, want at least 1") } finalChunkRecord := sendChunkRecords[len(sendChunkRecords)-1] if finalChunkRecord.ExpectedTXID == nil { t.Fatal("final send_chunk expected_tx_id = nil, want non-nil") } finalExpectedTXID := *finalChunkRecord.ExpectedTXID selectedRecords := selectedErrqueueRecords(errqueueRecords) if len(selectedRecords) == 0 { t.Fatal("selected errqueue debug records = 0, want at least 1") } highestObservedID := uint32(0) haveHighestObservedID := false haveExactFinalID := false for _, record := range errqueueRecords { if record.EEData == nil { continue } if !haveHighestObservedID || *record.EEData > highestObservedID { highestObservedID = *record.EEData haveHighestObservedID = true } if *record.EEData == finalExpectedTXID && isBusinessTXTimestampRecord(record) { haveExactFinalID = true } } if !haveHighestObservedID { t.Fatal("highestObservedID missing, want at least one ee_data") } wantSelectedID := highestObservedID if haveExactFinalID { wantSelectedID = finalExpectedTXID } for _, record := range selectedRecords { if record.EEData == nil { t.Fatalf("selected record missing ee_data: %+v", record) } if *record.EEData != wantSelectedID { t.Fatalf("selected ee_data = %d, want %d", *record.EEData, wantSelectedID) } } selectedByEventName := make(map[string]int64, len(selectedRecords)) for _, record := range selectedRecords { if record.TSUnixNano == nil { t.Fatalf("selected record missing timestamp: %+v", record) } selectedByEventName[record.EventName] = *record.TSUnixNano } senderEventsByName := make(map[string]int64) for _, event := range senderLogger.Events() { if event.MessageID != msg.ID { continue } if !isBusinessTXTimestampEventName(event.Event) { continue } senderEventsByName[event.Event] = event.TsUnixNano } for eventName, selectedTS := range selectedByEventName { if senderEventsByName[eventName] != selectedTS { t.Fatalf("sender latency event %s = %d, want %d from selected debug record", eventName, senderEventsByName[eventName], selectedTS) } } }