zebra/zebrad/tests/common/launch.rs

200 lines
6.7 KiB
Rust

//! `zebrad` launch-specific shared code for the `zebrad` acceptance tests.
//!
//! # Warning
//!
//! Test functions in this file will not be run.
//! This file is only for test library code.
use std::{env, path::Path, time::Duration};
use color_eyre::eyre::Result;
use zebrad::config::ZebradConfig;
use zebra_test::{command::TestDirExt, prelude::*};
/// After we launch `zebrad`, wait this long for the command to start up,
/// take the actions expected by the tests, and log the expected logs.
///
/// Previously, this value was 3 seconds, which caused rare
/// metrics or tracing test failures in Windows CI.
pub const LAUNCH_DELAY: Duration = Duration::from_secs(15);
/// After we launch `lightwalletd`, wait this long for the command to start up,
/// take the actions expected by the tests, and log the expected logs.
///
/// `lightwalletd`'s actions also depend on the actions of the `zebrad` instance
/// it is using for its RPCs.
pub const LIGHTWALLETD_DELAY: Duration = Duration::from_secs(60);
/// The amount of time we wait between launching two
/// conflicting nodes.
pub const BETWEEN_NODES_DELAY: Duration = Duration::from_secs(2);
/// Extension trait for methods on `tempfile::TempDir` for using it as a test
/// directory for `zebrad`.
pub trait ZebradTestDirExt
where
Self: AsRef<Path> + Sized,
{
// Zebra methods
/// Spawn `zebrad` with `args` as a child process in this test directory,
/// potentially taking ownership of the tempdir for the duration of the
/// child process.
///
/// If there is a config in the test directory, pass it to `zebrad`.
fn spawn_child(self, args: &[&str]) -> Result<TestChild<Self>>;
/// Create a config file and use it for all subsequently spawned `zebrad` processes.
/// Returns an error if the config already exists.
///
/// If needed:
/// - recursively create directories for the config and state
/// - set `config.cache_dir` based on `self`
fn with_config(self, config: &mut ZebradConfig) -> Result<Self>;
/// Create a config file with the exact contents of `config`, and use it for
/// all subsequently spawned `zebrad` processes. Returns an error if the config
/// already exists.
///
/// If needed:
/// - recursively create directories for the config and state
fn with_exact_config(self, config: &ZebradConfig) -> Result<Self>;
/// Overwrite any existing `zebrad` config file, and use the newly written config for
/// all subsequently spawned processes.
///
/// If needed:
/// - recursively create directories for the config and state
/// - set `config.cache_dir` based on `self`
fn replace_config(self, config: &mut ZebradConfig) -> Result<Self>;
/// `cache_dir` config update helper for `zebrad`.
///
/// If needed:
/// - set the cache_dir in the config.
fn cache_config_update_helper(self, config: &mut ZebradConfig) -> Result<Self>;
/// Config writing helper for `zebrad`.
///
/// If needed:
/// - recursively create directories for the config and state,
///
/// Then write out the config.
fn write_config_helper(self, config: &ZebradConfig) -> Result<Self>;
}
impl<T> ZebradTestDirExt for T
where
Self: TestDirExt + AsRef<Path> + Sized,
{
fn spawn_child(self, args: &[&str]) -> Result<TestChild<Self>> {
let dir = self.as_ref();
let default_config_path = dir.join("zebrad.toml");
if default_config_path.exists() {
let mut extra_args: Vec<_> = vec![
"-c",
default_config_path
.as_path()
.to_str()
.expect("Path is valid Unicode"),
];
extra_args.extend_from_slice(args);
self.spawn_child_with_command(env!("CARGO_BIN_EXE_zebrad"), &extra_args)
} else {
self.spawn_child_with_command(env!("CARGO_BIN_EXE_zebrad"), args)
}
}
fn with_config(self, config: &mut ZebradConfig) -> Result<Self> {
self.cache_config_update_helper(config)?
.write_config_helper(config)
}
fn with_exact_config(self, config: &ZebradConfig) -> Result<Self> {
self.write_config_helper(config)
}
fn replace_config(self, config: &mut ZebradConfig) -> Result<Self> {
use std::fs;
use std::io::ErrorKind;
// Remove any existing config before writing a new one
let dir = self.as_ref();
let config_file = dir.join("zebrad.toml");
match fs::remove_file(config_file) {
Ok(()) => {}
// If the config file doesn't exist, that's ok
Err(e) if e.kind() == ErrorKind::NotFound => {}
Err(e) => Err(e)?,
}
self.cache_config_update_helper(config)?
.write_config_helper(config)
}
fn cache_config_update_helper(self, config: &mut ZebradConfig) -> Result<Self> {
if !config.state.ephemeral {
let dir = self.as_ref();
let cache_dir = dir.join("state");
config.state.cache_dir = cache_dir;
}
Ok(self)
}
fn write_config_helper(self, config: &ZebradConfig) -> Result<Self> {
use std::fs;
use std::io::Write;
let dir = self.as_ref();
if !config.state.ephemeral {
let cache_dir = dir.join("state");
fs::create_dir_all(&cache_dir)?;
} else {
fs::create_dir_all(&dir)?;
}
let config_file = dir.join("zebrad.toml");
fs::File::create(config_file)?.write_all(toml::to_string(&config)?.as_bytes())?;
Ok(self)
}
}
/// Panics if `$pred` is false, with an error report containing:
/// * context from `$source`, and
/// * an optional wrapper error, using `$fmt_arg`+ as a format string and
/// arguments.
#[macro_export]
macro_rules! assert_with_context {
($pred:expr, $source:expr) => {
if !$pred {
use color_eyre::Section as _;
use color_eyre::SectionExt as _;
use zebra_test::command::ContextFrom as _;
let report = color_eyre::eyre::eyre!("failed assertion")
.section(stringify!($pred).header("Predicate:"))
.context_from($source);
panic!("Error: {:?}", report);
}
};
($pred:expr, $source:expr, $($fmt_arg:tt)+) => {
if !$pred {
use color_eyre::Section as _;
use color_eyre::SectionExt as _;
use zebra_test::command::ContextFrom as _;
let report = color_eyre::eyre::eyre!("failed assertion")
.section(stringify!($pred).header("Predicate:"))
.context_from($source)
.wrap_err(format!($($fmt_arg)+));
panic!("Error: {:?}", report);
}
};
}