Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions noq-proto/proptest-regressions/tests/proptests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ cc ec5baef3027436b012a332a97d46814ed157f259337c3f651bbb7a3233bd9c7f # shrinks to
cc b1429b84bf576bb9000e8d0d6d53cff4c93efacf033c9df898aeb9856d1b03fe # shrinks to input = _RandomInteractionWithMultipathSimpleRoutingArgs { seed: [159, 14, 107, 252, 130, 4, 190, 131, 86, 208, 127, 29, 140, 30, 55, 65, 242, 192, 2, 158, 40, 51, 110, 116, 46, 139, 156, 165, 64, 109, 33, 62], interactions: [PassiveMigration { side: Server, addr_idx: 0 }, OpenPath { side: Client, status: Available, addr_idx: 0 }] }
cc 4f717acb71d562f33601dfc8c7fbcca89b13c8259676a20ffc764a92e3ea07a1 # shrinks to input = _RandomInteractionWithMultipathComplexRoutingArgs { seed: [84, 97, 201, 172, 244, 139, 252, 60, 222, 107, 135, 245, 103, 45, 188, 138, 26, 198, 1, 97, 144, 22, 42, 228, 19, 154, 45, 135, 222, 137, 231, 16], interactions: [PassiveMigration { side: Server, addr_idx: 0 }, OpenPath { side: Client, status: Available, addr_idx: 0 }, ClosePath { side: Client, path_idx: 0, error_code: 0 }, PathSetStatus { side: Server, path_idx: 0, status: Backup }], routes: RoutingTable { client_routes: [([::ffff:1.1.1.0]:44433, 0)], server_routes: [([::ffff:2.2.2.0]:4433, 0)] } }
cc 9c8a45ade151832ae26ea86bb672ff78e0ea55444c6e7fd51db202f255b6f616
cc 829f66ceb9a2b64e08c089d76137a622a3209cb11a97a8c834b1f41b095b2a63 # shrinks to input = _MonkeyInteractionArgs { setup: MultipathSimpleRouting(Zeroes), side: Client, interactions: [Monkey([Frame(PathAck(PathAck { path_id: PathId(1), largest: 1, delay: 0, ranges: [1..2], ecn: None }))]), Normal(OpenPath { side: Client, status: Available, addr_idx: 0 })] }
cc 2f43de3c3cd460004fac95bd65ee18ec93bac6fca6f15bb7218cc044b0c5ff26 # shrinks to input = _MonkeyInteractionArgs { setup: MultipathSimpleRouting(Zeroes), side: Client, interactions: [Normal(PathSetStatus { side: Client, path_idx: 0, status: Backup }), Normal(Drive { side: Client }), Normal(Drive { side: Client }), Normal(OpenPath { side: Client, status: Available, addr_idx: 0 }), Normal(AdvanceTime), Monkey([Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])])] }
cc 9fa1a1369e820e46586c33e485be51800decf55fe13dabe6d13bb63168d4dfdf # shrinks to input = _MonkeyInteractionArgs { side: Client, setup: PairSetup { seed: Zeroes, extensions: MultipathOnly, routing_setup: Basic }, interactions: [Monkey([Frame(Stream(Stream { id: StreamId(0), offset: 0, fin: false, data: b"" })), Frame(Crypto(Crypto { offset: 0, data: b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" })), Bytes([00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00])]), Normal(OpenPath { side: Client, status: Available, addr_idx: 0 }), Normal(ClosePath { side: Client, path_idx: 0, error_code: 0 }), Normal(CloseConn { side: Server, error_code: 0 }), Normal(AdvanceTime)] }
100 changes: 99 additions & 1 deletion noq-proto/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ pub struct Connection {
/// State for n0's (<https://n0.computer>) nat traversal protocol.
n0_nat_traversal: n0_nat_traversal::State,
qlog: QlogSink,

/// A test-only frame queue for injecting frames or data before
/// encryption to test how another side reacts to arbitrary
/// authenticated data.
#[cfg(test)]
pending_data_to_inject: VecDeque<DataToInject>,
}

impl Connection {
Expand Down Expand Up @@ -430,6 +436,9 @@ impl Connection {

n0_nat_traversal: Default::default(),
qlog,

#[cfg(test)]
pending_data_to_inject: Default::default(),
};
if path_validated {
this.on_path_validated(PathId::ZERO);
Expand Down Expand Up @@ -1684,6 +1693,12 @@ impl Connection {
};
}

#[cfg(test)]
if !self.populate_injected_packet_data(path_id, &mut builder) {
self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder);
}

#[cfg(not(test))]
self.populate_packet(now, space_id, path_id, scheduling_info, &mut builder);

