Skip to content

Commit ac08483

Browse files
authored
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
1 parent 7fb6787 commit ac08483

File tree

8 files changed

+111
-50
lines changed

8 files changed

+111
-50
lines changed

.github/workflows/main.yml

+13-3
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,24 @@ jobs:
2323
steps:
2424
- uses: actions/checkout@v4
2525

26-
- name: Build
27-
run: cargo build --release
26+
- uses: Swatinem/rust-cache@v2
27+
28+
- name: Check formatting
29+
run: cargo fmt --all -- --check
2830

2931
- name: Lint
30-
run: cargo clippy --all-features --all-targets -- -Dwarnings
32+
run: |
33+
cargo clippy --all-targets -- -Dwarnings
34+
cargo clippy --no-default-features --all-targets -- -Dwarnings
35+
cargo clippy --features resilience --all-targets -- -Dwarnings
36+
cargo clippy --features async --all-targets -- -Dwarnings
37+
cargo clippy --all-features --all-targets -- -Dwarnings
3138
3239
- name: Run tests
3340
run: |-
41+
# Create test binaries
42+
cargo build --release --bins
43+
3444
cargo test -- --test-threads=1
3545
cargo test --no-default-features --test lib_test -- --test-threads=1
3646
cargo test --features resilience -- --test-threads=1

Cargo.toml

+12-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,26 @@ repository = "https://github.com/EphyraSoftware/proc-ctl"
99
[[bin]]
1010
name = "port-binder"
1111
path = "./sample/port-binder/main.rs"
12+
test = false
13+
doc = false
14+
doctest = false
15+
bench = false
1216

1317
[[bin]]
1418
name = "proc-runner"
1519
path = "./sample/proc-runner/main.rs"
20+
test = false
21+
doc = false
22+
doctest = false
23+
bench = false
1624

1725
[[bin]]
1826
name = "waiter"
1927
path = "./sample/waiter/main.rs"
28+
test = false
29+
doc = false
30+
doctest = false
31+
bench = false
2032

2133
[dependencies]
2234
thiserror = "1"
@@ -29,10 +41,8 @@ sysinfo = { version = "0.32.0", optional = true }
2941
procfs = "0.17"
3042

3143
[dev-dependencies]
32-
assert_cmd = "2.0.11"
3344
retry = "2.0.0"
3445
tokio = { version = "1", features = ["time", "rt", "macros"] }
35-
escargot = "0.5"
3646

3747
[features]
3848
default = ["proc"]

rust-toolchain.toml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[toolchain]
2+
channel = "1.82.0"
3+
components = ["rustfmt", "clippy"]
4+
profile = "minimal"

sample/waiter/main.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ use std::io::stdin;
22

33
fn main() {
44
println!("Waiting");
5-
stdin().read_line(&mut String::new()).unwrap();
5+
let buf = &mut String::new();
6+
stdin().read_line(buf).unwrap();
7+
println!("Waiting done with input: [{}]", buf);
68
}

src/common.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
use crate::ProcCtlError::ConfigurationError;
2-
use crate::{Pid, ProcCtlResult};
3-
1+
#[cfg(any(target_os = "linux", feature = "proc"))]
42
pub(crate) trait MaybeHasPid {
5-
fn get_pid(&self) -> Option<Pid>;
3+
fn get_pid(&self) -> Option<crate::Pid>;
64
}
75

8-
pub(crate) fn resolve_pid(maybe_has_pid: &dyn MaybeHasPid) -> ProcCtlResult<Pid> {
6+
#[cfg(any(target_os = "linux", feature = "proc"))]
7+
pub(crate) fn resolve_pid(maybe_has_pid: &dyn MaybeHasPid) -> crate::ProcCtlResult<crate::Pid> {
98
match &maybe_has_pid.get_pid() {
109
Some(pid) => Ok(*pid),
11-
None => Err(ConfigurationError("unable to resolve a pid".to_string())),
10+
None => Err(crate::ProcCtlError::ConfigurationError(
11+
"unable to resolve a pid".to_string(),
12+
)),
1213
}
1314
}

src/port_query.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::common::MaybeHasPid;
21
use crate::error::{ProcCtlError, ProcCtlResult};
32
use crate::types::{Pid, ProtocolPort};
43
use std::process::Child;
@@ -181,7 +180,8 @@ fn list_ports_for_pid(query: &PortQuery, pid: Pid) -> ProcCtlResult<Vec<Protocol
181180
Ok(out)
182181
}
183182

