From e029b57b2c7bb8471c82084cc1d7f051169c5815 Mon Sep 17 00:00:00 2001 From: Darshan U Date: Wed, 7 Oct 2026 06:59:48 +0530 Subject: [PATCH] Add timing and interface information to data transfer snapshots - Keep snapshotTimestamp for duration calculation between snapshots - Add interfaceType (InterfaceType?) to correlate paths with their underlying link type (Wi-Fi, Cellular, Ethernet, etc.) rather than relying only on integer path identifiers - Populate interfaceType from QUICPath.interface in both updateDataTransferSnapshot and getDiagnosticsSnapshot - Include interfaceType in JSON serialization output - Add tests for timestamp duration calculation and interface type preservation across snapshot instances --- .../Connection/DataTransferSnapshot.swift | 5 ++- .../SwiftNetwork/QUIC/QUICConnection.swift | 3 ++ Tests/QUICTests/QUICConnectionTests.swift | 37 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftNetwork/Connection/DataTransferSnapshot.swift b/Sources/SwiftNetwork/Connection/DataTransferSnapshot.swift index 4da2d75b..75e166c9 100644 --- a/Sources/SwiftNetwork/Connection/DataTransferSnapshot.swift +++ b/Sources/SwiftNetwork/Connection/DataTransferSnapshot.swift @@ -14,8 +14,11 @@ @_spi(ProtocolProvider) @available(Network 0.1.0, *) -public struct DataTransferSnapshot: Equatable { +public struct DataTransferSnapshot: Equatable, Sendable { + public var snapshotTimestamp: NetworkClock.Instant = .zero public var interfaceIndex: UInt64? + public var interfaceType: InterfaceType? + public var pathIdentifier: UInt64? public var receivedIPPacketCount: UInt64 = 0 public var receivedIPEct1PacketCount: UInt64 = 0 diff --git a/Sources/SwiftNetwork/QUIC/QUICConnection.swift b/Sources/SwiftNetwork/QUIC/QUICConnection.swift index c8c67719..3aa577d5 100644 --- a/Sources/SwiftNetwork/QUIC/QUICConnection.swift +++ b/Sources/SwiftNetwork/QUIC/QUICConnection.swift @@ -2766,6 +2766,7 @@ public final class QUICConnection: ManyToManyApplicationStreamProtocol, #endif public func updateDataTransferSnapshot(flow: MultiplexedFlowIdentifier, _ snapshot: inout DataTransferSnapshot) { + snapshot.snapshotTimestamp = self.now snapshot.receivedTransportOutOfOrderByteCount = UInt64(clamping: self.stats[.rxOutOfOrderBytes]) snapshot.sentTransportRetransmittedByteCount = UInt64(clamping: self.stats[.txRetransmittedBytes]) snapshot.sentTransportECNCapablePacketCount = UInt64(clamping: self.stats[.ecnCapablePacketsSent]) @@ -2773,6 +2774,8 @@ public final class QUICConnection: ManyToManyApplicationStreamProtocol, snapshot.sentTransportECNCapableMarkedPacketCount = UInt64(clamping: self.stats[.ecnCapablePacketsMarked]) snapshot.sentTransportECNCapableLostPacketCount = UInt64(clamping: self.stats[.ecnCapablePacketsLost]) if let path = currentPath { + snapshot.pathIdentifier = path.pathIdentifier + snapshot.interfaceType = path.interface?.interfaceType snapshot.transportMinimumRTT = path.rtt.minRTT snapshot.transportSmoothedRTT = path.rtt.smoothedRTT snapshot.transportCurrentRTT = path.rtt.adjustedRTT diff --git a/Tests/QUICTests/QUICConnectionTests.swift b/Tests/QUICTests/QUICConnectionTests.swift index 3acc6df5..26c9651a 100644 --- a/Tests/QUICTests/QUICConnectionTests.swift +++ b/Tests/QUICTests/QUICConnectionTests.swift @@ -514,6 +514,43 @@ final class QUICConnectionTests: XCTestCase { ) } } + + func testSnapshotTimestampDuration() { + // Verify that two snapshots with different timestamps can compute a duration + var first = DataTransferSnapshot() + first.snapshotTimestamp = NetworkClock.Instant(nanoseconds: 1_000_000_000) // 1s + + var second = DataTransferSnapshot() + second.snapshotTimestamp = NetworkClock.Instant(nanoseconds: 3_500_000_000) // 3.5s + + let duration = first.snapshotTimestamp.duration(to: second.snapshotTimestamp) + XCTAssertEqual(duration, .milliseconds(2500), "Duration between snapshots should be 2.5 seconds") + + // Verify snapshots remain independently equal to themselves + XCTAssertEqual(first, first) + XCTAssertEqual(second, second) + XCTAssertNotEqual(first, second) + } + + func testSnapshotPreservesInterfaceType() { + // Verify that path and interface information round-trips through a snapshot + var snapshot = DataTransferSnapshot() + snapshot.pathIdentifier = 42 + snapshot.interfaceType = .wifi + snapshot.snapshotTimestamp = NetworkClock.Instant(nanoseconds: 500_000_000) + + XCTAssertEqual(snapshot.pathIdentifier, 42) + XCTAssertEqual(snapshot.interfaceType, .wifi) + + // Verify different interface types are distinguishable + var cellularSnapshot = DataTransferSnapshot() + cellularSnapshot.pathIdentifier = 43 + cellularSnapshot.interfaceType = .cellular + cellularSnapshot.snapshotTimestamp = NetworkClock.Instant(nanoseconds: 600_000_000) + + XCTAssertNotEqual(snapshot.interfaceType, cellularSnapshot.interfaceType) + XCTAssertNotEqual(snapshot, cellularSnapshot) + } } #endif