moves EntryPoint to submodule and adds a test

This commit is contained in:
arya2 2022-08-26 12:33:40 -04:00
parent 6927d83b24
commit f06f245ece
3 changed files with 141 additions and 101 deletions

View File

@ -1,25 +1,22 @@
//! Zebrad Abscissa Application //! Zebrad Abscissa Application
use std::{fmt::Write as _, io::Write as _, path::PathBuf, process}; mod entry_point;
use self::entry_point::EntryPoint;
use std::{fmt::Write as _, io::Write as _, process};
use abscissa_core::{ use abscissa_core::{
application::{self, fatal_error, AppCell}, application::{self, fatal_error, AppCell},
command::{Command, Usage},
config::{self, Configurable}, config::{self, Configurable},
status_err, status_err,
terminal::{component::Terminal, stderr, stdout, ColorChoice}, terminal::{component::Terminal, stderr, stdout, ColorChoice},
Application, Component, FrameworkError, Options, Runnable, Shutdown, StandardPaths, Application, Component, FrameworkError, Shutdown, StandardPaths, Version,
Version,
}; };
use zebra_network::constants::PORT_IN_USE_ERROR; use zebra_network::constants::PORT_IN_USE_ERROR;
use zebra_state::constants::{DATABASE_FORMAT_VERSION, LOCK_FILE_ERROR}; use zebra_state::constants::{DATABASE_FORMAT_VERSION, LOCK_FILE_ERROR};
use crate::{ use crate::{commands::ZebradCmd, components::tracing::Tracing, config::ZebradConfig};
commands::{StartCmd, ZebradCmd},
components::tracing::Tracing,
config::ZebradConfig,
};
/// Application state /// Application state
pub static APPLICATION: AppCell<ZebradApp> = AppCell::new(); pub static APPLICATION: AppCell<ZebradApp> = AppCell::new();
@ -43,98 +40,6 @@ pub fn app_config() -> config::Reader<ZebradApp> {
config::Reader::new(&APPLICATION) config::Reader::new(&APPLICATION)
} }
/// Toplevel entrypoint command.
///
/// Handles obtaining toplevel help as well as verbosity settings.
#[derive(Debug, Options)]
pub struct EntryPoint {
#[options(help = "tracing filters which override the zebrad.toml config")]
filters: Vec<String>,
/// Path to the configuration file
#[options(short = "c", help = "path to configuration file")]
pub config: Option<PathBuf>,
/// Obtain help about the current command
#[options(short = "h", help = "print help message")]
pub help: bool,
/// Increase verbosity setting
#[options(short = "v", help = "be verbose")]
pub verbose: bool,
/// Subcommand to execute.
///
/// The `command` option will delegate option parsing to the command type,
/// starting at the first free argument. Defaults to start.
#[options(command, default_expr = "Some(ZebradCmd::Start(StartCmd::default()))")]
pub command: Option<ZebradCmd>,
}
impl EntryPoint {
/// Borrow the underlying command type or print usage info and exit
fn command(&self) -> &ZebradCmd {
self.command
.as_ref()
.expect("Some(ZebradCmd::Start(StartCmd::default()) as default value")
}
}
impl Runnable for EntryPoint {
fn run(&self) {
self.command().run()
}
}
impl Command for EntryPoint {
/// Name of this program as a string
fn name() -> &'static str {
ZebradCmd::name()
}
/// Description of this program
fn description() -> &'static str {
ZebradCmd::description()
}
/// Version of this program
fn version() -> &'static str {
ZebradCmd::version()
}
/// Authors of this program
fn authors() -> &'static str {
ZebradCmd::authors()
}
/// Get usage information for a particular subcommand (if available)
fn subcommand_usage(command: &str) -> Option<Usage> {
ZebradCmd::subcommand_usage(command)
}
}
impl Configurable<ZebradConfig> for EntryPoint {
/// Path to the command's configuration file
fn config_path(&self) -> Option<PathBuf> {
match &self.config {
// Use explicit `-c`/`--config` argument if passed
Some(cfg) => Some(cfg.clone()),
// Otherwise defer to the toplevel command's config path logic
None => self.command.as_ref().and_then(|cmd| cmd.config_path()),
}
}
/// Process the configuration after it has been loaded, potentially
/// modifying it or returning an error if options are incompatible
fn process_config(&self, config: ZebradConfig) -> Result<ZebradConfig, FrameworkError> {
match &self.command {
Some(cmd) => cmd.process_config(config),
None => Ok(config),
}
}
}
/// Returns the zebrad version for this build, in SemVer 2.0 format. /// Returns the zebrad version for this build, in SemVer 2.0 format.
/// ///
/// Includes the git commit and the number of commits since the last version /// Includes the git commit and the number of commits since the last version

