mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Reorganize protocol test modules
This commit is contained in:
@@ -1,282 +0,0 @@
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use unshell::protocol::{Leaf, Packet};
|
||||
|
||||
use crate::{
|
||||
FakePtyLeaf, FakePtyState, OP_ABORT, OP_ERROR, OP_EXIT, OP_INPUT, OP_OUTPUT, OP_STDIN_EOF,
|
||||
OP_TERMINATE, pty_open_packet,
|
||||
};
|
||||
|
||||
use super::support::{
|
||||
ENDPOINT_A, ENDPOINT_B, PROC_OTHER, assert_frame, assert_hook_present, assert_hook_removed,
|
||||
assert_opened, drain_parent_pty_packets, endpoint_at, has_frame, open_pty_session,
|
||||
pty_endpoints, send_downward_frame, transfer_packets,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn open_pty_paves_hook_and_creates_session() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 1);
|
||||
assert_eq!(leaf.state().active_count, 1);
|
||||
assert_eq!(leaf.state().total_opened, 1);
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_present(&endpoint_b, hook_id);
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_opened(&packets[0], hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn input_and_output_share_one_hook() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_INPUT,
|
||||
b"hello",
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_OUTPUT, false, b"hello");
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_present(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_eof_keeps_hook_until_exit() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_STDIN_EOF,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
|
||||
assert_eq!(leaf.state().last_stdin_eof_hook, Some(hook_id));
|
||||
assert!(drain_parent_pty_packets(&mut endpoint_a).is_empty());
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_present(&endpoint_b, hook_id);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_TERMINATE,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_EXIT, true, &[0]);
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exit_end_hook_cleans_route_and_session() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_TERMINATE,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_EXIT, true, &[0]);
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_final_exit_route_closes_session_without_retry() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_TERMINATE,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
endpoint_b.remove_connection(ENDPOINT_A, true);
|
||||
leaf.update(&mut endpoint_b);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_eq!(leaf.pending_packet_count(), 0);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
|
||||
endpoint_b.add_connection(ENDPOINT_A, true);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert!(packets.is_empty());
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abort_downward_end_hook_closes_without_ack() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_ABORT,
|
||||
&[],
|
||||
true,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert!(drain_parent_pty_packets(&mut endpoint_a).is_empty());
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_session_input_returns_error_end_hook() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = endpoint_a.get_hook_id();
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_INPUT,
|
||||
b"orphan",
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_ERROR, true, b"unknown-session");
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_pty_sessions_interleave_without_crossing_hooks() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
|
||||
let first_hook = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
let second_hook = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
second_hook,
|
||||
OP_INPUT,
|
||||
b"second",
|
||||
false,
|
||||
);
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
first_hook,
|
||||
OP_INPUT,
|
||||
b"first",
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 2);
|
||||
assert_eq!(packets.len(), 2);
|
||||
assert!(has_frame(&packets, first_hook, OP_OUTPUT, b"first"));
|
||||
assert!(has_frame(&packets, second_hook, OP_OUTPUT, b"second"));
|
||||
assert_hook_present(&endpoint_a, first_hook);
|
||||
assert_hook_present(&endpoint_a, second_hook);
|
||||
assert_hook_present(&endpoint_b, first_hook);
|
||||
assert_hook_present(&endpoint_b, second_hook);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pty_leaf_does_not_consume_other_leaf_packets() {
|
||||
let mut endpoint = endpoint_at(ENDPOINT_B, vec![ENDPOINT_A, ENDPOINT_B]);
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
endpoint.add_connection(ENDPOINT_A, true);
|
||||
|
||||
endpoint
|
||||
.add_inbound_from(ENDPOINT_A, pty_open_packet(vec![ENDPOINT_A, ENDPOINT_B], 7))
|
||||
.unwrap();
|
||||
endpoint
|
||||
.add_inbound_from(
|
||||
ENDPOINT_A,
|
||||
Packet {
|
||||
hook_id: 8,
|
||||
end_hook: false,
|
||||
path: vec![ENDPOINT_A, ENDPOINT_B],
|
||||
procedure_id: PROC_OTHER,
|
||||
data: b"leave-me".to_vec(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
leaf.update(&mut endpoint);
|
||||
|
||||
let mut other_packets = Vec::new();
|
||||
endpoint.take_inbound_matching(
|
||||
ENDPOINT_B,
|
||||
|packet| packet.procedure_id == PROC_OTHER,
|
||||
|packet| other_packets.push(packet),
|
||||
);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 1);
|
||||
assert_eq!(other_packets.len(), 1);
|
||||
assert_eq!(other_packets[0].procedure_id, PROC_OTHER);
|
||||
assert_eq!(other_packets[0].data, b"leave-me".to_vec());
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use unshell::protocol::Leaf;
|
||||
|
||||
use crate::{FakePtyLeaf, FakePtyState, OP_INPUT, OP_OUTPUT};
|
||||
|
||||
use super::super::support::{
|
||||
ENDPOINT_A, ENDPOINT_B, assert_hook_present, drain_parent_pty_packets, has_frame,
|
||||
open_pty_session, pty_endpoints, send_downward_frame, transfer_packets,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn two_pty_sessions_interleave_without_crossing_hooks() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
|
||||
let first_hook = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
let second_hook = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
second_hook,
|
||||
OP_INPUT,
|
||||
b"second",
|
||||
false,
|
||||
);
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
first_hook,
|
||||
OP_INPUT,
|
||||
b"first",
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 2);
|
||||
assert_eq!(packets.len(), 2);
|
||||
assert!(has_frame(&packets, first_hook, OP_OUTPUT, b"first"));
|
||||
assert!(has_frame(&packets, second_hook, OP_OUTPUT, b"second"));
|
||||
assert_hook_present(&endpoint_a, first_hook);
|
||||
assert_hook_present(&endpoint_a, second_hook);
|
||||
assert_hook_present(&endpoint_b, first_hook);
|
||||
assert_hook_present(&endpoint_b, second_hook);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use unshell::protocol::Leaf;
|
||||
|
||||
use crate::{FakePtyLeaf, FakePtyState, OP_TERMINATE};
|
||||
|
||||
use super::super::support::{
|
||||
ENDPOINT_A, ENDPOINT_B, assert_hook_present, assert_hook_removed, drain_parent_pty_packets,
|
||||
open_pty_session, pty_endpoints, send_downward_frame, transfer_packets,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn failed_final_exit_route_closes_session_without_retry() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_TERMINATE,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
endpoint_b.remove_connection(ENDPOINT_A, true);
|
||||
leaf.update(&mut endpoint_b);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_eq!(leaf.pending_packet_count(), 0);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
|
||||
endpoint_b.add_connection(ENDPOINT_A, true);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert!(packets.is_empty());
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use unshell::protocol::{Leaf, Packet};
|
||||
|
||||
use crate::{FakePtyLeaf, FakePtyState, pty_open_packet};
|
||||
|
||||
use super::super::support::{ENDPOINT_A, ENDPOINT_B, PROC_OTHER, endpoint_at};
|
||||
|
||||
#[test]
|
||||
fn pty_leaf_does_not_consume_other_leaf_packets() {
|
||||
let mut endpoint = endpoint_at(ENDPOINT_B, vec![ENDPOINT_A, ENDPOINT_B]);
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
endpoint.add_connection(ENDPOINT_A, true);
|
||||
|
||||
endpoint
|
||||
.add_inbound_from(ENDPOINT_A, pty_open_packet(vec![ENDPOINT_A, ENDPOINT_B], 7))
|
||||
.unwrap();
|
||||
endpoint
|
||||
.add_inbound_from(
|
||||
ENDPOINT_A,
|
||||
Packet {
|
||||
hook_id: 8,
|
||||
end_hook: false,
|
||||
path: vec![ENDPOINT_A, ENDPOINT_B],
|
||||
procedure_id: PROC_OTHER,
|
||||
data: b"leave-me".to_vec(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
leaf.update(&mut endpoint);
|
||||
|
||||
let mut other_packets = Vec::new();
|
||||
endpoint.take_inbound_matching(
|
||||
ENDPOINT_B,
|
||||
|packet| packet.procedure_id == PROC_OTHER,
|
||||
|packet| other_packets.push(packet),
|
||||
);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 1);
|
||||
assert_eq!(other_packets.len(), 1);
|
||||
assert_eq!(other_packets[0].procedure_id, PROC_OTHER);
|
||||
assert_eq!(other_packets[0].data, b"leave-me".to_vec());
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use unshell::protocol::Leaf;
|
||||
|
||||
use crate::{FakePtyLeaf, FakePtyState, OP_INPUT, OP_OUTPUT};
|
||||
|
||||
use super::super::support::{
|
||||
ENDPOINT_A, ENDPOINT_B, assert_frame, assert_hook_present, drain_parent_pty_packets,
|
||||
open_pty_session, pty_endpoints, send_downward_frame, transfer_packets,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn input_and_output_share_one_hook() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_INPUT,
|
||||
b"hello",
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_OUTPUT, false, b"hello");
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_present(&endpoint_b, hook_id);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
use unshell::protocol::Leaf;
|
||||
|
||||
use crate::{
|
||||
FakePtyLeaf, FakePtyState, OP_ABORT, OP_ERROR, OP_EXIT, OP_INPUT, OP_STDIN_EOF, OP_TERMINATE,
|
||||
};
|
||||
|
||||
use super::super::support::{
|
||||
ENDPOINT_A, ENDPOINT_B, assert_frame, assert_hook_present, assert_hook_removed, assert_opened,
|
||||
drain_parent_pty_packets, open_pty_session, pty_endpoints, send_downward_frame,
|
||||
transfer_packets,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn open_pty_paves_hook_and_creates_session() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 1);
|
||||
assert_eq!(leaf.state().active_count, 1);
|
||||
assert_eq!(leaf.state().total_opened, 1);
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_present(&endpoint_b, hook_id);
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_opened(&packets[0], hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stdin_eof_keeps_hook_until_exit() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_STDIN_EOF,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
|
||||
assert_eq!(leaf.state().last_stdin_eof_hook, Some(hook_id));
|
||||
assert!(drain_parent_pty_packets(&mut endpoint_a).is_empty());
|
||||
assert_hook_present(&endpoint_a, hook_id);
|
||||
assert_hook_present(&endpoint_b, hook_id);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_TERMINATE,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_EXIT, true, &[0]);
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exit_end_hook_cleans_route_and_session() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_TERMINATE,
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_EXIT, true, &[0]);
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abort_downward_end_hook_closes_without_ack() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = open_pty_session(&mut endpoint_a, &mut endpoint_b, &mut leaf);
|
||||
drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_ABORT,
|
||||
&[],
|
||||
true,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert!(drain_parent_pty_packets(&mut endpoint_a).is_empty());
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_session_input_returns_error_end_hook() {
|
||||
let (mut endpoint_a, mut endpoint_b) = pty_endpoints();
|
||||
let mut leaf = FakePtyLeaf::new(FakePtyState::new());
|
||||
let hook_id = endpoint_a.get_hook_id();
|
||||
|
||||
send_downward_frame(
|
||||
&mut endpoint_a,
|
||||
&mut endpoint_b,
|
||||
hook_id,
|
||||
OP_INPUT,
|
||||
b"orphan",
|
||||
false,
|
||||
);
|
||||
leaf.update(&mut endpoint_b);
|
||||
transfer_packets(&mut endpoint_b, &mut endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
let packets = drain_parent_pty_packets(&mut endpoint_a);
|
||||
|
||||
assert_eq!(packets.len(), 1);
|
||||
assert_frame(&packets[0], hook_id, OP_ERROR, true, b"unknown-session");
|
||||
assert_eq!(leaf.active_session_count(), 0);
|
||||
assert_hook_removed(&endpoint_a, hook_id);
|
||||
assert_hook_removed(&endpoint_b, hook_id);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod concurrency;
|
||||
mod failure;
|
||||
mod filtering;
|
||||
mod input_output;
|
||||
mod lifecycle;
|
||||
@@ -1,137 +0,0 @@
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use unshell::protocol::{Endpoint, Leaf, Packet};
|
||||
|
||||
use crate::{
|
||||
FakePtyLeaf, OP_OPENED, PROC_PTY, frame_opcode, frame_payload, pty_open_packet, pty_packet,
|
||||
};
|
||||
|
||||
pub(super) const ENDPOINT_A: u32 = 0;
|
||||
pub(super) const ENDPOINT_B: u32 = 1;
|
||||
pub(super) const PROC_OTHER: u32 = 31;
|
||||
|
||||
/// Creates a bare endpoint at a known absolute path.
|
||||
pub(super) fn endpoint_at(id: u32, path: Vec<u32>) -> Endpoint {
|
||||
let mut endpoint = Endpoint::new(id);
|
||||
endpoint.path = path;
|
||||
endpoint
|
||||
}
|
||||
|
||||
/// Creates the parent/child endpoint pair used by PTY session tests.
|
||||
pub(super) fn pty_endpoints() -> (Endpoint, Endpoint) {
|
||||
let mut endpoint_a = endpoint_at(ENDPOINT_A, vec![ENDPOINT_A]);
|
||||
let mut endpoint_b = endpoint_at(ENDPOINT_B, vec![ENDPOINT_A, ENDPOINT_B]);
|
||||
|
||||
endpoint_a.add_connection(ENDPOINT_B, false);
|
||||
endpoint_b.add_connection(ENDPOINT_A, true);
|
||||
|
||||
(endpoint_a, endpoint_b)
|
||||
}
|
||||
|
||||
/// Transfers every queued packet for `next_hop` into `receiver` as `remote_id` traffic.
|
||||
pub(super) fn transfer_packets(
|
||||
sender: &mut Endpoint,
|
||||
receiver: &mut Endpoint,
|
||||
next_hop: u32,
|
||||
remote_id: u32,
|
||||
) {
|
||||
let mut packets = Vec::new();
|
||||
sender.take_outbound_clear(next_hop, |packet| packets.push(packet.clone()));
|
||||
|
||||
for packet in packets {
|
||||
receiver.add_inbound_from(remote_id, packet).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends one downward PTY frame from endpoint A to endpoint B.
|
||||
pub(super) fn send_downward_frame(
|
||||
endpoint_a: &mut Endpoint,
|
||||
endpoint_b: &mut Endpoint,
|
||||
hook_id: u16,
|
||||
opcode: u8,
|
||||
payload: &[u8],
|
||||
end_hook: bool,
|
||||
) {
|
||||
endpoint_a
|
||||
.add_outbound(pty_packet(
|
||||
vec![ENDPOINT_A, ENDPOINT_B],
|
||||
hook_id,
|
||||
end_hook,
|
||||
opcode,
|
||||
payload,
|
||||
))
|
||||
.unwrap();
|
||||
transfer_packets(endpoint_a, endpoint_b, ENDPOINT_B, ENDPOINT_A);
|
||||
}
|
||||
|
||||
/// Opens a fake PTY session and delivers the `Opened` response to endpoint A.
|
||||
pub(super) fn open_pty_session(
|
||||
endpoint_a: &mut Endpoint,
|
||||
endpoint_b: &mut Endpoint,
|
||||
leaf: &mut FakePtyLeaf,
|
||||
) -> u16 {
|
||||
let hook_id = endpoint_a.get_hook_id();
|
||||
endpoint_a
|
||||
.add_outbound(pty_open_packet(vec![ENDPOINT_A, ENDPOINT_B], hook_id))
|
||||
.unwrap();
|
||||
|
||||
transfer_packets(endpoint_a, endpoint_b, ENDPOINT_B, ENDPOINT_A);
|
||||
leaf.update(endpoint_b);
|
||||
transfer_packets(endpoint_b, endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
|
||||
hook_id
|
||||
}
|
||||
|
||||
/// Drains packets for `procedure_id` delivered to endpoint A.
|
||||
pub(super) fn drain_parent_packets(endpoint: &mut Endpoint, procedure_id: u32) -> Vec<Packet> {
|
||||
let mut packets = Vec::new();
|
||||
endpoint.take_inbound_matching(
|
||||
ENDPOINT_A,
|
||||
|packet| packet.procedure_id == procedure_id,
|
||||
|packet| packets.push(packet),
|
||||
);
|
||||
packets
|
||||
}
|
||||
|
||||
/// Drains PTY packets delivered to endpoint A.
|
||||
pub(super) fn drain_parent_pty_packets(endpoint: &mut Endpoint) -> Vec<Packet> {
|
||||
drain_parent_packets(endpoint, PROC_PTY)
|
||||
}
|
||||
|
||||
/// Asserts that local hook state still contains `hook_id`.
|
||||
pub(super) fn assert_hook_present(endpoint: &Endpoint, hook_id: u16) {
|
||||
assert!(endpoint.has_hook(hook_id));
|
||||
}
|
||||
|
||||
/// Asserts that local hook state no longer contains `hook_id`.
|
||||
pub(super) fn assert_hook_removed(endpoint: &Endpoint, hook_id: u16) {
|
||||
assert!(!endpoint.has_hook(hook_id));
|
||||
}
|
||||
|
||||
/// Asserts that `packet` carries the expected PTY frame.
|
||||
pub(super) fn assert_frame(
|
||||
packet: &Packet,
|
||||
hook_id: u16,
|
||||
opcode: u8,
|
||||
end_hook: bool,
|
||||
payload: &[u8],
|
||||
) {
|
||||
assert_eq!(packet.hook_id, hook_id);
|
||||
assert_eq!(packet.end_hook, end_hook);
|
||||
assert_eq!(frame_opcode(packet), Some(opcode));
|
||||
assert_eq!(frame_payload(packet), payload);
|
||||
}
|
||||
|
||||
/// Returns true when `packets` contains the requested frame.
|
||||
pub(super) fn has_frame(packets: &[Packet], hook_id: u16, opcode: u8, payload: &[u8]) -> bool {
|
||||
packets.iter().any(|packet| {
|
||||
packet.hook_id == hook_id
|
||||
&& frame_opcode(packet) == Some(opcode)
|
||||
&& frame_payload(packet) == payload
|
||||
})
|
||||
}
|
||||
|
||||
/// Asserts that a packet is the fake PTY open acknowledgement.
|
||||
pub(super) fn assert_opened(packet: &Packet, hook_id: u16) {
|
||||
assert_frame(packet, hook_id, OP_OPENED, false, &[]);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
use unshell::protocol::{Endpoint, Packet};
|
||||
|
||||
use crate::{OP_OPENED, frame_opcode, frame_payload};
|
||||
|
||||
/// Asserts that local hook state still contains `hook_id`.
|
||||
pub(crate) fn assert_hook_present(endpoint: &Endpoint, hook_id: u16) {
|
||||
assert!(
|
||||
endpoint.has_hook(hook_id),
|
||||
"expected hook {hook_id} to remain registered"
|
||||
);
|
||||
}
|
||||
|
||||
/// Asserts that local hook state no longer contains `hook_id`.
|
||||
pub(crate) fn assert_hook_removed(endpoint: &Endpoint, hook_id: u16) {
|
||||
assert!(
|
||||
!endpoint.has_hook(hook_id),
|
||||
"expected hook {hook_id} to be cleaned up"
|
||||
);
|
||||
}
|
||||
|
||||
/// Asserts that `packet` carries the expected PTY frame.
|
||||
pub(crate) fn assert_frame(
|
||||
packet: &Packet,
|
||||
hook_id: u16,
|
||||
opcode: u8,
|
||||
end_hook: bool,
|
||||
payload: &[u8],
|
||||
) {
|
||||
assert_eq!(packet.hook_id, hook_id);
|
||||
assert_eq!(packet.end_hook, end_hook);
|
||||
assert_eq!(frame_opcode(packet), Some(opcode));
|
||||
assert_eq!(frame_payload(packet), payload);
|
||||
}
|
||||
|
||||
/// Returns true when `packets` contains the requested frame.
|
||||
pub(crate) fn has_frame(packets: &[Packet], hook_id: u16, opcode: u8, payload: &[u8]) -> bool {
|
||||
packets.iter().any(|packet| {
|
||||
packet.hook_id == hook_id
|
||||
&& frame_opcode(packet) == Some(opcode)
|
||||
&& frame_payload(packet) == payload
|
||||
})
|
||||
}
|
||||
|
||||
/// Asserts that a packet is the fake PTY open acknowledgement.
|
||||
pub(crate) fn assert_opened(packet: &Packet, hook_id: u16) {
|
||||
assert_frame(packet, hook_id, OP_OPENED, false, &[]);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use unshell::protocol::{Endpoint, Packet};
|
||||
|
||||
use crate::PROC_PTY;
|
||||
|
||||
use super::ENDPOINT_A;
|
||||
|
||||
/// Drains packets for `procedure_id` delivered to endpoint A.
|
||||
pub(crate) fn drain_parent_packets(endpoint: &mut Endpoint, procedure_id: u32) -> Vec<Packet> {
|
||||
let mut packets = Vec::new();
|
||||
endpoint.take_inbound_matching(
|
||||
ENDPOINT_A,
|
||||
|packet| packet.procedure_id == procedure_id,
|
||||
|packet| packets.push(packet),
|
||||
);
|
||||
packets
|
||||
}
|
||||
|
||||
/// Drains PTY packets delivered to endpoint A.
|
||||
pub(crate) fn drain_parent_pty_packets(endpoint: &mut Endpoint) -> Vec<Packet> {
|
||||
drain_parent_packets(endpoint, PROC_PTY)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use unshell::protocol::{Endpoint, Packet};
|
||||
|
||||
pub(crate) const ENDPOINT_A: u32 = 0;
|
||||
pub(crate) const ENDPOINT_B: u32 = 1;
|
||||
pub(crate) const PROC_OTHER: u32 = 31;
|
||||
|
||||
/// Creates a bare endpoint at a known absolute path.
|
||||
pub(crate) fn endpoint_at(id: u32, path: Vec<u32>) -> Endpoint {
|
||||
let mut endpoint = Endpoint::new(id);
|
||||
endpoint.path = path;
|
||||
endpoint
|
||||
}
|
||||
|
||||
/// Creates the parent/child endpoint pair used by PTY session tests.
|
||||
pub(crate) fn pty_endpoints() -> (Endpoint, Endpoint) {
|
||||
let mut endpoint_a = endpoint_at(ENDPOINT_A, vec![ENDPOINT_A]);
|
||||
let mut endpoint_b = endpoint_at(ENDPOINT_B, vec![ENDPOINT_A, ENDPOINT_B]);
|
||||
|
||||
endpoint_a.add_connection(ENDPOINT_B, false);
|
||||
endpoint_b.add_connection(ENDPOINT_A, true);
|
||||
|
||||
(endpoint_a, endpoint_b)
|
||||
}
|
||||
|
||||
/// Transfers every queued packet for `next_hop` into `receiver` as `remote_id` traffic.
|
||||
pub(crate) fn transfer_packets(
|
||||
sender: &mut Endpoint,
|
||||
receiver: &mut Endpoint,
|
||||
next_hop: u32,
|
||||
remote_id: u32,
|
||||
) {
|
||||
let mut packets = Vec::<Packet>::new();
|
||||
sender.take_outbound_clear(next_hop, |packet| packets.push(packet.clone()));
|
||||
|
||||
for packet in packets {
|
||||
receiver.add_inbound_from(remote_id, packet).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
mod assertions;
|
||||
mod drains;
|
||||
mod endpoints;
|
||||
mod packets;
|
||||
|
||||
pub(crate) use assertions::*;
|
||||
pub(crate) use drains::*;
|
||||
pub(crate) use endpoints::*;
|
||||
pub(crate) use packets::*;
|
||||
@@ -0,0 +1,46 @@
|
||||
use alloc::vec;
|
||||
|
||||
use unshell::protocol::{Endpoint, Leaf};
|
||||
|
||||
use crate::{FakePtyLeaf, pty_open_packet, pty_packet};
|
||||
|
||||
use super::{ENDPOINT_A, ENDPOINT_B, transfer_packets};
|
||||
|
||||
/// Sends one downward PTY frame from endpoint A to endpoint B.
|
||||
pub(crate) fn send_downward_frame(
|
||||
endpoint_a: &mut Endpoint,
|
||||
endpoint_b: &mut Endpoint,
|
||||
hook_id: u16,
|
||||
opcode: u8,
|
||||
payload: &[u8],
|
||||
end_hook: bool,
|
||||
) {
|
||||
endpoint_a
|
||||
.add_outbound(pty_packet(
|
||||
vec![ENDPOINT_A, ENDPOINT_B],
|
||||
hook_id,
|
||||
end_hook,
|
||||
opcode,
|
||||
payload,
|
||||
))
|
||||
.unwrap();
|
||||
transfer_packets(endpoint_a, endpoint_b, ENDPOINT_B, ENDPOINT_A);
|
||||
}
|
||||
|
||||
/// Opens a fake PTY session and delivers the `Opened` response to endpoint A.
|
||||
pub(crate) fn open_pty_session(
|
||||
endpoint_a: &mut Endpoint,
|
||||
endpoint_b: &mut Endpoint,
|
||||
leaf: &mut FakePtyLeaf,
|
||||
) -> u16 {
|
||||
let hook_id = endpoint_a.get_hook_id();
|
||||
endpoint_a
|
||||
.add_outbound(pty_open_packet(vec![ENDPOINT_A, ENDPOINT_B], hook_id))
|
||||
.unwrap();
|
||||
|
||||
transfer_packets(endpoint_a, endpoint_b, ENDPOINT_B, ENDPOINT_A);
|
||||
leaf.update(endpoint_b);
|
||||
transfer_packets(endpoint_b, endpoint_a, ENDPOINT_A, ENDPOINT_B);
|
||||
|
||||
hook_id
|
||||
}
|
||||
Reference in New Issue
Block a user