From 91e54ae3631a02bd502be7257aadd509f1de6eaa Mon Sep 17 00:00:00 2001 From: Aram Drevekenin Date: Mon, 30 Sep 2019 19:22:25 +0200 Subject: refactor(structure): run tests as unit tests and remove lib functionality --- src/lib.rs | 115 --- src/main.rs | 126 +++- src/network/connection.rs | 42 +- src/network/dns_queue.rs | 9 +- src/network/mod.rs | 8 +- src/network/resolve_connections.rs | 6 +- src/os/linux.rs | 4 +- src/tests/fakes/fake_input.rs | 153 ++++ src/tests/fakes/fake_output.rs | 108 +++ src/tests/fakes/mod.rs | 5 + src/tests/mod.rs | 793 +++++++++++++++++++++ src/tests/snapshots/tests__basic_startup.snap | 55 ++ .../snapshots/tests__bi_directional_traffic-2.snap | 55 ++ .../snapshots/tests__bi_directional_traffic.snap | 55 ++ ...sts__multiple_connections_from_remote_ip-2.snap | 55 ++ ...tests__multiple_connections_from_remote_ip.snap | 55 ++ ...ts_of_traffic_from_different_connections-2.snap | 55 ++ ...kets_of_traffic_from_different_connections.snap | 55 ++ ...ackets_of_traffic_from_single_connection-2.snap | 55 ++ ..._packets_of_traffic_from_single_connection.snap | 55 ++ ...iple_processes_with_multiple_connections-2.snap | 55 ++ ...ltiple_processes_with_multiple_connections.snap | 55 ++ .../snapshots/tests__one_packet_of_traffic-2.snap | 55 ++ .../snapshots/tests__one_packet_of_traffic.snap | 55 ++ ...s__one_process_with_multiple_connections-2.snap | 55 ++ ...sts__one_process_with_multiple_connections.snap | 55 ++ ...ustained_traffic_from_multiple_processes-2.snap | 55 ++ ..._sustained_traffic_from_multiple_processes.snap | 55 ++ ...c_from_multiple_processes_bi_directional-2.snap | 55 ++ ...fic_from_multiple_processes_bi_directional.snap | 55 ++ ...ests__sustained_traffic_from_one_process-2.snap | 55 ++ .../tests__sustained_traffic_from_one_process.snap | 55 ++ .../tests__traffic_with_host_names-2.snap | 55 ++ .../snapshots/tests__traffic_with_host_names.snap | 55 ++ tests/cli.rs | 781 -------------------- tests/fakes/fake_input.rs | 153 ---- tests/fakes/fake_output.rs | 108 --- tests/fakes/mod.rs | 7 - tests/snapshots/cli__basic_startup.snap | 55 -- tests/snapshots/cli__bi_directional_traffic-2.snap | 55 -- tests/snapshots/cli__bi_directional_traffic.snap | 55 -- ...cli__multiple_connections_from_remote_ip-2.snap | 55 -- .../cli__multiple_connections_from_remote_ip.snap | 55 -- ...ts_of_traffic_from_different_connections-2.snap | 55 -- ...kets_of_traffic_from_different_connections.snap | 55 -- ...ackets_of_traffic_from_single_connection-2.snap | 55 -- ..._packets_of_traffic_from_single_connection.snap | 55 -- ...iple_processes_with_multiple_connections-2.snap | 55 -- ...ltiple_processes_with_multiple_connections.snap | 55 -- tests/snapshots/cli__one_packet_of_traffic-2.snap | 55 -- tests/snapshots/cli__one_packet_of_traffic.snap | 55 -- ...i__one_process_with_multiple_connections-2.snap | 55 -- ...cli__one_process_with_multiple_connections.snap | 55 -- ...ustained_traffic_from_multiple_processes-2.snap | 55 -- ..._sustained_traffic_from_multiple_processes.snap | 55 -- ...c_from_multiple_processes_bi_directional-2.snap | 55 -- ...fic_from_multiple_processes_bi_directional.snap | 55 -- .../cli__sustained_traffic_from_one_process-2.snap | 55 -- .../cli__sustained_traffic_from_one_process.snap | 55 -- .../snapshots/cli__traffic_with_host_names-2.snap | 55 -- tests/snapshots/cli__traffic_with_host_names.snap | 55 -- 61 files changed, 2477 insertions(+), 2471 deletions(-) delete mode 100644 src/lib.rs create mode 100644 src/tests/fakes/fake_input.rs create mode 100644 src/tests/fakes/fake_output.rs create mode 100644 src/tests/fakes/mod.rs create mode 100644 src/tests/mod.rs create mode 100644 src/tests/snapshots/tests__basic_startup.snap create mode 100644 src/tests/snapshots/tests__bi_directional_traffic-2.snap create mode 100644 src/tests/snapshots/tests__bi_directional_traffic.snap create mode 100644 src/tests/snapshots/tests__multiple_connections_from_remote_ip-2.snap create mode 100644 src/tests/snapshots/tests__multiple_connections_from_remote_ip.snap create mode 100644 src/tests/snapshots/tests__multiple_packets_of_traffic_from_different_connections-2.snap create mode 100644 src/tests/snapshots/tests__multiple_packets_of_traffic_from_different_connections.snap create mode 100644 src/tests/snapshots/tests__multiple_packets_of_traffic_from_single_connection-2.snap create mode 100644 src/tests/snapshots/tests__multiple_packets_of_traffic_from_single_connection.snap create mode 100644 src/tests/snapshots/tests__multiple_processes_with_multiple_connections-2.snap create mode 100644 src/tests/snapshots/tests__multiple_processes_with_multiple_connections.snap create mode 100644 src/tests/snapshots/tests__one_packet_of_traffic-2.snap create mode 100644 src/tests/snapshots/tests__one_packet_of_traffic.snap create mode 100644 src/tests/snapshots/tests__one_process_with_multiple_connections-2.snap create mode 100644 src/tests/snapshots/tests__one_process_with_multiple_connections.snap create mode 100644 src/tests/snapshots/tests__sustained_traffic_from_multiple_processes-2.snap create mode 100644 src/tests/snapshots/tests__sustained_traffic_from_multiple_processes.snap create mode 100644 src/tests/snapshots/tests__sustained_traffic_from_multiple_processes_bi_directional-2.snap create mode 100644 src/tests/snapshots/tests__sustained_traffic_from_multiple_processes_bi_directional.snap create mode 100644 src/tests/snapshots/tests__sustained_traffic_from_one_process-2.snap create mode 100644 src/tests/snapshots/tests__sustained_traffic_from_one_process.snap create mode 100644 src/tests/snapshots/tests__traffic_with_host_names-2.snap create mode 100644 src/tests/snapshots/tests__traffic_with_host_names.snap delete mode 100644 tests/cli.rs delete mode 100644 tests/fakes/fake_input.rs delete mode 100644 tests/fakes/fake_output.rs delete mode 100644 tests/fakes/mod.rs delete mode 100644 tests/snapshots/cli__basic_startup.snap delete mode 100644 tests/snapshots/cli__bi_directional_traffic-2.snap delete mode 100644 tests/snapshots/cli__bi_directional_traffic.snap delete mode 100644 tests/snapshots/cli__multiple_connections_from_remote_ip-2.snap delete mode 100644 tests/snapshots/cli__multiple_connections_from_remote_ip.snap delete mode 100644 tests/snapshots/cli__multiple_packets_of_traffic_from_different_connections-2.snap delete mode 100644 tests/snapshots/cli__multiple_packets_of_traffic_from_different_connections.snap delete mode 100644 tests/snapshots/cli__multiple_packets_of_traffic_from_single_connection-2.snap delete mode 100644 tests/snapshots/cli__multiple_packets_of_traffic_from_single_connection.snap delete mode 100644 tests/snapshots/cli__multiple_processes_with_multiple_connections-2.snap delete mode 100644 tests/snapshots/cli__multiple_processes_with_multiple_connections.snap delete mode 100644 tests/snapshots/cli__one_packet_of_traffic-2.snap delete mode 100644 tests/snapshots/cli__one_packet_of_traffic.snap delete mode 100644 tests/snapshots/cli__one_process_with_multiple_connections-2.snap delete mode 100644 tests/snapshots/cli__one_process_with_multiple_connections.snap delete mode 100644 tests/snapshots/cli__sustained_traffic_from_multiple_processes-2.snap delete mode 100644 tests/snapshots/cli__sustained_traffic_from_multiple_processes.snap delete mode 100644 tests/snapshots/cli__sustained_traffic_from_multiple_processes_bi_directional-2.snap delete mode 100644 tests/snapshots/cli__sustained_traffic_from_multiple_processes_bi_directional.snap delete mode 100644 tests/snapshots/cli__sustained_traffic_from_one_process-2.snap delete mode 100644 tests/snapshots/cli__sustained_traffic_from_one_process.snap delete mode 100644 tests/snapshots/cli__traffic_with_host_names-2.snap delete mode 100644 tests/snapshots/cli__traffic_with_host_names.snap diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 99f0a39..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -mod display; -pub mod network; - -use display::display_loop; -use network::{Connection, Sniffer, Utilization, DnsQueue, resolve_connections}; - -use ::std::net::IpAddr; - -use ::pnet::datalink::{DataLinkReceiver, NetworkInterface}; -use ::std::collections::HashMap; -use ::std::sync::atomic::{AtomicBool, Ordering}; -use ::std::sync::{Arc, Mutex}; -use ::std::{thread, time}; -use ::termion::event::{Event, Key}; -use ::tui::backend::Backend; -use ::tui::Terminal; - -pub struct OsInput { - pub network_interface: NetworkInterface, - pub network_frames: Box, - pub get_open_sockets: fn() -> HashMap, - pub keyboard_events: Box + Send + Sync + 'static>, - pub lookup_addr: Box Option + Send + Sync + 'static> -} - -pub fn start(terminal_backend: B, os_input: OsInput) -where - B: Backend + Send + 'static, -{ - let running = Arc::new(AtomicBool::new(true)); - - let keyboard_events = os_input.keyboard_events; // TODO: as methods in os_interface - let get_open_sockets = os_input.get_open_sockets; - let lookup_addr = os_input.lookup_addr; - - let stdin_handler = thread::spawn({ - let running = running.clone(); - move || { - for evt in keyboard_events { - match evt { - Event::Key(Key::Ctrl('c')) | Event::Key(Key::Char('q')) => { - // TODO: exit faster - running.store(false, Ordering::Relaxed); - break; - } - _ => (), - }; - } - } - }); - - let mut sniffer = Sniffer::new(os_input.network_interface, os_input.network_frames); - let network_utilization = Arc::new(Mutex::new(Utilization::new())); - - let dns_queue = Arc::new(DnsQueue::new()); - let ip_to_host = Arc::new(Mutex::new(HashMap::new())); - - let dns_handler = thread::spawn({ - let running = running.clone(); - let dns_queue = dns_queue.clone(); - let ip_to_host = ip_to_host.clone(); - move || { - while running.load(Ordering::Relaxed) { - let jobs = dns_queue.wait_for_jobs(); - for ip in jobs { - if let Some(addr) = lookup_addr(&IpAddr::V4(ip.clone())) { - ip_to_host.lock().unwrap().insert(ip, addr); - } - } - } - } - }); - - let display_handler = thread::spawn({ - let running = running.clone(); - let network_utilization = network_utilization.clone(); - let ip_to_host = ip_to_host.clone(); - let dns_queue = dns_queue.clone(); - move || { - let mut terminal = Terminal::new(terminal_backend).unwrap(); - terminal.clear().unwrap(); - terminal.hide_cursor().unwrap(); - while running.load(Ordering::Relaxed) { - let connections_to_procs = { - let open_sockets = get_open_sockets(); - let ip_to_host = ip_to_host.lock().unwrap(); - let (unresolved_ips, connections_to_procs) = resolve_connections(open_sockets, &ip_to_host); - dns_queue.add_ips_to_resolve(unresolved_ips); - connections_to_procs - }; - { - let mut network_utilization = network_utilization.lock().unwrap(); - let utilization = network_utilization.clone_and_reset(); - display_loop(&utilization, &mut terminal, connections_to_procs); - } - thread::sleep(time::Duration::from_secs(1)); - } - terminal.clear().unwrap(); - terminal.show_cursor().unwrap(); - dns_queue.end(); - } - }); - - let sniffing_handler = thread::spawn(move || { - while running.load(Ordering::Relaxed) { - if let Some(segment) = sniffer.next() { - network_utilization.lock().unwrap().update(&segment) - } - } - }); - display_handler.join().unwrap(); - sniffing_handler.join().unwrap(); - stdin_handler.join().unwrap(); - dns_handler.join().unwrap(); -} diff --git a/src/main.rs b/src/main.rs index c85b555..8121894 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,22 @@ +mod display; +mod network; mod os; +#[cfg(test)] +mod tests; + +use display::display_loop; +use network::{resolve_connections, Connection, DnsQueue, Sniffer, Utilization}; + +use ::std::net::IpAddr; + +use ::pnet::datalink::{DataLinkReceiver, NetworkInterface}; +use ::std::collections::HashMap; +use ::std::sync::atomic::{AtomicBool, Ordering}; +use ::std::sync::{Arc, Mutex}; +use ::std::{thread, time}; +use ::termion::event::{Event, Key}; +use ::tui::backend::Backend; +use ::tui::Terminal; use ::std::io; use ::termion::raw::IntoRawMode; @@ -19,7 +37,7 @@ fn main() { "Sorry, no implementations for platforms other than linux yet :( - PRs welcome!" ); - use os::{get_datalink_channel, get_interface, get_open_sockets, KeyboardEvents, lookup_addr}; + use os::{get_datalink_channel, get_interface, get_open_sockets, lookup_addr, KeyboardEvents}; let opt = Opt::from_args(); let stdout = io::stdout().into_raw_mode().unwrap(); @@ -30,13 +48,113 @@ fn main() { let network_frames = get_datalink_channel(&network_interface); let lookup_addr = Box::new(lookup_addr); - let os_input = what::OsInput { + let os_input = OsInput { network_interface, network_frames, get_open_sockets, keyboard_events, - lookup_addr + lookup_addr, }; - what::start(terminal_backend, os_input) + start(terminal_backend, os_input) +} + +pub struct OsInput { + pub network_interface: NetworkInterface, + pub network_frames: Box, + pub get_open_sockets: fn() -> HashMap, + pub keyboard_events: Box + Send + Sync + 'static>, + pub lookup_addr: Box Option + Send + Sync + 'static>, +} + +pub fn start(terminal_backend: B, os_input: OsInput) +where + B: Backend + Send + 'static, +{ + let running = Arc::new(AtomicBool::new(true)); + + let keyboard_events = os_input.keyboard_events; // TODO: as methods in os_interface + let get_open_sockets = os_input.get_open_sockets; + let lookup_addr = os_input.lookup_addr; + + let stdin_handler = thread::spawn({ + let running = running.clone(); + move || { + for evt in keyboard_events { + match evt { + Event::Key(Key::Ctrl('c')) | Event::Key(Key::Char('q')) => { + // TODO: exit faster + running.store(false, Ordering::Relaxed); + break; + } + _ => (), + }; + } + } + }); + + let mut sniffer = Sniffer::new(os_input.network_interface, os_input.network_frames); + let network_utilization = Arc::new(Mutex::new(Utilization::new())); + + let dns_queue = Arc::new(DnsQueue::new()); + let ip_to_host = Arc::new(Mutex::new(HashMap::new())); + + let dns_handler = thread::spawn({ + let running = running.clone(); + let dns_queue = dns_queue.clone(); + let ip_to_host = ip_to_host.clone(); + move || { + while running.load(Ordering::Relaxed) { + let jobs = dns_queue.wait_for_jobs(); + for ip in jobs { + if let Some(addr) = lookup_addr(&IpAddr::V4(ip.clone())) { + ip_to_host.lock().unwrap().insert(ip, addr); + } + } + } + } + }); + + let display_handler = thread::spawn({ + let running = running.clone(); + let network_utilization = network_utilization.clone(); + let ip_to_host = ip_to_host.clone(); + let dns_queue = dns_queue.clone(); + move || { + let mut terminal = Terminal::new(terminal_backend).unwrap(); + terminal.clear().unwrap(); + terminal.hide_cursor().unwrap(); + while running.load(Ordering::Relaxed) { + let connections_to_procs = { + let open_sockets = get_open_sockets(); + let ip_to_host = ip_to_host.lock().unwrap(); + let (unresolved_ips, connections_to_procs) = + resolve_connections(open_sockets, &ip_to_host); + dns_queue.add_ips_to_resolve(unresolved_ips); + connections_to_procs + }; + { + let mut network_utilization = network_utilization.lock().unwrap(); + let utilization = network_utilization.clone_and_reset(); + display_loop(&utilization, &mut terminal, connections_to_procs); + } + thread::sleep(time::Duration::from_secs(1)); + } + terminal.clear().unwrap(); + terminal.show_cursor().unwrap(); + dns_queue.end(); + } + }); + + let sniffing_handler = thread::spawn(move || { + while running.load(Ordering::Relaxed) { + if let Some(segment) = sniffer.next() { + network_utilization.lock().unwrap().update(&segment) + } + } + }); + display_handler.join().unwrap(); + sniffing_handler.join().unwrap(); + stdin_handler.join().unwrap(); + dns_handler.join().unwrap(); } diff --git a/src/network/connection.rs b/src/network/connection.rs index 34194c1..5af3c32 100644 --- a/src/network/connection.rs +++ b/src/network/connection.rs @@ -26,14 +26,14 @@ impl fmt::Display for Protocol { pub struct Socket { pub ip: Ipv4Addr, pub port: u16, - host_addr: Option + host_addr: Option, } impl Socket { pub fn clone_host_or_ip(&self) -> String { match &self.host_addr { Some(host_addr) => host_addr.clone(), - None => self.ip.to_string() + None => self.ip.to_string(), } } } @@ -43,7 +43,7 @@ impl Ord for Socket { let ip_eq = self.ip.cmp(&other.ip); // TODO: also port match ip_eq { Ordering::Equal => self.port.cmp(&other.port), - _ => ip_eq + _ => ip_eq, } } } @@ -72,12 +72,8 @@ impl Hash for Socket { impl fmt::Display for Socket { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.host_addr { - Some(host_addr) => { - write!(f, "{}:{}", host_addr, self.port) - }, - None => { - write!(f, "{}:{}", self.ip, self.port) - } + Some(host_addr) => write!(f, "{}:{}", host_addr, self.port), + None => write!(f, "{}:{}", self.ip, self.port), } } } @@ -106,21 +102,19 @@ impl Connection { protocol: Protocol, ) -> Option { match (local_socket, remote_socket) { - (SocketAddr::V4(local_socket), SocketAddr::V4(remote_socket)) => { - Some(Connection { - local_socket: Socket { - ip: *local_socket.ip(), - port: local_socket.port(), - host_addr: None, - }, - remote_socket: Socket { - ip: *remote_socket.ip(), - port: remote_socket.port(), - host_addr: None, - }, - protocol, - }) - }, + (SocketAddr::V4(local_socket), SocketAddr::V4(remote_socket)) => Some(Connection { + local_socket: Socket { + ip: *local_socket.ip(), + port: local_socket.port(), + host_addr: None, + }, + remote_socket: Socket { + ip: *remote_socket.ip(), + port: remote_socket.port(), + host_addr: None, + }, + protocol, + }), (_, _) => None, } } diff --git a/src/network/dns_queue.rs b/src/network/dns_queue.rs index 93728e8..3072314 100644 --- a/src/network/dns_queue.rs +++ b/src/network/dns_queue.rs @@ -1,19 +1,18 @@ - use ::std::net::Ipv4Addr; use ::std::mem::swap; -use ::std::sync::{Mutex, Condvar}; +use ::std::sync::{Condvar, Mutex}; pub struct DnsQueue { jobs: Mutex>, - cvar: Condvar + cvar: Condvar, } impl DnsQueue { pub fn new() -> Self { DnsQueue { jobs: Mutex::new(Vec::new()), - cvar: Condvar::new() + cvar: Condvar::new(), } } } @@ -28,7 +27,7 @@ impl DnsQueue { let mut jobs = self.cvar.wait(self.jobs.lock().unwrap()).unwrap(); let mut new_jobs = Vec::new(); swap(&mut new_jobs, &mut jobs); - new_jobs + new_jobs } pub fn end(&self) { self.cvar.notify_all(); diff --git a/src/network/mod.rs b/src/network/mod.rs index 3bb54d7..7590875 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -1,11 +1,11 @@ mod connection; -mod sniffer; -mod utilization; mod dns_queue; mod resolve_connections; +mod sniffer; +mod utilization; pub use connection::*; -pub use sniffer::*; -pub use utilization::*; pub use dns_queue::*; pub use resolve_connections::*; +pub use sniffer::*; +pub use utilization::*; diff --git a/src/network/resolve_connections.rs b/src/network/resolve_connections.rs index ee70ade..be09d2d 100644 --- a/src/network/resolve_connections.rs +++ b/src/network/resolve_connections.rs @@ -1,11 +1,11 @@ use crate::Connection; -use ::std::net::Ipv4Addr; use ::std::collections::HashMap; +use ::std::net::Ipv4Addr; -pub fn resolve_connections ( +pub fn resolve_connections( open_sockets: HashMap, - ip_to_host: &HashMap + ip_to_host: &HashMap, ) -> (Vec, HashMap) { let mut unresolved_ips = vec![]; let mut resolved_connections_to_procs: HashMap = HashMap::new(); diff --git a/src/os/linux.rs b/src/os/linux.rs index e14a351..bdac6c1 100644 --- a/src/os/linux.rs +++ b/src/os/linux.rs @@ -10,7 +10,7 @@ use ::std::net::IpAddr; use ::procfs::FDTarget; -use what::network::{Connection, Protocol}; +use crate::network::{Connection, Protocol}; pub struct KeyboardEvents; @@ -81,7 +81,7 @@ pub fn get_open_sockets() -> HashMap { pub fn lookup_addr(ip: &IpAddr) -> Option { if let Ok(addr) = ::dns_lookup::lookup_addr(ip) { - return Some(addr) + return Some(addr); } None } diff --git a/src/tests/fakes/fake_input.rs b/src/tests/fakes/fake_input.rs new file mode 100644 index 0000000..6007b71 --- /dev/null +++ b/src/tests/fakes/fake_input.rs @@ -0,0 +1,153 @@ +use ::ipnetwork::IpNetwork; +use ::pnet::datalink::DataLinkReceiver; +use ::pnet::datalink::NetworkInterface; +use ::std::collections::HashMap; +use ::std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use ::std::{thread, time}; +use ::termion::event::Event; + +use crate::network::{Connection, Protocol}; + +pub struct KeyboardEvents { + pub events: Vec>, +} + +impl KeyboardEvents { + pub fn new(mut events: Vec>) -> Self { + events.reverse(); // this is so that we do not have to shift the array + KeyboardEvents { events } + } +} +impl Iterator for KeyboardEvents { + type Item = Event; + fn next(&mut self) -> Option { + match self.events.pop() { + Some(ev) => { + match ev { + Some(ev) => Some(ev), // TODO: better + None => { + thread::sleep(time::Duration::from_secs(1)); + self.next() + } + } + } + None => None, + } + } +} + +pub struct NetworkFrames { + pub packets: Vec>>, + pub current_index: usize, +} + +impl NetworkFrames { + pub fn new(packets: Vec>>) -> Box { + Box::new(NetworkFrames { + packets, + current_index: 0, + }) + } + fn next_packet(&mut self) -> &Option> { + let next_index = self.current_index; + self.current_index += 1; + &self.packets[next_index] + } +} +impl DataLinkReceiver for NetworkFrames { + fn next(&mut self) -> Result<&[u8], std::io::Error> { + if self.current_index == 0 { + // make it less likely to have a race condition with the display loop + // this is so the tests pass consistently + thread::sleep(time::Duration::from_millis(500)); + } + match self.current_index < self.packets.len() { + true => { + let action = self.next_packet(); + match action { + Some(packet) => { + Ok(&packet[..]) // TODO: better + } + None => { + thread::sleep(time::Duration::from_secs(1)); + Ok(&[][..]) + } + } + } + false => { + thread::sleep(time::Duration::from_secs(1)); + Ok(&[][..]) + } + } + } +} + +pub fn get_open_sockets() -> HashMap { + let mut open_sockets = HashMap::new(); + open_sockets.insert( + Connection::new( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 443), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 12345), + Protocol::Tcp, + ) + .unwrap(), + String::from("1"), + ); + open_sockets.insert( + Connection::new( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 443), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(2, 2, 2, 2)), 54321), + Protocol::Tcp, + ) + .unwrap(), + String::from("4"), + ); + open_sockets.insert( + Connection::new( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 443), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(3, 3, 3, 3)), 1337), + Protocol::Tcp, + ) + .unwrap(), + String::from("5"), + ); + open_sockets.insert( + Connection::new( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 443), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(4, 4, 4, 4)), 1337), + Protocol::Tcp, + ) + .unwrap(), + String::from("2"), + ); + open_sockets.insert( + Connection::new( + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)), 443), + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), 12346), + Protocol::Tcp, + ) + .unwrap(), + String::from("3"), + ); + open_sockets +} + +pub fn get_interface() -> NetworkInterface { + let interface = NetworkInterface { + name: String::from("foo"), + index: 42, + mac: None, + ips: vec![IpNetwork::V4("10.0.0.2".parse().unwrap())], + flags: 42, + }; + interface +} + +pub fn create_fake_lookup_addr( + ips_to_hosts: HashMap, +) -> Box Option + Send + Sync + 'static> { + Box::new(move |ip| match ips_to_hosts.get(ip) { + Some(host) => Some(host.clone()), + None => None, + }) +} diff --git a/src/tests/fakes/fake_output.rs b/src/tests/fakes/fake_output.rs new file mode 100644 index 0000000..6fb356c --- /dev/null +++ b/src/tests/fakes/fake_output.rs @@ -0,0 +1,108 @@ +use ::std::collections::HashMap; +use ::std::io; +use ::std::sync::{Arc, Mutex}; +use ::tui::backend::Backend; +use ::tui::buffer::Cell; +use ::tui::layout::Rect; + +#[derive(Hash, Debug, PartialEq)] +pub enum TerminalEvent { + Clear, + HideCursor, + ShowCursor, + GetCursor, + Flush, + Draw, +} + +pub struct TestBackend { + pub events: Arc>>, + pub draw_events: Arc>>, + terminal_width: u16, + terminal_height: u16, +} + +impl TestBackend { + pub fn new( + log: Arc>>, + draw_log: Arc>>, + ) -> TestBackend { + TestBackend { + events: log, + draw_events: draw_log, + terminal_width: 190, + terminal_height: 50, + } + } +} + +#[derive(Hash, Eq, PartialEq)] +struct Point { + x: u16, + y: u16, +} + +impl Backend for TestBackend { + fn clear(&mut self) -> io::Result<()> { + self.events.lock().unwrap().push(TerminalEvent::Clear); + Ok(()) + } + + fn hide_cursor(&mut self) -> io::Result<()> { + self.events.lock().unwrap().push(TerminalEvent::HideCursor); + Ok(()) + } + + fn show_cursor(&mut self) -> io::Result<()> { + self.events.lock().unwrap().push(TerminalEvent::ShowCursor); + Ok(()) + } + + fn get_cursor(&mut self) -> io::Result<(u16, u16)> { + self.events.lock().unwrap().push(TerminalEvent::GetCursor); + Ok((0, 0)) + } + + fn set_cursor(&mut self, _x: u16, _y: u16) -> io::Result<()> { + Ok(()) + } + + fn draw<'a, I>(&mut self, content: I) -> io::Result<()> + where + I: Iterator, + { + // use std::fmt::Write; + self.events.lock().unwrap().push(TerminalEvent::Draw); + let mut string = String::with_capacity(content.size_hint().0 * 3); + let mut coordinates = HashMap::new(); + for (x, y, cell) in content { + coordinates.insert(Point { x, y }, cell); + } + for y in 0..self.terminal_height { + for x in 0..self.terminal_width { + match coordinates.get(&Point { x, y }) { + Some(cell) => { + // this will contain no style information at all + // should be good enough for testing + string.push_str(&cell.symbol); + } + None => { + string.push_str(" "); + } + } + } + string.push_str("\n"); + } + self.draw_events.lock().unwrap().push(string); + Ok(()) + } + + fn size(&self) -> io::Result { + Ok(Rect::new(0, 0, self.terminal_width, self.terminal_height)) + } + + fn flush(&mut self) -> io::Result<()> { + self.events.lock().unwrap().push(TerminalEvent::Flush); + Ok(()) + } +} diff --git a/src/tests/fakes/mod.rs b/src/tests/fakes/mod.rs new file mode 100644 index 0000000..dcd441b --- /dev/null +++ b/src/tests/fakes/mod.rs @@ -0,0 +1,5 @@ +mod fake_input; +mod fake_output; + +pub use fake_input::*; +pub use fake_output::*; diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..b3a5d9d --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,793 @@ +mod fakes; + +use fakes::TerminalEvent::*; +use fakes::{ + create_fake_lookup_addr, get_interface, get_open_sockets, KeyboardEvents, NetworkFrames, + TestBackend, +}; + +use ::insta::assert_snapshot; +use ::std::sync::{Arc, Mutex}; +use ::termion::event::{Event, Key}; + +use ::std::collections::HashMap; +use ::std::net::IpAddr; + +use packet_builder::payload::PayloadData; +use packet_builder::*; +use pnet::packet::Packet; +use pnet_base::MacAddr; + +fn build_tcp_packet( + source_ip: &str, + destination_ip: &str, + source_port: u16, + destination_port: u16, + payload: &'static [u8], +) -> Vec { + let mut pkt_buf = [0u8; 1500]; + let pkt = packet_builder!( + pkt_buf, + ether({set_destination => MacAddr(0,0,0,0,0,0), set_source => MacAddr(0,0,0,0,0,0)}) / + ipv4({set_source => ipv4addr!(source_ip), set_destination => ipv4addr!(destination_ip) }) / + tcp({set_source => source_port, set_destination => destination_port }) / + payload(payload) + ); + pkt.packet().to_vec() +} + +struct LogWithMirror { + pub write: Arc>, + pub mirror: Arc>, +} + +impl LogWithMirror { + pub fn new(log: T) -> Self { + let write = Arc::new(Mutex::new(log)); + let mirror = write.clone(); + LogWithMirror { write, mirror } + } +} + +#[test] +fn basic_startup() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + None, // sleep + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![Clear, HideCursor, Draw, Flush, Clear, ShowCursor]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 1); + assert_snapshot!(&terminal_draw_events_mirror[0]); +} + +#[test] +fn one_packet_of_traffic() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![Some(build_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"I am a fake tcp packet", + ))]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn bi_directional_traffic() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"I am a fake tcp upload packet", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I am a fake tcp download packet", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn multiple_packets_of_traffic_from_different_connections() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + Some(build_tcp_packet( + "2.2.2.2", + "10.0.0.2", + 54321, + 443, + b"I come from 2.2.2.2", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn multiple_packets_of_traffic_from_single_connection() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I've come from 1.1.1.1 too!", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn one_process_with_multiple_connections() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"Funny that, I'm from 3.3.3.3", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn multiple_processes_with_multiple_connections() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"Awesome, I'm from 3.3.3.3", + )), + Some(build_tcp_packet( + "2.2.2.2", + "10.0.0.2", + 54321, + 443, + b"You know, 2.2.2.2 is really nice!", + )), + Some(build_tcp_packet( + "4.4.4.4", + "10.0.0.2", + 1337, + 443, + b"I'm partial to 4.4.4.4", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn multiple_connections_from_remote_ip() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12346, + 443, + b"Me too, but on a different port", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 2); + assert_snapshot!(&terminal_draw_events_mirror[0]); + assert_snapshot!(&terminal_draw_events_mirror[1]); +} + +#[test] +fn sustained_traffic_from_one_process() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + None, // sleep + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"Same here, but one second later", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 3); + assert_snapshot!(&terminal_draw_events_mirror[1]); + assert_snapshot!(&terminal_draw_events_mirror[2]); +} + +#[test] +fn sustained_traffic_from_multiple_processes() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"I come from 3.3.3.3", + )), + None, // sleep + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"I have come from 1.1.1.1 one second later", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"I come 3.3.3.3 one second later", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 3); + assert_snapshot!(&terminal_draw_events_mirror[1]); + assert_snapshot!(&terminal_draw_events_mirror[2]); +} + +#[test] +fn sustained_traffic_from_multiple_processes_bi_directional() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "10.0.0.2", + "3.3.3.3", + 443, + 1337, + b"omw to 3.3.3.3", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"I was just there!", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"Is it nice there? I think 1.1.1.1 is dull", + )), + Some(build_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"Well, I heard 1.1.1.1 is all the rage", + )), + None, // sleep + Some(build_tcp_packet( + "10.0.0.2", + "3.3.3.3", + 443, + 1337, + b"Wait for me!", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"They're waiting for you...", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"1.1.1.1 forever!", + )), + Some(build_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"10.0.0.2 forever!", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let lookup_addr = create_fake_lookup_addr(HashMap::new()); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 3); + assert_snapshot!(&terminal_draw_events_mirror[1]); + assert_snapshot!(&terminal_draw_events_mirror[2]); +} + +#[test] +fn traffic_with_host_names() { + let keyboard_events = Box::new(KeyboardEvents::new(vec![ + None, // sleep + None, // sleep + None, // sleep + Some(Event::Key(Key::Ctrl('c'))), + ])); + let network_frames = NetworkFrames::new(vec![ + Some(build_tcp_packet( + "10.0.0.2", + "3.3.3.3", + 443, + 1337, + b"omw to 3.3.3.3", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"I was just there!", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"Is it nice there? I think 1.1.1.1 is dull", + )), + Some(build_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"Well, I heard 1.1.1.1 is all the rage", + )), + None, // sleep + Some(build_tcp_packet( + "10.0.0.2", + "3.3.3.3", + 443, + 1337, + b"Wait for me!", + )), + Some(build_tcp_packet( + "3.3.3.3", + "10.0.0.2", + 1337, + 443, + b"They're waiting for you...", + )), + Some(build_tcp_packet( + "1.1.1.1", + "10.0.0.2", + 12345, + 443, + b"1.1.1.1 forever!", + )), + Some(build_tcp_packet( + "10.0.0.2", + "1.1.1.1", + 443, + 12345, + b"10.0.0.2 forever!", + )), + ]); + + let terminal_events = LogWithMirror::new(Vec::new()); + let terminal_draw_events = LogWithMirror::new(Vec::new()); + + let backend = TestBackend::new(terminal_events.write, terminal_draw_events.write); + let network_interface = get_interface(); + let mut ips_to_hostnames = HashMap::new(); + ips_to_hostnames.insert( + IpAddr::V4("1.1.1.1".parse().unwrap()), + String::from("one.one.one.one"), + ); + ips_to_hostnames.insert( + IpAddr::V4("3.3.3.3".parse().unwrap()), + String::from("three.three.three.three"), + ); + ips_to_hostnames.insert( + IpAddr::V4("10.0.0.2".parse().unwrap()), + String::from("i-like-cheese.com"), + ); + let lookup_addr = create_fake_lookup_addr(ips_to_hostnames); + + let os_input = crate::OsInput { + network_interface, + network_frames, + get_open_sockets, + keyboard_events, + lookup_addr, + }; + crate::start(backend, os_input); + + let terminal_events_mirror = terminal_events.mirror.lock().unwrap(); + let terminal_draw_events_mirror = terminal_draw_events.mirror.lock().unwrap(); + + let expected_terminal_events = vec![ + Clear, HideCursor, Draw, Flush, Draw, Flush, Draw, Flush, Clear, ShowCursor, + ]; + assert_eq!(&terminal_events_mirror[..], &expected_terminal_events[..]); + + assert_eq!(terminal_draw_events_mirror.len(), 3); + assert_snapshot!(&terminal_draw_events_mirror[1]); + assert_snapshot!(&terminal_draw_events_mirror[2]); +} diff --git a/src/tests/snapshots/tests__basic_startup.snap b/src/tests/snapshots/tests__basic_startup.snap new file mode 100644 index 0000000..c2ae8cb --- /dev/null +++ b/src/tests/snapshots/tests__basic_startup.snap @@ -0,0 +1,55 @@ +--- +source: tests/cli.rs +expression: "&terminal_draw_events_mirror[0]" +--- +┌Utilization by connection────────────────────────────────────────────────────────────────────┐┌Utilization by process name──────────────────────────────────────────────────────────────────┐ +│Connection Processes Total Bytes Up/Down ││Process Connection Count Total Bytes │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ │└─────────────────────────────────────────────────────────────────────────────────────────────┘ +│ │┌Utilization by remote ip─────────────────────────────────────────────────────────────────────┐ +│ ││Remote Address Connection Count Total Bytes │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│