diff --git a/Cargo.lock b/Cargo.lock index 77e2350d1f..4b9befde68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,6 +1778,8 @@ dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", + "memoffset", + "pin-utils", ] [[package]] @@ -1937,6 +1939,7 @@ dependencies = [ "lazy_static", "log", "mime_guess", + "nix", "pretty_assertions", "primes", "pulldown-cmark", diff --git a/Cargo.toml b/Cargo.toml index 02be6057dd..6ec8320d25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ tower-http = { version = "0.3.3", features = ["cors"] } [dev-dependencies] executable-path = "1.0.0" +nix = "0.25.0" pretty_assertions = "1.2.1" reqwest = { version = "0.11.10", features = ["blocking"] } tempfile = "3.2.0" diff --git a/tests/command_builder.rs b/tests/command_builder.rs index 016488fb99..eba31dee57 100644 --- a/tests/command_builder.rs +++ b/tests/command_builder.rs @@ -1,8 +1,13 @@ use super::*; +enum ExpectedExitStatus { + Code(i32), + Signal(Signal), +} + pub(crate) struct CommandBuilder { args: &'static str, - expected_status: i32, + expected_exit_status: ExpectedExitStatus, expected_stderr: Expected, expected_stdout: Expected, rpc_server_url: Option, @@ -13,7 +18,7 @@ impl CommandBuilder { pub(crate) fn new(args: &'static str) -> Self { Self { args, - expected_status: 0, + expected_exit_status: ExpectedExitStatus::Code(0), expected_stderr: Expected::String(String::new()), expected_stdout: Expected::String(String::new()), rpc_server_url: None, @@ -56,41 +61,53 @@ impl CommandBuilder { } } - pub(crate) fn expected_status(self, expected_status: i32) -> Self { + pub(crate) fn expected_exit_code(self, expected_status: i32) -> Self { Self { - expected_status, + expected_exit_status: ExpectedExitStatus::Code(expected_status), ..self } } - pub(crate) fn run(self) -> TempDir { + pub(crate) fn expected_exit_signal(self, expected_signal: Signal) -> Self { + Self { + expected_exit_status: ExpectedExitStatus::Signal(expected_signal), + ..self + } + } + + pub(crate) fn command(&self) -> Command { let mut command = Command::new(executable_path("ord")); - if let Some(rpc_server_url) = self.rpc_server_url { + if let Some(rpc_server_url) = &self.rpc_server_url { let cookiefile = self.tempdir.path().join("cookie"); fs::write(&cookiefile, "username:password").unwrap(); command.args(&[ "--rpc-url", - &rpc_server_url, + rpc_server_url, "--cookie-file", cookiefile.to_str().unwrap(), ]); } - let output = command + command .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .env("HOME", self.tempdir.path()) .current_dir(&self.tempdir) - .args(self.args.split_whitespace()) - .output() - .unwrap(); + .args(self.args.split_whitespace()); + + command + } + pub(crate) fn check(self, output: Output) -> TempDir { let stdout = str::from_utf8(&output.stdout).unwrap(); let stderr = str::from_utf8(&output.stderr).unwrap(); - if output.status.code() != Some(self.expected_status) { + if match self.expected_exit_status { + ExpectedExitStatus::Code(code) => output.status.code() != Some(code), + ExpectedExitStatus::Signal(signal) => output.status.signal() != Some(signal as i32), + } { panic!( "Test failed: {}\nstdout:\n{}\nstderr:\n{}", output.status, stdout, stderr @@ -102,4 +119,9 @@ impl CommandBuilder { self.tempdir } + + pub(crate) fn run(self) -> TempDir { + let output = self.command().output().unwrap(); + self.check(output) + } } diff --git a/tests/find.rs b/tests/find.rs index 1739f243e1..82035ca395 100644 --- a/tests/find.rs +++ b/tests/find.rs @@ -15,6 +15,6 @@ fn unmined_ordinal() { CommandBuilder::new("find 5000000000") .rpc_server(&rpc_server) .expected_stderr("error: Ordinal has not been mined as of index height\n") - .expected_status(1) + .expected_exit_code(1) .run(); } diff --git a/tests/lib.rs b/tests/lib.rs index 4cd61ed3dd..a0a7fca2ec 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -3,10 +3,12 @@ use { self::{command_builder::CommandBuilder, expected::Expected}, executable_path::executable_path, + nix::{sys::signal::Signal, unistd::Pid}, regex::Regex, std::{ fs, - process::{Command, Stdio}, + os::unix::process::ExitStatusExt, + process::{Command, Output, Stdio}, str, }, tempfile::TempDir, @@ -22,6 +24,7 @@ mod info; mod list; mod parse; mod range; +mod server; mod supply; mod traits; mod version; diff --git a/tests/list.rs b/tests/list.rs index f1f67e69aa..e4d2cb6d5c 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -14,7 +14,7 @@ fn output_not_found() { let rpc_server = test_bitcoincore_rpc::spawn(); CommandBuilder::new("list 0000000000000000000000000000000000000000000000000000000000000000:0") .rpc_server(&rpc_server) - .expected_status(1) + .expected_exit_code(1) .expected_stderr("error: Output not found\n") .run(); } diff --git a/tests/parse.rs b/tests/parse.rs index d1f190c795..55cefbd0fc 100644 --- a/tests/parse.rs +++ b/tests/parse.rs @@ -11,6 +11,6 @@ fn ok() { fn err() { CommandBuilder::new("parse A") .stderr_regex("error: .*: invalid digit found in string.*") - .expected_status(2) + .expected_exit_code(2) .run(); } diff --git a/tests/server.rs b/tests/server.rs new file mode 100644 index 0000000000..0e3e342ec0 --- /dev/null +++ b/tests/server.rs @@ -0,0 +1,24 @@ +use super::*; + +#[test] +fn run() { + let rpc_server = test_bitcoincore_rpc::spawn(); + + let builder = CommandBuilder::new("server") + .rpc_server(&rpc_server) + .expected_exit_signal(Signal::SIGINT); + + let mut command = builder.command(); + + let mut child = command.spawn().unwrap(); + + nix::sys::signal::kill( + Pid::from_raw(child.id().try_into().unwrap()), + Signal::SIGINT, + ) + .unwrap(); + + child.kill().unwrap(); + + builder.check(child.wait_with_output().unwrap()); +}