diff --git a/src/cli.rs b/src/cli.rs index 909bcf00f..07a7c7d44 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1289,9 +1289,18 @@ impl Opt { } } - pub fn from_args_and_git_config(env: &DeltaEnv, assets: HighlightingAssets) -> Call { - let args = std::env::args_os().collect::>(); - + pub fn from_args_and_git_config( + args: Vec, + env: &DeltaEnv, + assets: HighlightingAssets, + ) -> Call { + #[cfg(test)] + // Set argv[0] when called in tests: + let args = { + let mut args = args; + args.insert(0, OsString::from("delta")); + args + }; let matches = match Self::handle_help_and_version(&args) { Call::Delta(t) => t, msg => { diff --git a/src/git_config/mod.rs b/src/git_config/mod.rs index f508b9335..22fc60452 100644 --- a/src/git_config/mod.rs +++ b/src/git_config/mod.rs @@ -66,7 +66,8 @@ impl GitConfig { #[cfg(test)] pub fn try_create(_env: &DeltaEnv) -> Option { - unreachable!("GitConfig::try_create() is not available when testing"); + // Do not read local git configs when testing + None } pub fn from_path(env: &DeltaEnv, path: &Path, honor_env_var: bool) -> Self { diff --git a/src/main.rs b/src/main.rs index 0b16c6e4d..d8372a9dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,8 @@ mod subcommands; mod tests; -use std::io::{self, ErrorKind, IsTerminal, Write}; +use std::ffi::OsString; +use std::io::{self, Cursor, ErrorKind, IsTerminal, Write}; use std::process; use bytelines::ByteLinesReader; @@ -32,7 +33,7 @@ use bytelines::ByteLinesReader; use crate::cli::Call; use crate::delta::delta; use crate::utils::bat::assets::list_languages; -use crate::utils::bat::output::OutputType; +use crate::utils::bat::output::{OutputType, PagingMode}; pub fn fatal(errmsg: T) -> ! where @@ -65,7 +66,7 @@ fn main() -> std::io::Result<()> { // See https://github.com/dandavison/delta/issues/681 ctrlc::set_handler(|| {}) .unwrap_or_else(|err| eprintln!("Failed to set ctrl-c handler: {err}")); - let exit_code = run_app()?; + let exit_code = run_app(std::env::args_os().collect::>(), None)?; // when you call process::exit, no destructors are called, so we want to do it only once, here process::exit(exit_code); } @@ -74,10 +75,13 @@ fn main() -> std::io::Result<()> { // An Ok result contains the desired process exit code. Note that 1 is used to // report that two files differ when delta is called with two positional // arguments and without standard input; 2 is used to report a real problem. -fn run_app() -> std::io::Result { - let assets = utils::bat::assets::load_highlighting_assets(); +pub fn run_app( + args: Vec, + capture_output: Option<&mut Cursor>>, +) -> std::io::Result { let env = env::DeltaEnv::init(); - let opt = cli::Opt::from_args_and_git_config(&env, assets); + let assets = utils::bat::assets::load_highlighting_assets(); + let opt = cli::Opt::from_args_and_git_config(args, &env, assets); let opt = match opt { Call::Version(msg) => { @@ -134,10 +138,20 @@ fn run_app() -> std::io::Result { return Ok(0); } + // The following block structure is because of `writer` and related lifetimes: let pager_cfg = (&config).into(); + let paging_mode = if capture_output.is_some() { + PagingMode::Capture + } else { + config.paging_mode + }; let mut output_type = - OutputType::from_mode(&env, config.paging_mode, config.pager.clone(), &pager_cfg).unwrap(); - let mut writer = output_type.handle().unwrap(); + OutputType::from_mode(&env, paging_mode, config.pager.clone(), &pager_cfg).unwrap(); + let mut writer: &mut dyn Write = if paging_mode == PagingMode::Capture { + &mut capture_output.unwrap() + } else { + output_type.handle().unwrap() + }; if let (Some(minus_file), Some(plus_file)) = (&config.minus_file, &config.plus_file) { let exit_code = subcommands::diff::diff(minus_file, plus_file, &config, &mut writer); diff --git a/src/subcommands/diff.rs b/src/subcommands/diff.rs index 215d45e38..53a36c754 100644 --- a/src/subcommands/diff.rs +++ b/src/subcommands/diff.rs @@ -172,11 +172,10 @@ where #[cfg(test)] mod main_tests { + use std::ffi::OsString; use std::io::Cursor; - use std::path::PathBuf; - use super::{diff, diff_args_set_unified_context}; - use crate::tests::integration_test_utils; + use super::diff_args_set_unified_context; use rstest::rstest; @@ -215,20 +214,24 @@ mod main_tests { &str, >, ) { - let config = integration_test_utils::make_config_from_args(&args); let mut writer = Cursor::new(vec![]); - let exit_code = diff( - &PathBuf::from(file_a), - &PathBuf::from(file_b), - &config, - &mut writer, - ); + let mut runargs = vec![OsString::from(file_a), OsString::from(file_b)]; + runargs.extend(args.iter().map(OsString::from)); + let exit_code = crate::run_app(runargs, Some(&mut writer)); + assert_eq!( - exit_code, + exit_code.unwrap(), match expect_diff { ExpectDiff::Yes => 1, ExpectDiff::No => 0, } ); + assert_eq!( + std::str::from_utf8(writer.get_ref()).unwrap() != "", + match expect_diff { + ExpectDiff::Yes => true, + ExpectDiff::No => false, + } + ); } } diff --git a/src/subcommands/show_colors.rs b/src/subcommands/show_colors.rs index b67c22997..3a5cabc25 100644 --- a/src/subcommands/show_colors.rs +++ b/src/subcommands/show_colors.rs @@ -13,10 +13,11 @@ use crate::utils::bat::output::{OutputType, PagingMode}; pub fn show_colors() -> std::io::Result<()> { use crate::{delta::DiffType, utils}; - let assets = utils::bat::assets::load_highlighting_assets(); + let args = std::env::args_os().collect::>(); let env = DeltaEnv::default(); + let assets = utils::bat::assets::load_highlighting_assets(); - let opt = match cli::Opt::from_args_and_git_config(&env, assets) { + let opt = match cli::Opt::from_args_and_git_config(args, &env, assets) { cli::Call::Delta(opt) => opt, _ => panic!("non-Delta Call variant should not occur here"), }; diff --git a/src/subcommands/show_config.rs b/src/subcommands/show_config.rs index 3dba4110a..4725aa7bf 100644 --- a/src/subcommands/show_config.rs +++ b/src/subcommands/show_config.rs @@ -147,6 +147,7 @@ pub fn show_config(config: &config::Config, writer: &mut dyn Write) -> std::io:: PagingMode::Always => "always", PagingMode::Never => "never", PagingMode::QuitIfOneScreen => "auto", + PagingMode::Capture => unreachable!("capture can not be set"), }, side_by_side = config.side_by_side, syntax_theme = config diff --git a/src/utils/bat/output.rs b/src/utils/bat/output.rs index c3f4a04ad..a9cb1e46e 100644 --- a/src/utils/bat/output.rs +++ b/src/utils/bat/output.rs @@ -42,6 +42,7 @@ pub enum PagingMode { QuitIfOneScreen, #[default] Never, + Capture, } const LESSUTFCHARDEF: &str = "LESSUTFCHARDEF"; use crate::errors::*; @@ -49,6 +50,7 @@ use crate::errors::*; pub enum OutputType { Pager(Child), Stdout(io::Stdout), + Capture, } impl Drop for OutputType { @@ -84,6 +86,7 @@ impl OutputType { Ok(match mode { Always => OutputType::try_pager(env, false, pager, config)?, QuitIfOneScreen => OutputType::try_pager(env, true, pager, config)?, + Capture => OutputType::Capture, _ => OutputType::stdout(), }) } @@ -162,6 +165,7 @@ impl OutputType { .as_mut() .context("Could not open stdin for pager")?, OutputType::Stdout(ref mut handle) => handle, + OutputType::Capture => unreachable!("capture can not be set"), }) } }