184-
impl MaybeHasPid for PortQuery {
183+
#[cfg(any(target_os = "linux", feature = "proc"))]
184+
impl crate::common::MaybeHasPid for PortQuery {
185185
fn get_pid(&self) -> Option<Pid> {
186186
self.process_id
187187
}

src/proc_query.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,13 @@ impl ProcQuery {
8484
/// List all processes matching the current filters.
8585
pub fn list_processes(&self) -> ProcCtlResult<Vec<ProcInfo>> {
8686
let mut sys_handle = sys_handle().lock().unwrap();
87-
sys_handle.refresh_processes(ProcessesToUpdate::All, true);
88-
let processes = sys_handle.processes();
89-
println!(
90-
"Found processes: {:?}, \r\n\r\n while looking for {:?}",
91-
processes, self.name
87+
sys_handle.refresh_processes_specifics(
88+
ProcessesToUpdate::All,
89+
true,
90+
ProcessRefreshKind::everything(),
9291
);
92+
let processes = sys_handle.processes();
93+
9394
let infos: Vec<ProcInfo> = processes
9495
.values()
9596
.filter(|p| {

tests/lib_test.rs

+64-31
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,40 @@
1-
use assert_cmd::cargo::CommandCargoExt;
2-
use retry::delay::Fixed;
3-
use retry::retry;
4-
use std::process::Command;
1+
#[cfg(any(target_os = "linux", feature = "proc"))]
2+
fn create_command_for_sample(name: &str) -> std::process::Command {
3+
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
4+
.join("target")
5+
.join("release")
6+
.join(name);
7+
#[cfg(target_os = "windows")]
8+
let path = path.with_extension("exe");
9+
10+
if !path.exists() {
11+
panic!(
12+
"{} does not exist, try running `cargo build --release --bins`",
13+
path.display()
14+
);
15+
}
16+
17+
std::process::Command::new(path)
18+
}
519

20+
#[cfg(any(target_os = "linux", feature = "proc"))]
621
struct DropChild(std::process::Child);
722

23+
#[cfg(any(target_os = "linux", feature = "proc"))]
824
impl DropChild {
9-
fn spawn(mut cmd: Command) -> Self {
25+
fn spawn(mut cmd: std::process::Command) -> Self {
1026
DropChild(cmd.spawn().expect("Failed to spawn child process"))
1127
}
1228
}
1329

30+
#[cfg(any(target_os = "linux", feature = "proc"))]
1431
impl Drop for DropChild {
1532
fn drop(&mut self) {
1633
self.0.kill().expect("Failed to kill child process");
1734
}
1835
}
1936

37+
#[cfg(any(target_os = "linux", feature = "proc"))]
2038
impl std::ops::Deref for DropChild {
2139
type Target = std::process::Child;
2240

@@ -25,6 +43,7 @@ impl std::ops::Deref for DropChild {
2543
}
2644
}
2745

46+
#[cfg(any(target_os = "linux", feature = "proc"))]
2847
impl std::ops::DerefMut for DropChild {
2948
fn deref_mut(&mut self) -> &mut Self::Target {
3049
&mut self.0
@@ -34,16 +53,18 @@ impl std::ops::DerefMut for DropChild {
3453
#[cfg(target_os = "linux")]
3554
#[test]
3655
fn port_query() {
37-
let mut binder = Command::cargo_bin("port-binder").unwrap();
38-
let mut handle = binder.spawn().unwrap();
56+
use retry::delay::Fixed;
57+
58+
let binder = create_command_for_sample("port-binder");
59+
let mut handle = DropChild::spawn(binder);
3960

4061
let query = proc_ctl::PortQuery::new()
4162
.tcp_only()
4263
.ip_v4_only()
4364
.process_id(handle.id())
4465
.expect_min_num_ports(1);
4566

46-
let ports = retry(Fixed::from_millis(100).take(10), move || query.execute()).unwrap();
67+
let ports = retry::retry(Fixed::from_millis(100).take(10), move || query.execute()).unwrap();
4768

4869
handle.kill().unwrap();
4970

@@ -53,8 +74,10 @@ fn port_query() {
5374
#[cfg(target_os = "linux")]
5475
#[test]
5576
fn port_query_which_expects_too_many_ports() {
56-
let mut binder = Command::cargo_bin("port-binder").unwrap();
57-
let mut handle = binder.spawn().unwrap();
77+
use retry::delay::Fixed;
78+
79+
let binder = create_command_for_sample("port-binder");
80+
let mut handle = DropChild::spawn(binder);
5881

5982
let query = proc_ctl::PortQuery::new()
6083
.tcp_only()
@@ -63,7 +86,7 @@ fn port_query_which_expects_too_many_ports() {
6386
.expect_min_num_ports(2);
6487

6588
// Only retry once, getting no ports is still a valid test if the child program hasn't bound yet
66-
let result = retry(Fixed::from_millis(100).take(1), move || query.execute());
89+
let result = retry::retry(Fixed::from_millis(100).take(1), move || query.execute());
6790

6891
handle.kill().unwrap();
6992

@@ -75,8 +98,8 @@ fn port_query_which_expects_too_many_ports() {
7598
fn port_query_with_sync_retry() {
7699
use std::time::Duration;
77100

78-
let mut binder = Command::cargo_bin("port-binder").unwrap();
79-
let mut handle = binder.spawn().unwrap();
101+
let binder = create_command_for_sample("port-binder");
102+
let mut handle = DropChild::spawn(binder);
80103

81104
let query = proc_ctl::PortQuery::new()
82105
.tcp_only()
@@ -98,8 +121,8 @@ fn port_query_with_sync_retry() {
98121
async fn port_query_with_async_retry() {
99122
use std::time::Duration;
100123

101-
let mut binder = Command::cargo_bin("port-binder").unwrap();
102-
let mut handle = binder.spawn().unwrap();
124+
let binder = create_command_for_sample("port-binder");
125+
let mut handle = DropChild::spawn(binder);
103126

104127
let query = proc_ctl::PortQuery::new()
105128
.tcp_only()
@@ -121,18 +144,19 @@ async fn port_query_with_async_retry() {
121144
#[test]
122145
fn proc_query_by_name() {
123146
use proc_ctl::ProcQuery;
147+
use std::process::Stdio;
124148

125-
let _cmd = escargot::CargoBuild::new().bin("waiter").run().unwrap().command().spawn().unwrap();
149+
let mut cmd = create_command_for_sample("waiter")
150+
.stdin(Stdio::piped())
151+
.stdout(Stdio::inherit())
152+
.spawn()
153+
.unwrap();
126154

127155
let query = ProcQuery::new().process_name("waiter");
128156

129-
let processes = retry(Fixed::from_millis(100).take(10), move || {
130-
match query.list_processes().ok() {
131-
Some(processes) if !processes.is_empty() => Ok(processes),
132-
_ => Err("No processes found"),
133-
}
134-
})
135-
.expect("Failed to find process in time");
157+
let processes = query.list_processes().unwrap();
158+
159+
cmd.kill().unwrap();
136160

137161
assert_eq!(1, processes.len());
138162
}
@@ -141,24 +165,25 @@ fn proc_query_by_name() {
141165
#[test]
142166
fn proc_query_for_children() {
143167
use proc_ctl::ProcQuery;
168+
use retry::delay::Fixed;
144169

145-
let binder = Command::cargo_bin("port-binder").unwrap();
170+
let binder = create_command_for_sample("port-binder");
146171
let port_binder_path = binder.get_program();
147172

148-
let mut runner = Command::cargo_bin("proc-runner").unwrap();
173+
let mut runner = create_command_for_sample("proc-runner");
149174
runner.args([port_binder_path]);
150175
let mut handle = DropChild::spawn(runner);
151176

152177
let query = ProcQuery::new()
153178
.process_id_from_child(&handle)
154179
.expect_min_num_children(1);
155180

156-
let process_names = retry(Fixed::from_millis(100).take(10), move || {
181+
let process_names = retry::retry(Fixed::from_millis(100).take(10), move || {
157182
query
158183
.children()
159184
.map(|v| v.into_iter().map(|p| p.name).collect::<Vec<String>>())
160185
})
161-
.unwrap();
186+
.unwrap();
162187

163188
handle.kill().unwrap();
164189

@@ -176,10 +201,10 @@ fn proc_query_for_children_with_retry() {
176201
use proc_ctl::ProcQuery;
177202
use std::time::Duration;
178203

179-
let binder = Command::cargo_bin("port-binder").unwrap();
204+
let binder = create_command_for_sample("port-binder");
180205
let port_binder_path = binder.get_program();
181206

182-
let mut runner = Command::cargo_bin("proc-runner").unwrap();
207+
let mut runner = create_command_for_sample("proc-runner");
183208
runner.args([port_binder_path]);
184209
let mut handle = DropChild::spawn(runner);
185210

@@ -195,6 +220,10 @@ fn proc_query_for_children_with_retry() {
195220
handle.kill().unwrap();
196221

197222
assert_eq!(1, process_names.len());
223+
224+
#[cfg(target_os = "windows")]
225+
assert_eq!("port-binder.exe", process_names.first().unwrap());
226+
#[cfg(not(target_os = "windows"))]
198227
assert_eq!("port-binder", process_names.first().unwrap());
199228
}
200229

@@ -204,10 +233,10 @@ async fn proc_query_for_children_async_with_retry() {
204233
use proc_ctl::ProcQuery;
205234
use std::time::Duration;
206235

207-
let binder = Command::cargo_bin("port-binder").unwrap();
236+
let binder = create_command_for_sample("port-binder");
208237
let port_binder_path = binder.get_program();
209238

210-
let mut runner = Command::cargo_bin("proc-runner").unwrap();
239+
let mut runner = create_command_for_sample("proc-runner");
211240
runner.args([port_binder_path]);
212241
let mut handle = DropChild::spawn(runner);
213242

@@ -224,5 +253,9 @@ async fn proc_query_for_children_async_with_retry() {
224253
handle.kill().unwrap();
225254

226255
assert_eq!(1, process_names.len());
256+
257+
#[cfg(target_os = "windows")]
258+
assert_eq!("port-binder.exe", process_names.first().unwrap());
259+
#[cfg(not(target_os = "windows"))]
227260
assert_eq!("port-binder", process_names.first().unwrap());
228261
}

0 commit comments

Comments
 (0)