use super::BranchCreationCommand::{Checkout, Switch};
use super::{BranchCreationCommand, BranchCreationRegion, parse_branch_creation_region};
use crate::command_test_support::{
fixture, git, git_status, os_arguments, output_text, remove, repository,
};
use std::path::{Path, PathBuf};
use std::process::Output;
fn guess_fixture(name: &str) -> (PathBuf, PathBuf) {
let directory: PathBuf = fixture(name);
let root: PathBuf = repository(directory.as_path(), "repository");
git(
root.as_path(),
&["remote", "add", "origin", "/nonexistent/origin"],
);
for remote_branch in ["refs/remotes/origin/topic", "refs/remotes/origin/main"] {
git(root.as_path(), &["update-ref", remote_branch, "HEAD"]);
}
std::fs::write(root.join("list"), b"").expect("write empty pathspec file");
return (directory, root);
}
fn branches_after(root: &Path, word: &str, line: &str) -> Vec<String> {
let mut full: Vec<&str> = vec![word];
full.extend(line.split_whitespace());
git_status(root, full.as_slice());
let listing: String = output_text(&git(
root,
&["for-each-ref", "--format=%(refname)", "refs/heads"],
));
let mut created: Vec<String> = Vec::<String>::new();
for reference in listing.lines() {
if reference != "refs/heads/main" {
created.push(reference.trim_start_matches("refs/heads/").to_owned());
}
}
let existing: Vec<String> = created.clone();
let head: Output = git_status(root, &["symbolic-ref", "--short", "HEAD"]);
let current: String = output_text(&head);
if head.status.success() && current != "main" && !created.contains(¤t) {
created.push(current);
}
git(root, &["checkout", "--quiet", "--force", "main"]);
for name in &existing {
git(root, &["branch", "--quiet", "-D", name.as_str()]);
}
return created;
}
fn predicted(command: BranchCreationCommand, line: &str) -> bool {
let values: Vec<&str> = line.split_whitespace().collect();
let parsed: BranchCreationRegion =
parse_branch_creation_region(command, os_arguments(values.as_slice()).as_slice(), &[])
.expect("valid region");
if parsed.creates_branch {
return true;
}
if let Some(index) = parsed.implicit_creation_target {
return values[index] == "topic";
}
return false;
}
fn assert_cases(root: &Path, word: &str, command: BranchCreationCommand, cases: &[(&str, &str)]) {
for (line, expected) in cases {
let created: Vec<String> = branches_after(root, word, line);
let wanted: Vec<&str> = expected.split_whitespace().collect();
assert_eq!(created, wanted, "git {word} {line}");
assert_eq!(
predicted(command, line),
!wanted.is_empty(),
"git {word} {line}"
);
}
}
#[test]
fn checkout_creation_matches_git() {
let (directory, root): (PathBuf, PathBuf) = guess_fixture("checkout-guess");
assert_cases(
root.as_path(),
"checkout",
Checkout,
&[
("topic", "topic"),
("topic --", "topic"),
("-q topic", "topic"),
("-l topic", "topic"),
("--end-of-options topic", "topic"),
("--conflict merge topic", "topic"),
("--recurse-submodules topic", "topic"),
("--no-guess --guess topic", "topic"),
("--detach --no-detach topic", "topic"),
("--orphan new --no-orphan topic", "topic"),
("--pathspec-from-file=list topic", "topic"),
("--no-track origin/topic", "topic"),
("--track origin/topic", "topic"),
("-t origin/topic", "topic"),
("--track=inherit origin/topic", "topic"),
("-b new", "new"),
("-bnew", "new"),
("-B new", "new"),
("-qb new origin/topic", "new"),
("--orphan new", "new"),
("", ""),
("main", ""),
("absent", ""),
("-", ""),
("-- topic", ""),
("topic extra", ""),
("topic -- extra", ""),
("top*", ""),
("--no-guess topic", ""),
("--guess --no-guess topic", ""),
("--detach topic", ""),
("-d topic", ""),
("--detach", ""),
("-p topic", ""),
("--ours topic", ""),
("-3 topic", ""),
("--overlay topic", ""),
("--no-overlay topic", ""),
],
);
remove(directory.as_path());
}
#[test]
fn switch_creation_matches_git() {
let (directory, root): (PathBuf, PathBuf) = guess_fixture("switch-guess");
assert_cases(
root.as_path(),
"switch",
Switch,
&[
("topic", "topic"),
("-- topic", "topic"),
("--end-of-options topic", "topic"),
("-q topic", "topic"),
("--discard-changes topic", "topic"),
("--create new --no-create topic", "topic"),
("--no-track origin/topic", "topic"),
("-t origin/topic", "topic"),
("-c new", "new"),
("-cnew", "new"),
("--create=new", "new"),
("--cre new", "new"),
("-C new", "new"),
("--force-c new", "new"),
("--orphan new", "new"),
("", ""),
("main", ""),
("absent", ""),
("-", ""),
("topic extra", ""),
("--no-guess topic", ""),
("--detach topic", ""),
("-d topic", ""),
],
);
remove(directory.as_path());
}
#[test]
fn refusals_git_makes_later_are_still_reported_as_creation() {
let (directory, root): (PathBuf, PathBuf) = guess_fixture("guess-over-report");
for (word, command, line) in [
("checkout", Checkout, "--track topic"),
("checkout", Checkout, "--track=bogus origin/topic"),
("checkout", Checkout, "-b new -B other"),
("checkout", Checkout, "-f -m topic"),
("checkout", Checkout, "--pathspec-file-nul topic"),
("switch", Switch, "--orphan new --track origin/topic"),
] {
let created: Vec<String> = branches_after(root.as_path(), word, line);
assert!(created.is_empty(), "git {word} {line}: {created:?}");
assert!(predicted(command, line), "git {word} {line}");
}
remove(directory.as_path());
}