use std::fmt::Write; use std::{fs, path}; use anyhow::{Context, Result}; use lazy_static::lazy_static; use pretty_assertions::assert_eq; use restack_testtools::gitscript; use rstest::rstest; const RESTACK: &str = env!("CARGO_BIN_EXE_restack"); lazy_static! { static ref FIXTURES_DIR: path::PathBuf = path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures"); static ref DEFAULT_GITSCRIPT_GROUP: gitscript::Group = gitscript::Group::new(FIXTURES_DIR.as_path()); } fn open_fixture

(script_path: P) -> Result> where P: AsRef, { DEFAULT_GITSCRIPT_GROUP .open(script_path) .map_err(anyhow::Error::from) } #[rstest] #[case::editor_flag(true, "simple_stack.sh")] #[case::editor_env(false, "simple_stack.sh")] #[case::comment_char(false, "simple_stack_comment_char.sh")] #[case::comment_char(false, "simple_stack_comment_string.sh")] fn simple_stack(#[case] editor_flag: bool, #[case] fixture: &str) -> Result<()> { let repo_fixture = open_fixture(fixture)?; let editor = FIXTURES_DIR.join("bin/add_break.sh"); let mut seq_editor = format!("{} edit", RESTACK); if editor_flag { write!(&mut seq_editor, " --editor {}", editor.display())?; } duct::cmd!("git", "config", "sequence.editor", seq_editor) .dir(repo_fixture.dir()) .run()?; duct::cmd!("git", "rebase", "--interactive", "main") .env("EDITOR", FIXTURES_DIR.join("bin/add_break.sh")) .dir(repo_fixture.dir()) .run()?; // add_break.sh should have seen a rebase list // with instructions to update branches. // To verify this, introduce a new commit at the top of the stack, // and verify that that file is present in all branches after the rebase finishes. fs::write(repo_fixture.dir().join("README"), "wait for me")?; duct::cmd!( "bash", "-c", "git add README && git commit -m 'add README' && git rebase --continue" ) .dir(repo_fixture.dir()) .run()?; let branches = &["foo", "bar", "baz"]; for br in branches { let got = duct::cmd!("git", "show", format!("{}:README", br)) .dir(repo_fixture.dir()) .read() .with_context(|| format!("Unable to print {}:README", br))?; assert_eq!( "wait for me", &got, "Contents of {}:README do not match", br ); } Ok(()) } #[rstest] #[case::empty("", "No editor specified: please use --editor")] #[case::non_zero_status("false", "Editor returned non-zero status")] #[case::malicious("rm", "Could not overwrite")] fn editor_error(#[case] editor: &str, #[case] msg: &str) -> Result<()> { let repo_fixture = open_fixture("simple_stack.sh")?; let seq_editor = format!("{} edit", RESTACK); duct::cmd!("git", "config", "sequence.editor", seq_editor) .dir(repo_fixture.dir()) .run()?; let out = duct::cmd!("git", "rebase", "--interactive", "main") .env("EDITOR", editor) .dir(repo_fixture.dir()) .stderr_capture() .unchecked() .run()?; assert!(!out.status.success()); let stderr = String::from_utf8(out.stderr)?; assert!( stderr.contains(msg), "unexpected stderr, must contain '{}':\n{}", msg, &stderr ); Ok(()) }