From ac084831e9aed61cdfd679c795fa29acb6252445 Mon Sep 17 00:00:00 2001 From: ThetaSinner Date: Mon, 28 Oct 2024 01:27:26 +0000 Subject: [PATCH] Minimal platform support (#1) * Add another process * List processes * Print before exit * Print before exit and show processes after expected launch * Log input when waiter receives it * Explicitly make stdin piped * Get to the tests quicker * stdin piped, out inherit * Don't build binaries during test * Use prebuilt binaries * Tidy * Re-enable all CI checks * Fix resolve_pid include logic and remove unused deps * Ensure retry is included * Check formatting * Add a toolchain definition * Update Linux only tests * Remove unwraps that aren't needed * Fix lint errors * Lint errors * Lint with default features * Add cache --- .github/workflows/main.yml | 16 +++++-- Cargo.toml | 14 +++++- rust-toolchain.toml | 4 ++ sample/waiter/main.rs | 4 +- src/common.rs | 13 +++--- src/port_query.rs | 4 +- src/proc_query.rs | 11 +++-- tests/lib_test.rs | 95 +++++++++++++++++++++++++------------- 8 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7ef55de..8ef4982 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,14 +23,24 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Build - run: cargo build --release + - uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: cargo fmt --all -- --check - name: Lint - run: cargo clippy --all-features --all-targets -- -Dwarnings + run: | + cargo clippy --all-targets -- -Dwarnings + cargo clippy --no-default-features --all-targets -- -Dwarnings + cargo clippy --features resilience --all-targets -- -Dwarnings + cargo clippy --features async --all-targets -- -Dwarnings + cargo clippy --all-features --all-targets -- -Dwarnings - name: Run tests run: |- + # Create test binaries + cargo build --release --bins + cargo test -- --test-threads=1 cargo test --no-default-features --test lib_test -- --test-threads=1 cargo test --features resilience -- --test-threads=1 diff --git a/Cargo.toml b/Cargo.toml index ea2131c..b124f9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,14 +9,26 @@ repository = "https://github.com/EphyraSoftware/proc-ctl" [[bin]] name = "port-binder" path = "./sample/port-binder/main.rs" +test = false +doc = false +doctest = false +bench = false [[bin]] name = "proc-runner" path = "./sample/proc-runner/main.rs" +test = false +doc = false +doctest = false +bench = false [[bin]] name = "waiter" path = "./sample/waiter/main.rs" +test = false +doc = false +doctest = false +bench = false [dependencies] thiserror = "1" @@ -29,10 +41,8 @@ sysinfo = { version = "0.32.0", optional = true } procfs = "0.17" [dev-dependencies] -assert_cmd = "2.0.11" retry = "2.0.0" tokio = { version = "1", features = ["time", "rt", "macros"] } -escargot = "0.5" [features] default = ["proc"] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..0127c34 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.82.0" +components = ["rustfmt", "clippy"] +profile = "minimal" diff --git a/sample/waiter/main.rs b/sample/waiter/main.rs index 5a2de17..bbaaebe 100644 --- a/sample/waiter/main.rs +++ b/sample/waiter/main.rs @@ -2,5 +2,7 @@ use std::io::stdin; fn main() { println!("Waiting"); - stdin().read_line(&mut String::new()).unwrap(); + let buf = &mut String::new(); + stdin().read_line(buf).unwrap(); + println!("Waiting done with input: [{}]", buf); } diff --git a/src/common.rs b/src/common.rs index f44a5bf..3f42fe6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,13 +1,14 @@ -use crate::ProcCtlError::ConfigurationError; -use crate::{Pid, ProcCtlResult}; - +#[cfg(any(target_os = "linux", feature = "proc"))] pub(crate) trait MaybeHasPid { - fn get_pid(&self) -> Option; + fn get_pid(&self) -> Option; } -pub(crate) fn resolve_pid(maybe_has_pid: &dyn MaybeHasPid) -> ProcCtlResult { +#[cfg(any(target_os = "linux", feature = "proc"))] +pub(crate) fn resolve_pid(maybe_has_pid: &dyn MaybeHasPid) -> crate::ProcCtlResult { match &maybe_has_pid.get_pid() { Some(pid) => Ok(*pid), - None => Err(ConfigurationError("unable to resolve a pid".to_string())), + None => Err(crate::ProcCtlError::ConfigurationError( + "unable to resolve a pid".to_string(), + )), } } diff --git a/src/port_query.rs b/src/port_query.rs index 76815c8..970f7ea 100644 --- a/src/port_query.rs +++ b/src/port_query.rs @@ -1,4 +1,3 @@ -use crate::common::MaybeHasPid; use crate::error::{ProcCtlError, ProcCtlResult}; use crate::types::{Pid, ProtocolPort}; use std::process::Child; @@ -181,7 +180,8 @@ fn list_ports_for_pid(query: &PortQuery, pid: Pid) -> ProcCtlResult Option { self.process_id } diff --git a/src/proc_query.rs b/src/proc_query.rs index 7755269..4571a92 100644 --- a/src/proc_query.rs +++ b/src/proc_query.rs @@ -84,12 +84,13 @@ impl ProcQuery { /// List all processes matching the current filters. pub fn list_processes(&self) -> ProcCtlResult> { let mut sys_handle = sys_handle().lock().unwrap(); - sys_handle.refresh_processes(ProcessesToUpdate::All, true); - let processes = sys_handle.processes(); - println!( - "Found processes: {:?}, \r\n\r\n while looking for {:?}", - processes, self.name + sys_handle.refresh_processes_specifics( + ProcessesToUpdate::All, + true, + ProcessRefreshKind::everything(), ); + let processes = sys_handle.processes(); + let infos: Vec = processes .values() .filter(|p| { diff --git a/tests/lib_test.rs b/tests/lib_test.rs index aadf653..5a2bd51 100644 --- a/tests/lib_test.rs +++ b/tests/lib_test.rs @@ -1,22 +1,40 @@ -use assert_cmd::cargo::CommandCargoExt; -use retry::delay::Fixed; -use retry::retry; -use std::process::Command; +#[cfg(any(target_os = "linux", feature = "proc"))] +fn create_command_for_sample(name: &str) -> std::process::Command { + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("target") + .join("release") + .join(name); + #[cfg(target_os = "windows")] + let path = path.with_extension("exe"); + + if !path.exists() { + panic!( + "{} does not exist, try running `cargo build --release --bins`", + path.display() + ); + } + + std::process::Command::new(path) +} +#[cfg(any(target_os = "linux", feature = "proc"))] struct DropChild(std::process::Child); +#[cfg(any(target_os = "linux", feature = "proc"))] impl DropChild { - fn spawn(mut cmd: Command) -> Self { + fn spawn(mut cmd: std::process::Command) -> Self { DropChild(cmd.spawn().expect("Failed to spawn child process")) } } +#[cfg(any(target_os = "linux", feature = "proc"))] impl Drop for DropChild { fn drop(&mut self) { self.0.kill().expect("Failed to kill child process"); } } +#[cfg(any(target_os = "linux", feature = "proc"))] impl std::ops::Deref for DropChild { type Target = std::process::Child; @@ -25,6 +43,7 @@ impl std::ops::Deref for DropChild { } } +#[cfg(any(target_os = "linux", feature = "proc"))] impl std::ops::DerefMut for DropChild { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 @@ -34,8 +53,10 @@ impl std::ops::DerefMut for DropChild { #[cfg(target_os = "linux")] #[test] fn port_query() { - let mut binder = Command::cargo_bin("port-binder").unwrap(); - let mut handle = binder.spawn().unwrap(); + use retry::delay::Fixed; + + let binder = create_command_for_sample("port-binder"); + let mut handle = DropChild::spawn(binder); let query = proc_ctl::PortQuery::new() .tcp_only() @@ -43,7 +64,7 @@ fn port_query() { .process_id(handle.id()) .expect_min_num_ports(1); - let ports = retry(Fixed::from_millis(100).take(10), move || query.execute()).unwrap(); + let ports = retry::retry(Fixed::from_millis(100).take(10), move || query.execute()).unwrap(); handle.kill().unwrap(); @@ -53,8 +74,10 @@ fn port_query() { #[cfg(target_os = "linux")] #[test] fn port_query_which_expects_too_many_ports() { - let mut binder = Command::cargo_bin("port-binder").unwrap(); - let mut handle = binder.spawn().unwrap(); + use retry::delay::Fixed; + + let binder = create_command_for_sample("port-binder"); + let mut handle = DropChild::spawn(binder); let query = proc_ctl::PortQuery::new() .tcp_only() @@ -63,7 +86,7 @@ fn port_query_which_expects_too_many_ports() { .expect_min_num_ports(2); // Only retry once, getting no ports is still a valid test if the child program hasn't bound yet - let result = retry(Fixed::from_millis(100).take(1), move || query.execute()); + let result = retry::retry(Fixed::from_millis(100).take(1), move || query.execute()); handle.kill().unwrap(); @@ -75,8 +98,8 @@ fn port_query_which_expects_too_many_ports() { fn port_query_with_sync_retry() { use std::time::Duration; - let mut binder = Command::cargo_bin("port-binder").unwrap(); - let mut handle = binder.spawn().unwrap(); + let binder = create_command_for_sample("port-binder"); + let mut handle = DropChild::spawn(binder); let query = proc_ctl::PortQuery::new() .tcp_only() @@ -98,8 +121,8 @@ fn port_query_with_sync_retry() { async fn port_query_with_async_retry() { use std::time::Duration; - let mut binder = Command::cargo_bin("port-binder").unwrap(); - let mut handle = binder.spawn().unwrap(); + let binder = create_command_for_sample("port-binder"); + let mut handle = DropChild::spawn(binder); let query = proc_ctl::PortQuery::new() .tcp_only() @@ -121,18 +144,19 @@ async fn port_query_with_async_retry() { #[test] fn proc_query_by_name() { use proc_ctl::ProcQuery; + use std::process::Stdio; - let _cmd = escargot::CargoBuild::new().bin("waiter").run().unwrap().command().spawn().unwrap(); + let mut cmd = create_command_for_sample("waiter") + .stdin(Stdio::piped()) + .stdout(Stdio::inherit()) + .spawn() + .unwrap(); let query = ProcQuery::new().process_name("waiter"); - let processes = retry(Fixed::from_millis(100).take(10), move || { - match query.list_processes().ok() { - Some(processes) if !processes.is_empty() => Ok(processes), - _ => Err("No processes found"), - } - }) - .expect("Failed to find process in time"); + let processes = query.list_processes().unwrap(); + + cmd.kill().unwrap(); assert_eq!(1, processes.len()); } @@ -141,11 +165,12 @@ fn proc_query_by_name() { #[test] fn proc_query_for_children() { use proc_ctl::ProcQuery; + use retry::delay::Fixed; - let binder = Command::cargo_bin("port-binder").unwrap(); + let binder = create_command_for_sample("port-binder"); let port_binder_path = binder.get_program(); - let mut runner = Command::cargo_bin("proc-runner").unwrap(); + let mut runner = create_command_for_sample("proc-runner"); runner.args([port_binder_path]); let mut handle = DropChild::spawn(runner); @@ -153,12 +178,12 @@ fn proc_query_for_children() { .process_id_from_child(&handle) .expect_min_num_children(1); - let process_names = retry(Fixed::from_millis(100).take(10), move || { + let process_names = retry::retry(Fixed::from_millis(100).take(10), move || { query .children() .map(|v| v.into_iter().map(|p| p.name).collect::>()) }) - .unwrap(); + .unwrap(); handle.kill().unwrap(); @@ -176,10 +201,10 @@ fn proc_query_for_children_with_retry() { use proc_ctl::ProcQuery; use std::time::Duration; - let binder = Command::cargo_bin("port-binder").unwrap(); + let binder = create_command_for_sample("port-binder"); let port_binder_path = binder.get_program(); - let mut runner = Command::cargo_bin("proc-runner").unwrap(); + let mut runner = create_command_for_sample("proc-runner"); runner.args([port_binder_path]); let mut handle = DropChild::spawn(runner); @@ -195,6 +220,10 @@ fn proc_query_for_children_with_retry() { handle.kill().unwrap(); assert_eq!(1, process_names.len()); + + #[cfg(target_os = "windows")] + assert_eq!("port-binder.exe", process_names.first().unwrap()); + #[cfg(not(target_os = "windows"))] assert_eq!("port-binder", process_names.first().unwrap()); } @@ -204,10 +233,10 @@ async fn proc_query_for_children_async_with_retry() { use proc_ctl::ProcQuery; use std::time::Duration; - let binder = Command::cargo_bin("port-binder").unwrap(); + let binder = create_command_for_sample("port-binder"); let port_binder_path = binder.get_program(); - let mut runner = Command::cargo_bin("proc-runner").unwrap(); + let mut runner = create_command_for_sample("proc-runner"); runner.args([port_binder_path]); let mut handle = DropChild::spawn(runner); @@ -224,5 +253,9 @@ async fn proc_query_for_children_async_with_retry() { handle.kill().unwrap(); assert_eq!(1, process_names.len()); + + #[cfg(target_os = "windows")] + assert_eq!("port-binder.exe", process_names.first().unwrap()); + #[cfg(not(target_os = "windows"))] assert_eq!("port-binder", process_names.first().unwrap()); }