use std::time::Duration; use rand::Rng; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; use tokio::time; use crate::test::LOCALHOST_IP; pub struct DummyTCPServer(TcpListener); impl DummyTCPServer { pub async fn start(port: u16) -> Self { let addr = format!("{}:{}", LOCALHOST_IP, port); println!("[DUMMY TCP] Listen on {}", addr); let listener = TcpListener::bind(addr) .await .expect("Failed to bind dummy TCP listener!"); Self(listener) } /// Receive chunk of data from following connection pub async fn read_next_connection(&self) -> Vec { let (mut conn, _addr) = self .0 .accept() .await .expect("Could not open next connection!"); let mut buff = Vec::with_capacity(100); conn.read_to_end(&mut buff).await.unwrap(); buff } /// Receive chunk of data from following connection pub async fn read_then_write_next_connection(&self, to_send: &[u8]) -> Vec { let (mut conn, _addr) = self .0 .accept() .await .expect("Could not open next connection!"); let mut buff: [u8; 100] = [0; 100]; let size = conn.read(&mut buff).await.unwrap(); conn.write_all(to_send).await.unwrap(); buff[0..size].to_vec() } /// Receive chunk of data from following connection pub async fn write_next_connection(&self, to_send: &[u8]) { let (mut conn, _addr) = self .0 .accept() .await .expect("Could not open next connection!"); conn.write_all(to_send).await.unwrap() } /// Perform complex exchange: receive numbers from client and respond with their square pub async fn next_conn_square_operations(&self) { let (mut conn, _addr) = self .0 .accept() .await .expect("Could not open next connection!"); let mut buff: [u8; 100] = [0; 100]; loop { let size = conn.read(&mut buff).await.unwrap(); if size == 0 { break; } let content = String::from_utf8_lossy(&buff[0..size]) .to_string() .parse::() .unwrap(); conn.write_all((content * content).to_string().as_bytes()) .await .unwrap(); } } pub async fn loop_conn_square_operations(&self) { loop { self.next_conn_square_operations().await } } /// Perform complex exchange: receive numbers from client and respond with their value + a given number pub async fn next_conn_add_operations(&self, inc: i32) { let (mut conn, _addr) = self .0 .accept() .await .expect("Could not open next connection!"); let mut buff: [u8; 100] = [0; 100]; loop { let size = conn.read(&mut buff).await.unwrap(); if size == 0 { break; } let content = String::from_utf8_lossy(&buff[0..size]) .to_string() .parse::() .unwrap(); conn.write_all((content + inc as i64).to_string().as_bytes()) .await .unwrap(); } } pub async fn loop_next_conn_add_operations(&self, inc: i32) { loop { self.next_conn_add_operations(inc).await } } } pub async fn dummy_tcp_client_read_conn(port: u16) -> Vec { let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port)) .await .expect("Failed to connect to dummy TCP server!"); let mut buff: [u8; 100] = [0; 100]; let size = socket.read(&mut buff).await.unwrap(); buff[0..size].to_vec() } pub async fn dummy_tcp_client_write_then_read_conn(port: u16, data: &[u8]) -> Vec { let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port)) .await .expect("Failed to connect to dummy TCP server!"); socket.write_all(data).await.unwrap(); let mut buff: [u8; 100] = [0; 100]; let size = socket.read(&mut buff).await.unwrap(); buff[0..size].to_vec() } pub async fn dummy_tcp_client_write_conn(port: u16, data: &[u8]) { let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port)) .await .expect("Failed to connect to dummy TCP server!"); socket.write_all(data).await.unwrap() } pub async fn dummy_tcp_client_square_root_requests(port: u16, num_exchanges: usize) { let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port)) .await .expect("Failed to connect to dummy TCP server!"); let mut rng = rand::thread_rng(); let mut buff: [u8; 100] = [0; 100]; for _ in 0..num_exchanges { let num = rng.gen::() % 100; socket.write_all(num.to_string().as_bytes()).await.unwrap(); let size = socket.read(&mut buff).await.unwrap(); if size == 0 { panic!("Got empty response!"); } let got = String::from_utf8_lossy(&buff[0..size]) .to_string() .parse::() .unwrap(); println!("{} * {} = {} (based on server response)", num, num, got); assert_eq!((num * num) as i64, got); } } pub async fn dummy_tcp_client_additions_requests(port: u16, inc: i32, num_exchanges: usize) { let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port)) .await .expect("Failed to connect to dummy TCP server!"); let mut rng = rand::thread_rng(); let mut buff: [u8; 100] = [0; 100]; for _ in 0..num_exchanges { let num = rng.gen::() % 100; socket.write_all(num.to_string().as_bytes()).await.unwrap(); let size = socket.read(&mut buff).await.unwrap(); if size == 0 { panic!("Got empty response!"); } let got = String::from_utf8_lossy(&buff[0..size]) .to_string() .parse::() .unwrap(); println!("{} + {} = {} (based on server response)", num, inc, got); assert_eq!((num + inc) as i64, got); } } /// Check whether a given port is open or not pub async fn is_port_open(port: u16) -> bool { match TcpStream::connect(("127.0.0.1", port)).await { Ok(_) => true, Err(_) => false, } } /// Wait for a port to become available pub async fn wait_for_port(port: u16) { for _ in 0..50 { if is_port_open(port).await { return; } time::sleep(Duration::from_millis(10)).await; } eprintln!("Port {} did not open in time!", port); std::process::exit(2); } mod test { use crate::test::dummy_tcp_sockets::{ dummy_tcp_client_additions_requests, dummy_tcp_client_read_conn, dummy_tcp_client_square_root_requests, dummy_tcp_client_write_conn, dummy_tcp_client_write_then_read_conn, DummyTCPServer, }; use crate::test::{get_port_number, PortsAllocation}; fn port(index: u16) -> u16 { get_port_number(PortsAllocation::DummyTCPServer, index) } #[tokio::test] async fn socket_read_from_server() { const DATA: &[u8] = "Hello world!!!".as_bytes(); let listener = DummyTCPServer::start(port(0)).await; let handle = tokio::spawn(async move { listener.write_next_connection(DATA).await; }); let data = dummy_tcp_client_read_conn(port(0)).await; assert_eq!(data, DATA); handle.await.unwrap(); } #[tokio::test] async fn socket_write_to_server() { const DATA: &[u8] = "Hello world 2".as_bytes(); let listener = DummyTCPServer::start(port(1)).await; tokio::spawn(async move { dummy_tcp_client_write_conn(port(1), DATA).await; }); let data = listener.read_next_connection().await; assert_eq!(data, DATA); } #[tokio::test] async fn socket_read_and_write_to_server() { const DATA_1: &[u8] = "Hello world 3a".as_bytes(); const DATA_2: &[u8] = "Hello world 3b".as_bytes(); let listener = DummyTCPServer::start(port(2)).await; let handle = tokio::spawn(async move { println!("client handle"); let data = dummy_tcp_client_write_then_read_conn(port(2), DATA_1).await; assert_eq!(data, DATA_2); }); let h2 = tokio::spawn(async move { println!("server handle"); let data = listener.read_then_write_next_connection(DATA_2).await; assert_eq!(data, DATA_1); }); handle.await.unwrap(); h2.await.unwrap(); } #[tokio::test] async fn socket_dummy_root_calculator() { let listener = DummyTCPServer::start(port(3)).await; let handle = tokio::spawn(async move { listener.next_conn_square_operations().await; }); let data = dummy_tcp_client_write_then_read_conn(port(3), "5".as_bytes()).await; assert_eq!(data, "25".as_bytes()); handle.await.unwrap(); } #[tokio::test] async fn socket_dummy_root_calculator_multiple() { let listener = DummyTCPServer::start(port(4)).await; let handle = tokio::spawn(async move { listener.next_conn_square_operations().await; }); dummy_tcp_client_square_root_requests(port(4), 10).await; handle.await.unwrap(); } #[tokio::test] async fn socket_dummy_addition_calculator() { let listener = DummyTCPServer::start(port(5)).await; let handle = tokio::spawn(async move { listener.next_conn_add_operations(4).await; }); let data = dummy_tcp_client_write_then_read_conn(port(5), "5".as_bytes()).await; assert_eq!(data, "9".as_bytes()); handle.await.unwrap(); } #[tokio::test] async fn socket_dummy_addition_calculator_multiple() { let listener = DummyTCPServer::start(port(6)).await; let handle = tokio::spawn(async move { listener.next_conn_add_operations(7).await; }); dummy_tcp_client_additions_requests(port(6), 7, 10).await; handle.await.unwrap(); } }