Rewrite acceptance test matching
- Add a custom semver match for `zebrad` versions - Prefer "line contains string" matches, so tests ignore minor changes - Escape regex meta-characters when a literal string match is intended - Rename test functions so they are more precise - Rewrite match internals to remove duplicate code and enable custom matches - Document match functions
This commit is contained in:
parent
fc0edb5c44
commit
56ef08e385
|
@ -19,7 +19,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"regex",
|
||||
"secrecy",
|
||||
"semver",
|
||||
"semver 0.9.0",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
"termcolor",
|
||||
|
@ -2954,7 +2954,7 @@ version = "0.2.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3064,6 +3064,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f3aac57ee7f3272d8395c6e4f502f434f0e289fcd62876f70daa008c20dcabe"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
|
@ -4622,6 +4628,8 @@ dependencies = [
|
|||
"once_cell",
|
||||
"pin-project 0.4.27",
|
||||
"rand 0.8.1",
|
||||
"regex",
|
||||
"semver 1.0.3",
|
||||
"sentry",
|
||||
"sentry-tracing",
|
||||
"serde",
|
||||
|
|
|
@ -133,7 +133,7 @@ lazy_static! {
|
|||
/// OS-specific error when the port attempting to be opened is already in use.
|
||||
pub static ref PORT_IN_USE_ERROR: Regex = if cfg!(unix) {
|
||||
#[allow(clippy::trivial_regex)]
|
||||
Regex::new("already in use")
|
||||
Regex::new(®ex::escape("already in use"))
|
||||
} else {
|
||||
Regex::new("(access a socket in a way forbidden by its access permissions)|(Only one usage of each socket address)")
|
||||
}.expect("regex is valid");
|
||||
|
|
|
@ -10,7 +10,7 @@ use tracing::instrument;
|
|||
use std::os::unix::process::ExitStatusExt;
|
||||
use std::{
|
||||
convert::Infallible as NoDir,
|
||||
fmt::Write as _,
|
||||
fmt::{self, Write as _},
|
||||
io::BufRead,
|
||||
io::{BufReader, Lines, Read},
|
||||
path::Path,
|
||||
|
@ -194,7 +194,7 @@ impl<T> TestChild<T> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Set a timeout for `expect_stdout` or `expect_stderr`.
|
||||
/// Set a timeout for `expect_stdout_line_matches` or `expect_stderr_line_matches`.
|
||||
///
|
||||
/// Does not apply to `wait_with_output`.
|
||||
pub fn with_timeout(mut self, timeout: Duration) -> Self {
|
||||
|
@ -215,7 +215,7 @@ impl<T> TestChild<T> {
|
|||
/// Kills the child after the configured timeout has elapsed.
|
||||
/// See `expect_line_matching` for details.
|
||||
#[instrument(skip(self))]
|
||||
pub fn expect_stdout(&mut self, regex: &str) -> Result<&mut Self> {
|
||||
pub fn expect_stdout_line_matches(&mut self, regex: &str) -> Result<&mut Self> {
|
||||
if self.stdout.is_none() {
|
||||
self.stdout = self
|
||||
.child
|
||||
|
@ -228,7 +228,7 @@ impl<T> TestChild<T> {
|
|||
let mut lines = self
|
||||
.stdout
|
||||
.take()
|
||||
.expect("child must capture stdout to call expect_stdout");
|
||||
.expect("child must capture stdout to call expect_stdout_line_matches");
|
||||
|
||||
match self.expect_line_matching(&mut lines, regex, "stdout") {
|
||||
Ok(()) => {
|
||||
|
@ -245,7 +245,7 @@ impl<T> TestChild<T> {
|
|||
/// Kills the child after the configured timeout has elapsed.
|
||||
/// See `expect_line_matching` for details.
|
||||
#[instrument(skip(self))]
|
||||
pub fn expect_stderr(&mut self, regex: &str) -> Result<&mut Self> {
|
||||
pub fn expect_stderr_line_matches(&mut self, regex: &str) -> Result<&mut Self> {
|
||||
if self.stderr.is_none() {
|
||||
self.stderr = self
|
||||
.child
|
||||
|
@ -258,7 +258,7 @@ impl<T> TestChild<T> {
|
|||
let mut lines = self
|
||||
.stderr
|
||||
.take()
|
||||
.expect("child must capture stderr to call expect_stderr");
|
||||
.expect("child must capture stderr to call expect_stderr_line_matches");
|
||||
|
||||
match self.expect_line_matching(&mut lines, regex, "stderr") {
|
||||
Ok(()) => {
|
||||
|
@ -399,94 +399,181 @@ impl<T> TestOutput<T> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub fn stdout_contains(&self, regex: &str) -> Result<&Self> {
|
||||
let re = regex::Regex::new(regex)?;
|
||||
let stdout = String::from_utf8_lossy(&self.output.stdout);
|
||||
/// Checks the output of a command, using a closure to determine if the
|
||||
/// output is valid.
|
||||
///
|
||||
/// If the closure returns `true`, the check returns `Ok(self)`.
|
||||
/// If the closure returns `false`, the check returns an error containing
|
||||
/// `output_name` and `err_msg`, with context from the command.
|
||||
///
|
||||
/// `output` is typically `self.output.stdout` or `self.output.stderr`.
|
||||
#[instrument(skip(self, output_predicate, output))]
|
||||
pub fn output_check<P>(
|
||||
&self,
|
||||
output_predicate: P,
|
||||
output: &[u8],
|
||||
output_name: impl ToString + fmt::Debug,
|
||||
err_msg: impl ToString + fmt::Debug,
|
||||
) -> Result<&Self>
|
||||
where
|
||||
P: FnOnce(&str) -> bool,
|
||||
{
|
||||
let output = String::from_utf8_lossy(output);
|
||||
|
||||
for line in stdout.lines() {
|
||||
if re.is_match(line) {
|
||||
return Ok(self);
|
||||
}
|
||||
}
|
||||
|
||||
Err(eyre!(
|
||||
"stdout of command did not contain any matches for the given regex"
|
||||
))
|
||||
.context_from(self)
|
||||
.with_section(|| format!("{:?}", regex).header("Match Regex:"))
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub fn stdout_equals(&self, s: &str) -> Result<&Self> {
|
||||
let stdout = String::from_utf8_lossy(&self.output.stdout);
|
||||
|
||||
if stdout == s {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
Err(eyre!("stdout of command is not equal the given string"))
|
||||
if output_predicate(&output) {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre!(
|
||||
"{} of command did not {}",
|
||||
output_name.to_string(),
|
||||
err_msg.to_string()
|
||||
))
|
||||
.context_from(self)
|
||||
.with_section(|| format!("{:?}", s).header("Match String:"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks each line in the output of a command, using a closure to determine
|
||||
/// if the line is valid.
|
||||
///
|
||||
/// See [`output_check`] for details.
|
||||
#[instrument(skip(self, line_predicate, output))]
|
||||
pub fn any_output_line<P>(
|
||||
&self,
|
||||
mut line_predicate: P,
|
||||
output: &[u8],
|
||||
output_name: impl ToString + fmt::Debug,
|
||||
err_msg: impl ToString + fmt::Debug,
|
||||
) -> Result<&Self>
|
||||
where
|
||||
P: FnMut(&str) -> bool,
|
||||
{
|
||||
let output_predicate = |stdout: &str| {
|
||||
for line in stdout.lines() {
|
||||
if line_predicate(line) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
|
||||
self.output_check(
|
||||
output_predicate,
|
||||
output,
|
||||
output_name,
|
||||
format!("have any lines that {}", err_msg.to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Tests if any lines in the output of a command contain `s`.
|
||||
///
|
||||
/// See [`any_output_line`] for details.
|
||||
#[instrument(skip(self, output))]
|
||||
pub fn any_output_line_contains(
|
||||
&self,
|
||||
s: &str,
|
||||
output: &[u8],
|
||||
output_name: impl ToString + fmt::Debug,
|
||||
err_msg: impl ToString + fmt::Debug,
|
||||
) -> Result<&Self> {
|
||||
self.any_output_line(
|
||||
|line| line.contains(s),
|
||||
output,
|
||||
output_name,
|
||||
format!("contain {}", err_msg.to_string()),
|
||||
)
|
||||
.with_section(|| format!("{:?}", s).header("Match String:"))
|
||||
}
|
||||
|
||||
/// Tests if standard output contains `s`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stdout_contains(&self, s: &str) -> Result<&Self> {
|
||||
self.output_check(
|
||||
|stdout| stdout.contains(s),
|
||||
&self.output.stdout,
|
||||
"stdout",
|
||||
"contain the given string",
|
||||
)
|
||||
.with_section(|| format!("{:?}", s).header("Match String:"))
|
||||
}
|
||||
|
||||
/// Tests if standard output matches `regex`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stdout_matches(&self, regex: &str) -> Result<&Self> {
|
||||
let re = regex::Regex::new(regex)?;
|
||||
let stdout = String::from_utf8_lossy(&self.output.stdout);
|
||||
|
||||
if re.is_match(&stdout) {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
Err(eyre!("stdout of command is not equal to the given regex"))
|
||||
.context_from(self)
|
||||
.with_section(|| format!("{:?}", regex).header("Match Regex:"))
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
pub fn stderr_contains(&self, regex: &str) -> Result<&Self> {
|
||||
let re = regex::Regex::new(regex)?;
|
||||
let stderr = String::from_utf8_lossy(&self.output.stderr);
|
||||
|
||||
for line in stderr.lines() {
|
||||
if re.is_match(line) {
|
||||
return Ok(self);
|
||||
}
|
||||
}
|
||||
|
||||
Err(eyre!(
|
||||
"stderr of command did not contain any matches for the given regex"
|
||||
))
|
||||
.context_from(self)
|
||||
self.output_check(
|
||||
|stdout| re.is_match(stdout),
|
||||
&self.output.stdout,
|
||||
"stdout",
|
||||
"matched the given regex",
|
||||
)
|
||||
.with_section(|| format!("{:?}", regex).header("Match Regex:"))
|
||||
}
|
||||
|
||||
/// Tests if any lines in standard output contain `s`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stderr_equals(&self, s: &str) -> Result<&Self> {
|
||||
let stderr = String::from_utf8_lossy(&self.output.stderr);
|
||||
|
||||
if stderr == s {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
Err(eyre!("stderr of command is not equal the given string"))
|
||||
.context_from(self)
|
||||
.with_section(|| format!("{:?}", s).header("Match String:"))
|
||||
pub fn stdout_line_contains(&self, s: &str) -> Result<&Self> {
|
||||
self.any_output_line_contains(s, &self.output.stdout, "stdout", "the given string")
|
||||
}
|
||||
|
||||
/// Tests if any lines in standard output match `regex`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stdout_line_matches(&self, regex: &str) -> Result<&Self> {
|
||||
let re = regex::Regex::new(regex)?;
|
||||
|
||||
self.any_output_line(
|
||||
|line| re.is_match(line),
|
||||
&self.output.stdout,
|
||||
"stdout",
|
||||
"matched the given regex",
|
||||
)
|
||||
.with_section(|| format!("{:?}", regex).header("Line Match Regex:"))
|
||||
}
|
||||
|
||||
/// Tests if standard error contains `s`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stderr_contains(&self, s: &str) -> Result<&Self> {
|
||||
self.output_check(
|
||||
|stderr| stderr.contains(s),
|
||||
&self.output.stderr,
|
||||
"stderr",
|
||||
"contain the given string",
|
||||
)
|
||||
.with_section(|| format!("{:?}", s).header("Match String:"))
|
||||
}
|
||||
|
||||
/// Tests if standard error matches `regex`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stderr_matches(&self, regex: &str) -> Result<&Self> {
|
||||
let re = regex::Regex::new(regex)?;
|
||||
let stderr = String::from_utf8_lossy(&self.output.stderr);
|
||||
|
||||
if re.is_match(&stderr) {
|
||||
return Ok(self);
|
||||
}
|
||||
self.output_check(
|
||||
|stderr| re.is_match(stderr),
|
||||
&self.output.stderr,
|
||||
"stderr",
|
||||
"matched the given regex",
|
||||
)
|
||||
.with_section(|| format!("{:?}", regex).header("Match Regex:"))
|
||||
}
|
||||
|
||||
Err(eyre!("stderr of command is not equal to the given regex"))
|
||||
.context_from(self)
|
||||
.with_section(|| format!("{:?}", regex).header("Match Regex:"))
|
||||
/// Tests if any lines in standard error contain `s`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stderr_line_contains(&self, s: &str) -> Result<&Self> {
|
||||
self.any_output_line_contains(s, &self.output.stderr, "stderr", "the given string")
|
||||
}
|
||||
|
||||
/// Tests if any lines in standard error match `regex`.
|
||||
#[instrument(skip(self))]
|
||||
pub fn stderr_line_matches(&self, regex: &str) -> Result<&Self> {
|
||||
let re = regex::Regex::new(regex)?;
|
||||
|
||||
self.any_output_line(
|
||||
|line| re.is_match(line),
|
||||
&self.output.stderr,
|
||||
"stderr",
|
||||
"matched the given regex",
|
||||
)
|
||||
.with_section(|| format!("{:?}", regex).header("Line Match Regex:"))
|
||||
}
|
||||
|
||||
/// Returns Ok if the program was killed, Err(Report) if exit was by another
|
||||
|
|
|
@ -63,9 +63,11 @@ fn kill_on_timeout_output_continuous_lines() -> Result<()> {
|
|||
.spawn_child_with_command(TEST_CMD, &["-v", "/dev/zero"])?
|
||||
.with_timeout(Duration::from_secs(2));
|
||||
|
||||
// We need to use expect_stdout, because wait_with_output ignores timeouts.
|
||||
// We need to use expect_stdout_line_matches, because wait_with_output ignores timeouts.
|
||||
// We use a non-matching regex, to trigger the timeout.
|
||||
assert!(child.expect_stdout("this regex should not match").is_err());
|
||||
assert!(child
|
||||
.expect_stdout_line_matches("this regex should not match")
|
||||
.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -88,9 +90,11 @@ fn finish_before_timeout_output_single_line() -> Result<()> {
|
|||
.spawn_child_with_command(TEST_CMD, &["zebra_test_output"])?
|
||||
.with_timeout(Duration::from_secs(2));
|
||||
|
||||
// We need to use expect_stdout, because wait_with_output ignores timeouts.
|
||||
// We need to use expect_stdout_line_matches, because wait_with_output ignores timeouts.
|
||||
// We use a non-matching regex, to trigger the timeout.
|
||||
assert!(child.expect_stdout("this regex should not match").is_err());
|
||||
assert!(child
|
||||
.expect_stdout_line_matches("this regex should not match")
|
||||
.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -115,9 +119,11 @@ fn kill_on_timeout_continuous_output_no_newlines() -> Result<()> {
|
|||
.spawn_child_with_command(TEST_CMD, &["/dev/zero"])?
|
||||
.with_timeout(Duration::from_secs(2));
|
||||
|
||||
// We need to use expect_stdout, because wait_with_output ignores timeouts.
|
||||
// We need to use expect_stdout_line_matches, because wait_with_output ignores timeouts.
|
||||
// We use a non-matching regex, to trigger the timeout.
|
||||
assert!(child.expect_stdout("this regex should not match").is_err());
|
||||
assert!(child
|
||||
.expect_stdout_line_matches("this regex should not match")
|
||||
.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -141,9 +147,11 @@ fn finish_before_timeout_short_output_no_newlines() -> Result<()> {
|
|||
.spawn_child_with_command(TEST_CMD, &["zebra_test_output"])?
|
||||
.with_timeout(Duration::from_secs(2));
|
||||
|
||||
// We need to use expect_stdout, because wait_with_output ignores timeouts.
|
||||
// We need to use expect_stdout_line_matches, because wait_with_output ignores timeouts.
|
||||
// We use a non-matching regex, to trigger the timeout.
|
||||
assert!(child.expect_stdout("this regex should not match").is_err());
|
||||
assert!(child
|
||||
.expect_stdout_line_matches("this regex should not match")
|
||||
.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -167,9 +175,11 @@ fn kill_on_timeout_no_output() -> Result<()> {
|
|||
.spawn_child_with_command(TEST_CMD, &["120"])?
|
||||
.with_timeout(Duration::from_secs(2));
|
||||
|
||||
// We need to use expect_stdout, because wait_with_output ignores timeouts.
|
||||
// We need to use expect_stdout_line_matches, because wait_with_output ignores timeouts.
|
||||
// We use a non-matching regex, to trigger the timeout.
|
||||
assert!(child.expect_stdout("this regex should not match").is_err());
|
||||
assert!(child
|
||||
.expect_stdout_line_matches("this regex should not match")
|
||||
.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ vergen = { version = "5.1.8", default-features = false, features = ["cargo", "gi
|
|||
[dev-dependencies]
|
||||
abscissa_core = { version = "0.5", features = ["testing"] }
|
||||
once_cell = "1.7"
|
||||
regex = "1.4.6"
|
||||
semver = "1.0.3"
|
||||
tempdir = "0.3.7"
|
||||
zebra-test = { path = "../zebra-test" }
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
#![deny(clippy::await_holding_lock)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use eyre::WrapErr;
|
||||
use color_eyre::{
|
||||
eyre::{Result, WrapErr},
|
||||
Help,
|
||||
};
|
||||
use tempdir::TempDir;
|
||||
|
||||
use std::{collections::HashSet, convert::TryInto, env, path::Path, path::PathBuf, time::Duration};
|
||||
|
@ -45,11 +47,6 @@ use zebrad::config::ZebradConfig;
|
|||
/// metrics or tracing test failures in Windows CI.
|
||||
const LAUNCH_DELAY: Duration = Duration::from_secs(10);
|
||||
|
||||
/// A regular expression that matches `zebrad` versions.
|
||||
///
|
||||
/// `zebrad` uses [semantic versioning](https://semver.org/).
|
||||
const ZEBRAD_VERSION_REGEX: &str = r"zebrad [0-9].[0-9].[0-9]-[A-Za-z]*.[0-9]+";
|
||||
|
||||
fn default_test_config() -> Result<ZebradConfig> {
|
||||
let auto_port_ipv4_local = zebra_network::Config {
|
||||
listen_addr: "127.0.0.1:0".parse()?,
|
||||
|
@ -218,7 +215,7 @@ fn generate_no_args() -> Result<()> {
|
|||
let output = output.assert_success()?;
|
||||
|
||||
// First line
|
||||
output.stdout_contains(r"# Default configuration for zebrad.")?;
|
||||
output.stdout_line_contains("# Default configuration for zebrad")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -301,6 +298,17 @@ fn generate_args() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Is `s` a valid `zebrad` version string?
|
||||
///
|
||||
/// Trims whitespace before parsing the version.
|
||||
///
|
||||
/// Returns false if the version is invalid, or if there is anything else on the
|
||||
/// line that contains the version. In particular, this check will fail if `s`
|
||||
/// includes any terminal formatting.
|
||||
fn is_zebrad_version(s: &str) -> bool {
|
||||
semver::Version::parse(s.replace("zebrad", "").trim()).is_ok()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_no_args() -> Result<()> {
|
||||
zebra_test::init();
|
||||
|
@ -311,11 +319,16 @@ fn help_no_args() -> Result<()> {
|
|||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_success()?;
|
||||
|
||||
// First line haves the version
|
||||
output.stdout_contains(ZEBRAD_VERSION_REGEX)?;
|
||||
// The first line should have the version
|
||||
output.any_output_line(
|
||||
is_zebrad_version,
|
||||
&output.output.stdout,
|
||||
"stdout",
|
||||
"a valid zebrad semantic version",
|
||||
)?;
|
||||
|
||||
// Make sure we are in help by looking usage string
|
||||
output.stdout_contains(r"USAGE:")?;
|
||||
output.stdout_line_contains("USAGE:")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -356,7 +369,7 @@ fn start_no_args() -> Result<()> {
|
|||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_failure()?;
|
||||
|
||||
output.stdout_contains(r"Starting zebrad$")?;
|
||||
output.stdout_line_contains("Starting zebrad")?;
|
||||
|
||||
// Make sure the command was killed
|
||||
output.assert_was_killed()?;
|
||||
|
@ -563,7 +576,7 @@ fn app_no_args() -> Result<()> {
|
|||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_success()?;
|
||||
|
||||
output.stdout_contains(r"USAGE:")?;
|
||||
output.stdout_line_contains("USAGE:")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -578,7 +591,13 @@ fn version_no_args() -> Result<()> {
|
|||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_success()?;
|
||||
|
||||
output.stdout_contains(ZEBRAD_VERSION_REGEX)?;
|
||||
// The output should only contain the version
|
||||
output.output_check(
|
||||
is_zebrad_version,
|
||||
&output.output.stdout,
|
||||
"stdout",
|
||||
"a valid zebrad semantic version",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -608,12 +627,12 @@ fn valid_generated_config_test() -> Result<()> {
|
|||
// Unlike the other tests, these tests can not be run in parallel, because
|
||||
// they use the generated config. So parallel execution can cause port and
|
||||
// cache conflicts.
|
||||
valid_generated_config("start", r"Starting zebrad$")?;
|
||||
valid_generated_config("start", "Starting zebrad")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn valid_generated_config(command: &str, expected_output: &str) -> Result<()> {
|
||||
fn valid_generated_config(command: &str, expect_stdout_line_contains: &str) -> Result<()> {
|
||||
zebra_test::init();
|
||||
|
||||
let testdir = testdir()?;
|
||||
|
@ -643,7 +662,7 @@ fn valid_generated_config(command: &str, expected_output: &str) -> Result<()> {
|
|||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_failure()?;
|
||||
|
||||
output.stdout_contains(expected_output)?;
|
||||
output.stdout_line_contains(expect_stdout_line_contains)?;
|
||||
|
||||
// [Note on port conflict](#Note on port conflict)
|
||||
output.assert_was_killed().wrap_err("Possible port or cache conflict. Are there other acceptance test, zebrad, or zcashd processes running?")?;
|
||||
|
@ -800,8 +819,8 @@ fn sync_until(
|
|||
let mut child = tempdir.spawn_child(&["start"])?.with_timeout(timeout);
|
||||
|
||||
let network = format!("network: {},", network);
|
||||
child.expect_stdout(&network)?;
|
||||
child.expect_stdout(stop_regex)?;
|
||||
child.expect_stdout_line_matches(&network)?;
|
||||
child.expect_stdout_line_matches(stop_regex)?;
|
||||
child.kill()?;
|
||||
|
||||
Ok(child.dir)
|
||||
|
@ -833,8 +852,8 @@ fn create_cached_database_height(network: Network, height: Height) -> Result<()>
|
|||
.bypass_test_capture(true);
|
||||
|
||||
let network = format!("network: {},", network);
|
||||
child.expect_stdout(&network)?;
|
||||
child.expect_stdout(STOP_AT_HEIGHT_REGEX)?;
|
||||
child.expect_stdout_line_matches(&network)?;
|
||||
child.expect_stdout_line_matches(STOP_AT_HEIGHT_REGEX)?;
|
||||
child.kill()?;
|
||||
|
||||
Ok(())
|
||||
|
@ -980,20 +999,21 @@ async fn metrics_endpoint() -> Result<()> {
|
|||
assert!(res.status().is_success());
|
||||
let body = hyper::body::to_bytes(res).await;
|
||||
let (body, mut child) = child.kill_on_error(body)?;
|
||||
assert!(
|
||||
std::str::from_utf8(&body)
|
||||
.expect("metrics response is valid UTF-8")
|
||||
.contains("metrics snapshot"),
|
||||
"metrics exporter returns data in the expected format"
|
||||
);
|
||||
|
||||
child.kill()?;
|
||||
|
||||
let output = child.wait_with_output()?;
|
||||
let output = output.assert_failure()?;
|
||||
|
||||
output.any_output_line_contains(
|
||||
"metrics snapshot",
|
||||
&body,
|
||||
"metrics exporter response",
|
||||
"the metrics response header",
|
||||
)?;
|
||||
std::str::from_utf8(&body).expect("unexpected invalid UTF-8 in metrics exporter response");
|
||||
|
||||
// Make sure metrics was started
|
||||
output.stdout_contains(format!(r"Opened metrics endpoint at {}", endpoint).as_str())?;
|
||||
output.stdout_line_contains(format!("Opened metrics endpoint at {}", endpoint).as_str())?;
|
||||
|
||||
// [Note on port conflict](#Note on port conflict)
|
||||
output
|
||||
|
@ -1037,9 +1057,6 @@ async fn tracing_endpoint() -> Result<()> {
|
|||
assert!(res.status().is_success());
|
||||
let body = hyper::body::to_bytes(res).await;
|
||||
let (body, child) = child.kill_on_error(body)?;
|
||||
assert!(std::str::from_utf8(&body).unwrap().contains(
|
||||
"This HTTP endpoint allows dynamic control of the filter applied to\ntracing events."
|
||||
));
|
||||
|
||||
// Set a filter and make sure it was changed
|
||||
let request = Request::post(url_filter.clone())
|
||||
|
@ -1055,9 +1072,6 @@ async fn tracing_endpoint() -> Result<()> {
|
|||
assert!(tracing_res.status().is_success());
|
||||
let tracing_body = hyper::body::to_bytes(tracing_res).await;
|
||||
let (tracing_body, mut child) = child.kill_on_error(tracing_body)?;
|
||||
assert!(std::str::from_utf8(&tracing_body)
|
||||
.unwrap()
|
||||
.contains("zebrad=debug"));
|
||||
|
||||
child.kill()?;
|
||||
|
||||
|
@ -1065,9 +1079,36 @@ async fn tracing_endpoint() -> Result<()> {
|
|||
let output = output.assert_failure()?;
|
||||
|
||||
// Make sure tracing endpoint was started
|
||||
output.stdout_contains(format!(r"Opened tracing endpoint at {}", endpoint).as_str())?;
|
||||
output.stdout_line_contains(format!("Opened tracing endpoint at {}", endpoint).as_str())?;
|
||||
// TODO: Match some trace level messages from output
|
||||
|
||||
// Make sure the endpoint header is correct
|
||||
// The header is split over two lines. But we don't want to require line
|
||||
// breaks at a specific word, so we run two checks for different substrings.
|
||||
output.any_output_line_contains(
|
||||
"HTTP endpoint allows dynamic control of the filter",
|
||||
&body,
|
||||
"tracing filter endpoint response",
|
||||
"the tracing response header",
|
||||
)?;
|
||||
output.any_output_line_contains(
|
||||
"tracing events",
|
||||
&body,
|
||||
"tracing filter endpoint response",
|
||||
"the tracing response header",
|
||||
)?;
|
||||
std::str::from_utf8(&body).expect("unexpected invalid UTF-8 in tracing filter response");
|
||||
|
||||
// Make sure endpoint requests change the filter
|
||||
output.any_output_line_contains(
|
||||
"zebrad=debug",
|
||||
&tracing_body,
|
||||
"tracing filter endpoint response",
|
||||
"the modified tracing filter",
|
||||
)?;
|
||||
std::str::from_utf8(&tracing_body)
|
||||
.expect("unexpected invalid UTF-8 in modified tracing filter response");
|
||||
|
||||
// [Note on port conflict](#Note on port conflict)
|
||||
output
|
||||
.assert_was_killed()
|
||||
|
@ -1091,7 +1132,10 @@ fn zebra_zcash_listener_conflict() -> Result<()> {
|
|||
let mut config = default_test_config()?;
|
||||
config.network.listen_addr = listen_addr.parse().unwrap();
|
||||
let dir1 = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
||||
let regex1 = format!(r"Opened Zcash protocol endpoint at {}", listen_addr);
|
||||
let regex1 = regex::escape(&format!(
|
||||
"Opened Zcash protocol endpoint at {}",
|
||||
listen_addr
|
||||
));
|
||||
|
||||
// From another folder create a configuration with the same listener.
|
||||
// `network.listen_addr` will be the same in the 2 nodes.
|
||||
|
@ -1119,7 +1163,7 @@ fn zebra_metrics_conflict() -> Result<()> {
|
|||
let mut config = default_test_config()?;
|
||||
config.metrics.endpoint_addr = Some(listen_addr.parse().unwrap());
|
||||
let dir1 = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
||||
let regex1 = format!(r"Opened metrics endpoint at {}", listen_addr);
|
||||
let regex1 = regex::escape(&format!(r"Opened metrics endpoint at {}", listen_addr));
|
||||
|
||||
// From another folder create a configuration with the same endpoint.
|
||||
// `metrics.endpoint_addr` will be the same in the 2 nodes.
|
||||
|
@ -1147,7 +1191,7 @@ fn zebra_tracing_conflict() -> Result<()> {
|
|||
let mut config = default_test_config()?;
|
||||
config.tracing.endpoint_addr = Some(listen_addr.parse().unwrap());
|
||||
let dir1 = TempDir::new("zebrad_tests")?.with_config(&mut config)?;
|
||||
let regex1 = format!(r"Opened tracing endpoint at {}", listen_addr);
|
||||
let regex1 = regex::escape(&format!(r"Opened tracing endpoint at {}", listen_addr));
|
||||
|
||||
// From another folder create a configuration with the same endpoint.
|
||||
// `tracing.endpoint_addr` will be the same in the 2 nodes.
|
||||
|
@ -1173,7 +1217,7 @@ fn zebra_state_conflict() -> Result<()> {
|
|||
|
||||
// Windows problems with this match will be worked on at #1654
|
||||
// We are matching the whole opened path only for unix by now.
|
||||
let regex = if cfg!(unix) {
|
||||
let contains = if cfg!(unix) {
|
||||
let mut dir_conflict_full = PathBuf::new();
|
||||
dir_conflict_full.push(dir_conflict.path());
|
||||
dir_conflict_full.push("state");
|
||||
|
@ -1193,7 +1237,7 @@ fn zebra_state_conflict() -> Result<()> {
|
|||
|
||||
check_config_conflict(
|
||||
dir_conflict.path(),
|
||||
regex.as_str(),
|
||||
regex::escape(&contains).as_str(),
|
||||
dir_conflict.path(),
|
||||
LOCK_FILE_ERROR.as_str(),
|
||||
)?;
|
||||
|
@ -1259,9 +1303,8 @@ where
|
|||
|
||||
// Look for the success regex
|
||||
output1
|
||||
.stdout_contains(first_stdout_regex)
|
||||
.stdout_line_matches(first_stdout_regex)
|
||||
.context_from(&output2)?;
|
||||
use color_eyre::Help;
|
||||
output1
|
||||
.assert_was_killed()
|
||||
.warning("Possible port conflict. Are there other acceptance tests running?")
|
||||
|
@ -1269,7 +1312,7 @@ where
|
|||
|
||||
// In the second node we look for the conflict regex
|
||||
output2
|
||||
.stderr_contains(second_stderr_regex)
|
||||
.stderr_line_matches(second_stderr_regex)
|
||||
.context_from(&output1)?;
|
||||
output2
|
||||
.assert_was_not_killed()
|
||||
|
|
Loading…
Reference in New Issue