add unittest for whole TCP server
Some checks failed
Rust/sat-rs/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2023-09-16 16:23:42 +02:00
parent eb5c755dd3
commit 0e6d903942
Signed by: muellerr
GPG Key ID: A649FB78196E3849
2 changed files with 98 additions and 22 deletions

View File

@ -4,7 +4,6 @@ use std::net::SocketAddr;
use std::net::{TcpListener, ToSocketAddrs}; use std::net::{TcpListener, ToSocketAddrs};
use crate::tmtc::{ReceivesTc, TmPacketSource}; use crate::tmtc::{ReceivesTc, TmPacketSource};
use core::fmt::Display;
use thiserror::Error; use thiserror::Error;
// Re-export the TMTC in COBS server. // Re-export the TMTC in COBS server.
@ -13,7 +12,7 @@ pub use crate::hal::host::tcp_with_cobs_server::{
}; };
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum TcpTmtcError<TmError: Display, TcError: Display> { pub enum TcpTmtcError<TmError, TcError> {
#[error("TM retrieval error: {0}")] #[error("TM retrieval error: {0}")]
TmError(TmError), TmError(TmError),
#[error("TC retrieval error: {0}")] #[error("TC retrieval error: {0}")]
@ -33,9 +32,9 @@ pub struct ConnectionResult {
pub(crate) struct TcpTmtcServerBase<TcError, TmError> { pub(crate) struct TcpTmtcServerBase<TcError, TmError> {
pub(crate) listener: TcpListener, pub(crate) listener: TcpListener,
pub(crate) tm_source: Box<dyn TmPacketSource<Error = TmError>>, pub(crate) tm_source: Box<dyn TmPacketSource<Error = TmError> + Send>,
pub(crate) tm_buffer: Vec<u8>, pub(crate) tm_buffer: Vec<u8>,
pub(crate) tc_receiver: Box<dyn ReceivesTc<Error = TcError>>, pub(crate) tc_receiver: Box<dyn ReceivesTc<Error = TcError> + Send>,
pub(crate) tc_buffer: Vec<u8>, pub(crate) tc_buffer: Vec<u8>,
} }
@ -43,9 +42,9 @@ impl<TcError, TmError> TcpTmtcServerBase<TcError, TmError> {
pub(crate) fn new<A: ToSocketAddrs>( pub(crate) fn new<A: ToSocketAddrs>(
addr: A, addr: A,
tm_buffer_size: usize, tm_buffer_size: usize,
tm_source: Box<dyn TmPacketSource<Error = TmError>>, tm_source: Box<dyn TmPacketSource<Error = TmError> + Send>,
tc_buffer_size: usize, tc_buffer_size: usize,
tc_receiver: Box<dyn ReceivesTc<Error = TcError>>, tc_receiver: Box<dyn ReceivesTc<Error = TcError> + Send>,
) -> Result<Self, std::io::Error> { ) -> Result<Self, std::io::Error> {
Ok(Self { Ok(Self {
listener: TcpListener::bind(addr)?, listener: TcpListener::bind(addr)?,

View File

@ -3,7 +3,6 @@ use alloc::vec;
use cobs::decode_in_place; use cobs::decode_in_place;
use cobs::encode; use cobs::encode;
use cobs::max_encoding_length; use cobs::max_encoding_length;
use core::fmt::Display;
use std::io::Read; use std::io::Read;
use std::io::Write; use std::io::Write;
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
@ -25,19 +24,18 @@ use super::tcp_server::TcpTmtcError;
/// ///
/// The server wil use the [parse_buffer_for_cobs_encoded_packets] function to parse for packets /// The server wil use the [parse_buffer_for_cobs_encoded_packets] function to parse for packets
/// and pass them to a generic TC receiver. /// and pass them to a generic TC receiver.
///
pub struct TcpTmtcInCobsServer<TcError, TmError> { pub struct TcpTmtcInCobsServer<TcError, TmError> {
base: TcpTmtcServerBase<TcError, TmError>, base: TcpTmtcServerBase<TcError, TmError>,
tm_encoding_buffer: Vec<u8>, tm_encoding_buffer: Vec<u8>,
} }
impl<TcError: 'static + Display, TmError: 'static + Display> TcpTmtcInCobsServer<TcError, TmError> { impl<TcError: 'static, TmError: 'static> TcpTmtcInCobsServer<TcError, TmError> {
pub fn new<A: ToSocketAddrs>( pub fn new<A: ToSocketAddrs>(
addr: A, addr: A,
tm_buffer_size: usize, tm_buffer_size: usize,
tm_source: Box<dyn TmPacketSource<Error = TmError>>, tm_source: Box<dyn TmPacketSource<Error = TmError> + Send>,
tc_buffer_size: usize, tc_buffer_size: usize,
tc_receiver: Box<dyn ReceivesTc<Error = TcError>>, tc_receiver: Box<dyn ReceivesTc<Error = TcError> + Send>,
) -> Result<Self, std::io::Error> { ) -> Result<Self, std::io::Error> {
Ok(Self { Ok(Self {
base: TcpTmtcServerBase::new( base: TcpTmtcServerBase::new(
@ -170,20 +168,31 @@ pub fn parse_buffer_for_cobs_encoded_packets<E>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tmtc::ReceivesTcCore; use core::{
use alloc::vec::Vec; sync::atomic::{AtomicBool, Ordering},
time::Duration,
};
use std::{
io::Write,
net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream},
sync::Mutex,
thread,
};
use crate::tmtc::{ReceivesTcCore, TmPacketSource};
use alloc::{boxed::Box, collections::VecDeque, sync::Arc, vec::Vec};
use cobs::encode; use cobs::encode;
use super::parse_buffer_for_cobs_encoded_packets; use super::{parse_buffer_for_cobs_encoded_packets, TcpTmtcInCobsServer};
const SIMPLE_PACKET: [u8; 5] = [1, 2, 3, 4, 5]; const SIMPLE_PACKET: [u8; 5] = [1, 2, 3, 4, 5];
#[derive(Default)] #[derive(Default)]
struct TestSender { struct TestTcSender {
received_tcs: Vec<Vec<u8>>, received_tcs: Vec<Vec<u8>>,
} }
impl ReceivesTcCore for TestSender { impl ReceivesTcCore for TestTcSender {
type Error = (); type Error = ();
fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> {
@ -192,6 +201,32 @@ mod tests {
} }
} }
#[derive(Default, Clone)]
struct TmSource {
shared_tm_source: Arc<Mutex<VecDeque<Vec<u8>>>>,
}
impl TmSource {
fn new() -> Self {
Self {
shared_tm_source: Default::default(),
}
}
fn add_tm(&mut self, tm: &[u8]) {
let mut shared_tm_source = self.shared_tm_source.lock().unwrap();
shared_tm_source.push_back(tm.to_vec());
}
}
impl TmPacketSource for TmSource {
type Error = ();
fn retrieve_packet(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error> {
Ok(0)
}
}
fn encode_simple_packet(encoded_buf: &mut [u8], current_idx: &mut usize) { fn encode_simple_packet(encoded_buf: &mut [u8], current_idx: &mut usize) {
encoded_buf[*current_idx] = 0; encoded_buf[*current_idx] = 0;
*current_idx += 1; *current_idx += 1;
@ -202,7 +237,7 @@ mod tests {
#[test] #[test]
fn test_parsing_simple_packet() { fn test_parsing_simple_packet() {
let mut test_sender = TestSender::default(); let mut test_sender = TestTcSender::default();
let mut encoded_buf: [u8; 16] = [0; 16]; let mut encoded_buf: [u8; 16] = [0; 16];
let mut current_idx = 0; let mut current_idx = 0;
encode_simple_packet(&mut encoded_buf, &mut current_idx); encode_simple_packet(&mut encoded_buf, &mut current_idx);
@ -221,7 +256,7 @@ mod tests {
#[test] #[test]
fn test_parsing_consecutive_packets() { fn test_parsing_consecutive_packets() {
let mut test_sender = TestSender::default(); let mut test_sender = TestTcSender::default();
let mut encoded_buf: [u8; 16] = [0; 16]; let mut encoded_buf: [u8; 16] = [0; 16];
let mut current_idx = 0; let mut current_idx = 0;
encode_simple_packet(&mut encoded_buf, &mut current_idx); encode_simple_packet(&mut encoded_buf, &mut current_idx);
@ -250,7 +285,7 @@ mod tests {
#[test] #[test]
fn test_split_tail_packet_only() { fn test_split_tail_packet_only() {
let mut test_sender = TestSender::default(); let mut test_sender = TestTcSender::default();
let mut encoded_buf: [u8; 16] = [0; 16]; let mut encoded_buf: [u8; 16] = [0; 16];
let mut current_idx = 0; let mut current_idx = 0;
encode_simple_packet(&mut encoded_buf, &mut current_idx); encode_simple_packet(&mut encoded_buf, &mut current_idx);
@ -268,7 +303,7 @@ mod tests {
} }
fn generic_test_split_packet(cut_off: usize) { fn generic_test_split_packet(cut_off: usize) {
let mut test_sender = TestSender::default(); let mut test_sender = TestTcSender::default();
let mut encoded_buf: [u8; 16] = [0; 16]; let mut encoded_buf: [u8; 16] = [0; 16];
let inverted_packet: [u8; 5] = [5, 4, 3, 2, 1]; let inverted_packet: [u8; 5] = [5, 4, 3, 2, 1];
assert!(cut_off < inverted_packet.len() + 1); assert!(cut_off < inverted_packet.len() + 1);
@ -319,7 +354,7 @@ mod tests {
#[test] #[test]
fn test_zero_at_end() { fn test_zero_at_end() {
let mut test_sender = TestSender::default(); let mut test_sender = TestTcSender::default();
let mut encoded_buf: [u8; 16] = [0; 16]; let mut encoded_buf: [u8; 16] = [0; 16];
let mut next_write_idx = 0; let mut next_write_idx = 0;
let mut current_idx = 0; let mut current_idx = 0;
@ -344,7 +379,7 @@ mod tests {
#[test] #[test]
fn test_all_zeroes() { fn test_all_zeroes() {
let mut test_sender = TestSender::default(); let mut test_sender = TestTcSender::default();
let mut all_zeroes: [u8; 5] = [0; 5]; let mut all_zeroes: [u8; 5] = [0; 5];
let mut next_write_idx = 0; let mut next_write_idx = 0;
let packets = parse_buffer_for_cobs_encoded_packets( let packets = parse_buffer_for_cobs_encoded_packets(
@ -358,4 +393,46 @@ mod tests {
assert!(test_sender.received_tcs.is_empty()); assert!(test_sender.received_tcs.is_empty());
assert_eq!(next_write_idx, 0); assert_eq!(next_write_idx, 0);
} }
#[test]
fn test_server_basic() {
let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777);
let tc_receiver = TestTcSender::default();
let tm_source = TmSource::default();
let mut tcp_server = TcpTmtcInCobsServer::new(
dest_addr,
1024,
Box::new(tm_source),
1024,
Box::new(tc_receiver),
)
.expect("TCP server generation failed");
let conn_handled: Arc<AtomicBool> = Default::default();
let set_if_done = conn_handled.clone();
// Call the connection handler in separate thread, does block.
thread::spawn(move || {
let result = tcp_server.handle_next_connection();
if result.is_err() {
panic!("handling connection failed: {:?}", result.unwrap_err());
}
set_if_done.store(true, Ordering::Relaxed);
});
// Send TC to server now.
let mut encoded_buf: [u8; 16] = [0; 16];
let mut current_idx = 0;
encode_simple_packet(&mut encoded_buf, &mut current_idx);
let mut stream = TcpStream::connect(dest_addr).expect("connecting to TCP server failed");
stream
.write_all(&encoded_buf)
.expect("writing to TCP server failed");
// A certain amount of time is allowed for the transaction to complete.
for _ in 0..3 {
if !conn_handled.load(Ordering::Relaxed) {
thread::sleep(Duration::from_millis(1));
}
}
if !conn_handled.load(Ordering::Relaxed) {
panic!("connection was not handled properly");
}
}
} }