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:
teor 2021-06-04 12:28:43 +10:00 committed by Deirdre Connolly
parent fc0edb5c44
commit 56ef08e385
6 changed files with 282 additions and 132 deletions

12
Cargo.lock generated
View File

@ -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",

View File

@ -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(&regex::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");

View File

@ -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

View File

@ -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(())
}

View File

@ -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" }

View File

@ -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()