View File

@ -0,0 +1,104 @@
//! Zebrad EntryPoint
use crate::{
commands::{StartCmd, ZebradCmd},
config::ZebradConfig,
};
use std::path::PathBuf;
use abscissa_core::{
command::{Command, Usage},
config::Configurable,
FrameworkError, Options, Runnable,
};
// (See https://docs.rs/abscissa_core/0.5.2/src/abscissa_core/command/entrypoint.rs.html)
/// Toplevel entrypoint command.
///
/// Handles obtaining toplevel help as well as verbosity settings.
#[derive(Debug, Options)]
pub struct EntryPoint {
/// Path to the configuration file
#[options(short = "c", help = "path to configuration file")]
pub config: Option<PathBuf>,
/// Obtain help about the current command
#[options(short = "h", help = "print help message")]
pub help: bool,
/// Increase verbosity setting
#[options(short = "v", help = "be verbose")]
pub verbose: bool,
/// Subcommand to execute.
///
/// The `command` option will delegate option parsing to the command type,
/// starting at the first free argument. Defaults to start.
#[options(command, default_expr = "Some(ZebradCmd::Start(StartCmd::default()))")]
pub command: Option<ZebradCmd>,
}
impl EntryPoint {
/// Borrow the underlying command type or print usage info and exit
fn command(&self) -> &ZebradCmd {
self.command
.as_ref()
.expect("Some(ZebradCmd::Start(StartCmd::default()) as default value")
}
}
impl Runnable for EntryPoint {
fn run(&self) {
self.command().run()
}
}
impl Command for EntryPoint {
/// Name of this program as a string
fn name() -> &'static str {
ZebradCmd::name()
}
/// Description of this program
fn description() -> &'static str {
ZebradCmd::description()
}
/// Version of this program
fn version() -> &'static str {
ZebradCmd::version()
}
/// Authors of this program
fn authors() -> &'static str {
ZebradCmd::authors()
}
/// Get usage information for a particular subcommand (if available)
fn subcommand_usage(command: &str) -> Option<Usage> {
ZebradCmd::subcommand_usage(command)
}
}
impl Configurable<ZebradConfig> for EntryPoint {
/// Path to the command's configuration file
fn config_path(&self) -> Option<PathBuf> {
match &self.config {
// Use explicit `-c`/`--config` argument if passed
Some(cfg) => Some(cfg.clone()),
// Otherwise defer to the toplevel command's config path logic
None => self.command.as_ref().and_then(|cmd| cmd.config_path()),
}
}
/// Process the configuration after it has been loaded, potentially
/// modifying it or returning an error if options are incompatible
fn process_config(&self, config: ZebradConfig) -> Result<ZebradConfig, FrameworkError> {
match &self.command {
Some(cmd) => cmd.process_config(config),
None => Ok(config),
}
}
}

View File

@ -501,6 +501,37 @@ fn config_test() -> Result<()> {
// Check that an older stored configuration we have for Zebra works // Check that an older stored configuration we have for Zebra works
stored_config_works()?; stored_config_works()?;
// Runs `zebrad` serially to avoid potential port conflicts
app_no_args()?;
Ok(())
}
/// Test that `zebrad` runs the start command with no args
fn app_no_args() -> Result<()> {
let _init_guard = zebra_test::init();
// start caches state, so run one of the start tests with persistent state
let testdir = testdir()?.with_config(&mut persistent_test_config()?)?;
let mut child = testdir.spawn_child(args![])?;
// Run the program and kill it after a few seconds
std::thread::sleep(LAUNCH_DELAY);
child.kill()?;
let output = child.wait_with_output()?;
let output = output.assert_failure()?;
output.stdout_line_contains("Starting zebrad")?;
// Make sure the command passed the legacy chain check
output.stdout_line_contains("starting legacy chain check")?;
output.stdout_line_contains("no legacy chain found")?;
// Make sure the command was killed
output.assert_was_killed()?;
Ok(()) Ok(())
} }