// ACK-only packets should only be sent when explicitly allowed. If we write them due to
Expand Down Expand Up @@ -2165,9 +2180,29 @@ impl Connection {

can_send.close = connection_close_pending && space_has_crypto;

#[cfg(test)]
{
can_send |= self.can_send_data_to_inject(path_id);
}

can_send
}

#[cfg(test)]
fn can_send_data_to_inject(&self, _path_id: PathId) -> SendableFrames {
SendableFrames {
acks: self.pending_data_to_inject.iter().any(|frame| {
matches!(
frame,
DataToInject::Frame(Frame::Ack(_)) | DataToInject::Frame(Frame::PathAck(_))
)
}),
other: !self.pending_data_to_inject.is_empty(),
close: false,
space_specific: false,
}
}

/// Process `ConnectionEvent`s generated by the associated `Endpoint`
///
/// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
Expand Down Expand Up @@ -5748,7 +5783,7 @@ impl Connection {
&self.config,
)
};
new_path_data.last_observed_addr_report = path.data.last_observed_addr_report.clone();
new_path_data.last_observed_addr_report = path.data.last_observed_addr_report;
if let Some(report) = observed_addr
&& let Some(updated) = new_path_data.update_observed_addr_report(report)
{
Expand Down Expand Up @@ -6584,6 +6619,53 @@ impl Connection {
}
}

#[cfg(test)]
fn populate_injected_packet_data<'a, 'b>(
&mut self,
path_id: PathId,
builder: &mut PacketBuilder<'a, 'b>,
) -> bool {
let stats = &mut self.path_stats.for_path(path_id).frame_tx;

let mut injected_test_packet = false;

while let Some(inject) = self.pending_data_to_inject.pop_front() {
use crate::coding::Encodable;

match inject {
DataToInject::Frame(frame) => {
let Some(enc_frame) = frame.to_encodable_frame() else {
continue;
};
let mut encode_for_size = Vec::new();
enc_frame.encode(&mut encode_for_size);
let size = encode_for_size.len();

if builder.frame_space_remaining() < size {
self.pending_data_to_inject
.push_front(DataToInject::Frame(frame));
break;
}

injected_test_packet = true;
builder.inject_test_frame(enc_frame, stats);
}
DataToInject::Bytes(bytes) => {
if builder.frame_space_remaining() < bytes.len() {
self.pending_data_to_inject
.push_front(DataToInject::Bytes(bytes));
break;
}

injected_test_packet = true;
builder.inject_test_bytes(bytes);
}
}
}

injected_test_packet
}

/// Write pending ACKs into a buffer
fn populate_acks<'a, 'b>(
now: Instant,
Expand Down Expand Up @@ -6946,6 +7028,14 @@ impl Connection {
}
}

/// Injects arbitrary data into the next packet for test purposes.
///
/// This data can be structured frames or arbitrary bytes.
#[cfg(test)]
pub(crate) fn test_inject_data(&mut self, data: impl IntoIterator<Item = DataToInject>) {
self.pending_data_to_inject.extend(data);
}

/// Whether we have on-path 1-RTT data to send.
///
/// This checks for frames that can only be sent in the data space (1-RTT):
Expand Down Expand Up @@ -7742,6 +7832,14 @@ fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Du
}
}

#[cfg(test)]
#[derive(derive_more::Debug, test_strategy::Arbitrary)]
pub(crate) enum DataToInject {
Frame(Frame),
#[debug("Bytes({_0:02X?})")]
Bytes(#[strategy(proptest::collection::vec(proptest::prelude::any::<u8>(), 1..1500))] Vec<u8>),
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
28 changes: 28 additions & 0 deletions noq-proto/src/connection/packet_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,34 @@ impl<'a, 'b> PacketBuilder<'a, 'b> {
self.sent_frames.record_sent_frame(frame);
}

/// Writes/injects a test frame into this packet.
///
/// Exactly like [`Self::write_frame`], but
/// - logs with "(injected frame)" as the log message
/// - doesn't record the frame in `sent_frames` to avoid triggering retransmission and
/// unwanted processing of ACKs that were intentionally invalid.
#[cfg(test)]
pub(super) fn inject_test_frame<'c>(
&mut self,
frame: EncodableFrame<'c>,
stats: &mut FrameStats,
) {
frame.encode(&mut self.frame_space_mut());
stats.record(frame.get_type());
self.qlog.record(&frame);
trace!(%frame, "(test-injected frame)");
}

/// Inserts arbitrary bytes into this packet for testing purposes.
#[cfg(test)]
pub(super) fn inject_test_bytes(&mut self, bytes: Vec<u8>) {
use std::io::Write;

let len = bytes.len();
let res = self.frame_space_mut().writer().write_all(&bytes);
trace!(len, ?res, "(test-injected data)");
}

/// Returns a writable buffer limited to the remaining frame space
///
/// The [`BufMut::remaining_mut`] call on the returned buffer indicates the amount of
Expand Down
Loading
Loading