Skip to content

Commit

Permalink
test: add more test cases (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
unknowndevQwQ authored Feb 10, 2024
2 parents c8b1eb8 + c47f599 commit d294726
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 29 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ thiserror = "1.0.40"
[dev-dependencies]
sysinfo = { version = "0.30.5", default-features = false }
spdlog-rs = {features = ["log"], version = "0.3.12"}
test-binary = "3.0.2"
base64 = "0.21.7"
linereader = "0.4.0"

[features]
default = ["clobber_environ", "comp_argv", "stack_walking", "replace_argv_element", "replace_environ_element"]
Expand Down
2 changes: 2 additions & 0 deletions testbin/set_cmdline_from_stdin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
/Cargo.lock
15 changes: 15 additions & 0 deletions testbin/set_cmdline_from_stdin/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "set_cmdline_from_stdin"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
base64 = "0.21.7"
killmyargv = { path = "../.." }

# A deliberately empty workspace section so that Cargo doesn't try to search
# upwards, just in case the parent manifest is broken. See:
# https://github.com/rust-lang/cargo/issues/10872#issuecomment-1186112506
[workspace]
36 changes: 36 additions & 0 deletions testbin/set_cmdline_from_stdin/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use std::{
error::Error,
io::{stdin, BufRead},
};

use base64::{
alphabet,
engine::{general_purpose::GeneralPurpose, GeneralPurposeConfig},
Engine,
};
use killmyargv::KillMyArgv;

fn main() -> Result<(), Box<dyn Error>> {
let stdin = stdin().lock();
let alphabet = alphabet::STANDARD;
let config = GeneralPurposeConfig::default();
let engine = GeneralPurpose::new(&alphabet, config);

let kill_my_argv = KillMyArgv::new()?;

if let Some(output_argv_max_len) = std::env::args().nth(1) {
if "true" == &output_argv_max_len {
println!("{}", kill_my_argv.max_len())
}
}

for next_cmd_line in stdin.lines() {
let cmd_line = next_cmd_line?;
let cmd_line = engine.decode(&cmd_line)?;
kill_my_argv.set(&cmd_line);

println!("set done");
}

Ok(())
}
74 changes: 45 additions & 29 deletions tests/set_cmdline.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,52 @@
use std::{error::Error, process};

use killmyargv::KillMyArgv;
use sysinfo::{Pid, ProcessRefreshKind};
use utils::{get_set_cmdline_path, set_cmdline, set_cmdline_with_child};

#[test]
fn test_set_cmdline() -> Result<(), Box<dyn Error>> {
// KillMyArgv is unsafe to call in parallel.
// When we add new tests, we must either set this variable,
// or use a `Mutex` lock
//
// assert_eq!(env!("RUST_TEST_THREADS"), 1);
let pid = Pid::from_u32(process::id());
let mut system = sysinfo::System::new();
let kill_my_argv = KillMyArgv::new()?;
fn test_set_cmdline_once() -> Result<()> {
set_cmdline(["Hello?"], [vec!["Hello?"]])?;
set_cmdline(["Hi\0there!"], [vec!["Hi", "there!"]])?;
Ok(())
}

for case_str in [
"MAGIC USED HERE\0-p LOL",
"Or\0This\0--help\0--secret=***********",
] {
kill_my_argv.set(case_str.as_bytes());
#[test]
fn test_set_cmdline_multiple_times() -> Result<()> {
// Note: On a sort of OS, when `**argv` is not continuous to `envp`,
// cmdline terminates on it's first NUL byte.
set_cmdline(
["Hello?", "Hi\0there!"],
[vec!["Hello?"], vec!["Hi", "there!"]],
)?;
Ok(())
}

let args = case_str
.split_terminator('\0')
.map(|s| s.to_owned())
.collect::<Vec<String>>();
assert!(system.refresh_process_specifics(
pid,
ProcessRefreshKind::new().with_cmd(sysinfo::UpdateKind::Always)
));
let set_cmdline_process = system.process(pid).expect("set-cmdline exits unexpectedly");
assert_eq!(set_cmdline_process.cmd(), &args);
}
#[test]
fn test_set_cmdline_truncate_max_len() -> Result<()> {
let set_cmdline_path = get_set_cmdline_path()?;
let mut child = Command::new(set_cmdline_path)
.arg("true")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let child_pid = child.id();
let child_stdin = child.stdin.take().unwrap();
let child_stdout = child.stdout.take().unwrap();

let mut reader = linereader::LineReader::new(child_stdout);
let max_len = reader.next_line().unwrap()?;
let max_len = String::from_utf8_lossy(max_len).trim().parse()?;
let expected = "o".repeat(max_len);
let input = "o".repeat(max_len * 1);
set_cmdline_with_child(
[input],
[vec![expected]],
child_stdin,
reader.into_inner(),
child_pid,
)?;
Ok(())
}

mod utils;

use std::error::Error;
use std::process::{Command, Stdio};
type Result<T> = std::result::Result<T, Box<dyn Error>>;
74 changes: 74 additions & 0 deletions tests/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use base64::alphabet::STANDARD;
use base64::engine::GeneralPurpose;
use base64::Engine;
use sysinfo::{Pid, ProcessRefreshKind};
use test_binary::build_test_binary;

pub fn get_set_cmdline_path() -> Result<OsString> {
Ok(build_test_binary("set_cmdline_from_stdin", "testbin")?)
}

pub fn set_cmdline(
inputs: impl IntoIterator<Item = impl AsRef<str>>,
results: impl IntoIterator<Item = impl IntoIterator<Item = impl AsRef<str>>>,
) -> Result<()> {
let set_cmdline_path = get_set_cmdline_path()?;
let mut set_cmdline = std::process::Command::new(set_cmdline_path)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let child_stdin = Option::take(&mut set_cmdline.stdin).unwrap();
let child_stdout = Option::take(&mut set_cmdline.stdout).unwrap();
let child_pid = set_cmdline.id();

set_cmdline_with_child(inputs, results, child_stdin, child_stdout, child_pid)
}

pub fn set_cmdline_with_child(
inputs: impl IntoIterator<Item = impl AsRef<str>>,
results: impl IntoIterator<Item = impl IntoIterator<Item = impl AsRef<str>>>,
mut child_stdin: ChildStdin,
child_stdout: ChildStdout,
child_pid: u32,
) -> Result<()> {
let mut child_stdout = linereader::LineReader::new(child_stdout);
let pid = Pid::from_u32(child_pid);
let engine = GeneralPurpose::new(&STANDARD, Default::default());
let mut system = sysinfo::System::new();

for (case_str, results) in inputs.into_iter().zip(results) {
let case_base64 = engine
.encode(case_str.as_ref())
.bytes()
.filter(|ch| ch != &b'\n')
.collect::<Vec<_>>();
child_stdin.write(&case_base64)?;
child_stdin.write(&[b'\n'])?;
child_stdin.flush()?;

if let Some(line) = child_stdout.next_line() {
line?;
}

assert!(system.refresh_process_specifics(
pid,
ProcessRefreshKind::new().with_cmd(sysinfo::UpdateKind::Always)
));
let set_cmdline_process = system.process(pid).expect("set-cmdline exits unexpectedly");
let actual_cmdline = set_cmdline_process.cmd();
let expected_cmdline = results
.into_iter()
.map(|s| s.as_ref().to_owned())
.collect::<Vec<String>>();
assert_eq!(actual_cmdline, &expected_cmdline);
}
Ok(())
}

use std::{
error::Error,
ffi::OsString,
io::Write,
process::{ChildStdin, ChildStdout, Stdio},
};
type Result<T> = std::result::Result<T, Box<dyn Error>>;

0 comments on commit d294726

Please sign in to comment.