// git-slides — Navigate through Git commits like presentation slides.
// Copyright (C) 2024 Quentin Richert
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
mod git;
use std::path::Path;
use std::process::Command;
use std::{env, fs};
const GIT_SLIDES: &str = env!("CARGO_BIN_EXE_git-slides");
struct Output {
exit_code: i32,
stdout: String,
stderr: String,
}
fn run(dir: &Path, args: &[&str]) -> Output {
let mut output = Command::new(GIT_SLIDES);
for arg in args {
output.arg(arg);
}
let output = output.current_dir(dir).output().unwrap();
Output {
exit_code: output.status.code().unwrap(),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
}
}
#[test]
fn help() {
// Works outside of git repository.
let output = run(&env::temp_dir(), &["--help"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains("-h, --help"));
assert!(output.stdout.contains("-v, --version"));
assert!(output.stdout.contains("start [[]"));
assert!(output.stdout.contains("stop"));
assert!(output.stdout.contains("next, n []"));
assert!(output.stdout.contains("previous, p []"));
assert!(output.stdout.contains("go "));
assert!(output.stdout.contains("status"));
assert!(output.stdout.contains("list"));
}
#[test]
fn no_args_shows_help() {
let dir = git::init("no_args_shows_help");
let output = run(&dir, &[]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains("-h, --help"));
}
#[test]
fn no_args_but_presentation_is_started_shows_status() {
let dir = git::init("no_args_but_presentation_is_started_shows_status");
git::commit(&dir, "Slide 1");
run(&dir, &["start"]);
let output = run(&dir, &[]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 1");
assert!(output.stdout.contains("* 1/1"));
}
#[test]
fn version() {
// Works outside of git repository.
let output = run(&env::temp_dir(), &["--version"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains(env!("CARGO_PKG_VERSION")));
}
#[test]
fn git_not_in_path() {
let output = Command::new(GIT_SLIDES).env("PATH", "").output().unwrap();
assert_eq!(output.status.code().unwrap(), 1);
assert_eq!(
String::from_utf8_lossy(&output.stderr).to_string(),
"fatal: Did not find git executable.\n"
);
}
#[test]
fn not_a_git_directory() {
let output = run(&env::temp_dir(), &[]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"fatal: Not a git repository (or any of the parent directories): .git\n"
);
}
#[test]
fn start_regular() {
let dir = git::init("start_regular");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
let store_file = dir.join(".git/git-slides");
assert_eq!(git::status(&dir), "Slide 3");
assert!(!store_file.is_file());
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.starts_with("Presentation started at"));
assert_eq!(git::status(&dir), "Slide 1"); // Goes to first slide.
assert!(store_file.is_file()); // Store file created.
}
#[test]
fn start_shows_status() {
let dir = git::init("start_shows_status");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 1");
assert!(output.stdout.contains("* 1/2"));
}
#[test]
fn start_at_ref() {
let dir = git::init("start_at_ref");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
assert_eq!(git::status(&dir), "Slide 3");
let output = run(&dir, &["start", "HEAD~"]);
// Presentation goes only up to slide 2.
assert!(!output.stdout.contains("Slide 3"));
assert!(output.stdout.contains("* 1/2"));
assert!(output.stdout.contains(" 2/2"));
}
#[test]
fn start_bad_ref() {
let dir = git::init("start_bad_ref");
// Will always work, as there are no commits in this repo.
let output = run(&dir, &["start", "abcdefg"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "error: Bad ref input: 'abcdefg'.\n");
}
#[test]
fn start_in_dirty_working_directory() {
let dir = git::init("start_in_dirty_working_directory");
git::commit(&dir, "Initial commit");
let new_file = dir.join("hello.txt");
let _ = fs::write(&new_file, ":)");
git::add(&dir, &new_file);
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Working directory contains uncommitted changes.\n"
);
}
#[test]
fn start_in_half_dirty_working_directory() {
let dir = git::init("start_in_half_dirty_working_directory");
git::commit(&dir, "Initial commit");
let new_file = dir.join("hello.txt");
let _ = fs::write(new_file, ":)");
// This version doesn't 'git add' the file; it remains untracked.
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.starts_with("Presentation started at"));
}
#[test]
fn start_in_repo_without_commits() {
let dir = git::init("start_in_repo_without_commits");
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: No HEAD commit. Please provide a valid ref.\n"
);
}
#[test]
fn all_methods_requiring_presentation_to_be_started() {
let dir = git::init("all_methods_requiring_presentation_to_be_started");
let output = run(&dir, &["stop"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "You need to start by 'git slides start'.\n");
let output = run(&dir, &["next"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "You need to start by 'git slides start'.\n");
let output = run(&dir, &["previous"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "You need to start by 'git slides start'.\n");
let output = run(&dir, &["go", "1"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "You need to start by 'git slides start'.\n");
let output = run(&dir, &["status"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "You need to start by 'git slides start'.\n");
let output = run(&dir, &["list"]);
assert_eq!(output.exit_code, 1);
assert_eq!(output.stderr, "You need to start by 'git slides start'.\n");
}
#[test]
fn next_regular() {
let dir = git::init("next_regular");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
run(&dir, &["start"]);
assert_eq!(git::status(&dir), "Slide 1");
let output = run(&dir, &["next"]);
assert_eq!(git::status(&dir), "Slide 2");
assert_eq!(output.exit_code, 0);
assert!(!output
.stdout
.contains("You've reached the end of the presentation.\n"));
let output = run(&dir, &["next"]);
assert_eq!(git::status(&dir), "Slide 3");
assert_eq!(output.exit_code, 0);
assert!(output
.stdout
.contains("You've reached the end of the presentation.\n"));
}
#[test]
fn next_shorthand() {
let dir = git::init("next_shorthand");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
run(&dir, &["start"]);
assert_eq!(git::status(&dir), "Slide 1");
let output = run(&dir, &["n"]);
assert_eq!(git::status(&dir), "Slide 2");
assert_eq!(output.exit_code, 0);
}
#[test]
fn next_with_offset() {
let dir = git::init("next_with_offset");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
run(&dir, &["start"]);
assert_eq!(git::status(&dir), "Slide 1");
let output = run(&dir, &["next", "2"]);
assert_eq!(git::status(&dir), "Slide 3");
assert_eq!(output.exit_code, 0);
assert!(!output
.stdout
.contains("You've reached the end of the presentation.\n"));
// Does not overflow.
let output = run(&dir, &["next", "10"]);
assert_eq!(git::status(&dir), "Slide 4");
assert_eq!(output.exit_code, 0);
assert!(output
.stdout
.contains("You've reached the end of the presentation.\n"));
}
#[test]
fn next_with_overlow() {
let dir = git::init("next_with_overflow");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
run(&dir, &["start"]);
assert_eq!(git::status(&dir), "Slide 1");
run(&dir, &["next"]);
run(&dir, &["next"]);
run(&dir, &["next"]);
let output = run(&dir, &["next"]);
assert_eq!(git::status(&dir), "Slide 4");
assert_eq!(output.exit_code, 0);
assert!(output
.stdout
.contains("You've reached the end of the presentation.\n"));
}
#[test]
fn next_error_getting_current_commit() {
let dir = git::init("next_error_getting_current_commit");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Not in presentation");
run(&dir, &["start", "HEAD~"]);
git::checkout(&dir, "main"); // Commit not in presentation.
let output = run(&dir, &["next"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Current HEAD not part of presentation.\n"
);
}
#[test]
fn previous_regular() {
let dir = git::init("previous_regular");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
run(&dir, &["start"]);
run(&dir, &["go", "3"]);
assert_eq!(git::status(&dir), "Slide 3");
let output = run(&dir, &["previous"]);
assert_eq!(git::status(&dir), "Slide 2");
assert_eq!(output.exit_code, 0);
assert!(!output
.stdout
.contains("You're at the start of the presentation.\n"));
let output = run(&dir, &["previous"]);
assert_eq!(git::status(&dir), "Slide 1");
assert_eq!(output.exit_code, 0);
assert!(output
.stdout
.contains("You're at the start of the presentation.\n"));
}
#[test]
fn previous_shorthand() {
let dir = git::init("previous_shorthand");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
run(&dir, &["start"]);
run(&dir, &["go", "2"]);
assert_eq!(git::status(&dir), "Slide 2");
let output = run(&dir, &["p"]);
assert_eq!(git::status(&dir), "Slide 1");
assert_eq!(output.exit_code, 0);
}
#[test]
fn previous_with_offset() {
let dir = git::init("previous_with_offset");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
run(&dir, &["start"]);
run(&dir, &["go", "4"]);
assert_eq!(git::status(&dir), "Slide 4");
let output = run(&dir, &["previous", "2"]);
assert_eq!(git::status(&dir), "Slide 2");
assert_eq!(output.exit_code, 0);
assert!(!output
.stdout
.contains("You're at the start of the presentation.\n"));
// Does not overflow.
let output = run(&dir, &["previous", "10"]);
assert_eq!(git::status(&dir), "Slide 1");
assert_eq!(output.exit_code, 0);
assert!(output
.stdout
.contains("You're at the start of the presentation.\n"));
}
#[test]
fn previous_with_overlow() {
let dir = git::init("previous_with_overflow");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
run(&dir, &["start"]);
run(&dir, &["go", "4"]);
assert_eq!(git::status(&dir), "Slide 4");
run(&dir, &["previous"]);
run(&dir, &["previous"]);
run(&dir, &["previous"]);
let output = run(&dir, &["previous"]);
assert_eq!(git::status(&dir), "Slide 1");
assert_eq!(output.exit_code, 0);
assert!(output
.stdout
.contains("You're at the start of the presentation.\n"));
}
#[test]
fn previous_error_getting_current_commit() {
let dir = git::init("previous_error_getting_current_commit");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Not in presentation");
run(&dir, &["start", "HEAD~"]);
git::checkout(&dir, "main"); // Commit not in presentation.
let output = run(&dir, &["previous"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Current HEAD not part of presentation.\n"
);
}
#[test]
fn stop_regular() {
let dir = git::init("stop_regular");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
let store_file = dir.join(".git/git-slides");
assert_eq!(git::status(&dir), "Slide 3");
assert!(!store_file.is_file());
run(&dir, &["start"]);
assert_eq!(git::status(&dir), "Slide 1");
assert!(store_file.is_file());
let output = run(&dir, &["stop"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.starts_with("Presentation stopped.\n"));
assert!(!store_file.is_file()); // Removed store file.
// By default, switch back to initial branch.
assert!(output.stdout.contains("Going back to branch 'main'.\n"));
assert_eq!(git::status(&dir), "Slide 3");
}
#[test]
fn stop_started_from_detached() {
let dir = git::init("stop_started_from_detached");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::checkout(&dir, "HEAD~");
assert_eq!(git::status(&dir), "Slide 2"); // Start from slide 2.
run(&dir, &["start"]);
assert_eq!(git::status(&dir), "Slide 1");
let output = run(&dir, &["stop"]);
// Switch back to initial commit.
assert!(output.stdout.contains("Going back to commit"));
assert_eq!(git::status(&dir), "Slide 2"); // Back to slide 2.
}
#[test]
fn stop_in_dirty_working_directory() {
let dir = git::init("stop_in_dirty_working_directory");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
run(&dir, &["start"]);
let new_file = dir.join("hello.txt");
let _ = fs::write(&new_file, ":)");
git::add(&dir, &new_file);
assert!(!git::has_stashed_changes(&dir));
let output = run(&dir, &["stop"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains("Stashed uncommitted changes."));
assert!(git::has_stashed_changes(&dir));
}
#[test]
fn go_regular() {
let dir = git::init("go_regular");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 1");
let output = run(&dir, &["go", "2"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 2");
let output = run(&dir, &["go", "3"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 3");
// Go backwards.
let output = run(&dir, &["go", "1"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 1");
}
#[test]
fn go_to_current() {
let dir = git::init("go_to_current");
git::commit(&dir, "Slide 1");
let output = run(&dir, &["start"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 1");
// Go to current.
let output = run(&dir, &["go", "1"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 1");
}
#[test]
fn go_shows_status() {
let dir = git::init("go_shows_status");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
run(&dir, &["start"]);
let output = run(&dir, &["go", "2"]);
assert_eq!(output.exit_code, 0);
assert_eq!(git::status(&dir), "Slide 2");
assert!(output.stdout.contains("* 2/2"));
}
#[test]
fn go_in_dirty_working_directory() {
let dir = git::init("go_in_dirty_working_directory");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
run(&dir, &["start"]);
let new_file = dir.join("hello.txt");
let _ = fs::write(&new_file, ":)");
git::add(&dir, &new_file);
assert!(!git::has_stashed_changes(&dir));
let output = run(&dir, &["go", "2"]);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains("Stashed uncommitted changes."));
assert!(output.stdout.contains("* 2/2"));
assert!(git::has_stashed_changes(&dir));
}
#[test]
fn go_no_index() {
let dir = git::init("go_no_index");
let output = run(&dir, &["go"]);
assert_eq!(output.exit_code, 2);
assert_eq!(output.stderr, "fatal: Need a slide number.\n");
}
#[test]
fn go_bad_index() {
let dir = git::init("go_bad_index");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
run(&dir, &["start"]);
let output = run(&dir, &["go", "0"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Bad slide index. Slide 0 does not exist.\nPossible values range from 1 to 2.\n"
);
let output = run(&dir, &["go", "3"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Bad slide index. Slide 3 does not exist.\nPossible values range from 1 to 2.\n"
);
}
#[test]
fn status_full() {
let dir = git::init("status_full");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
run(&dir, &["start"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains("(Start)"));
assert!(output.stdout.contains("* 1/3"));
assert!(output.stdout.contains("2/3"));
assert!(output.stdout.contains("3/3"));
assert!(output.stdout.contains("(End)"));
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn status_cut() {
let dir = git::init("status_cut");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
git::commit(&dir, "Slide 5");
git::commit(&dir, "Slide 6");
git::commit(&dir, "Slide 7");
run(&dir, &["start"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(output.stdout.contains("(Start)"));
assert!(output.stdout.contains("* 1/7"));
assert!(output.stdout.contains("2/7"));
assert!(output.stdout.contains("3/7"));
assert!(output.stdout.contains("4/7"));
assert!(!output.stdout.contains("5/7"));
run(&dir, &["go", "2"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(output.stdout.contains("(Start)"));
assert!(output.stdout.contains("1/7"));
assert!(output.stdout.contains("* 2/7"));
assert!(output.stdout.contains("3/7"));
assert!(output.stdout.contains("4/7"));
assert!(output.stdout.contains("5/7"));
assert!(!output.stdout.contains("6/7"));
run(&dir, &["go", "3"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(!output.stdout.contains("(Start)"));
assert!(output.stdout.contains("1/7"));
assert!(output.stdout.contains("2/7"));
assert!(output.stdout.contains("* 3/7"));
assert!(output.stdout.contains("4/7"));
assert!(output.stdout.contains("5/7"));
assert!(output.stdout.contains("6/7"));
assert!(!output.stdout.contains("7/7"));
run(&dir, &["go", "4"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(!output.stdout.contains("1/7"));
assert!(output.stdout.contains("2/7"));
assert!(output.stdout.contains("3/7"));
assert!(output.stdout.contains("* 4/7"));
assert!(output.stdout.contains("5/7"));
assert!(output.stdout.contains("6/7"));
assert!(output.stdout.contains("7/7"));
assert!(!output.stdout.contains("(End)"));
run(&dir, &["go", "5"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(!output.stdout.contains("2/7"));
assert!(output.stdout.contains("3/7"));
assert!(output.stdout.contains("4/7"));
assert!(output.stdout.contains("* 5/7"));
assert!(output.stdout.contains("6/7"));
assert!(output.stdout.contains("7/7"));
assert!(output.stdout.contains("(End)"));
run(&dir, &["go", "6"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(!output.stdout.contains("3/7"));
assert!(output.stdout.contains("4/7"));
assert!(output.stdout.contains("5/7"));
assert!(output.stdout.contains("* 6/7"));
assert!(output.stdout.contains("7/7"));
assert!(output.stdout.contains("(End)"));
run(&dir, &["go", "7"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(!output.stdout.contains("4/7"));
assert!(output.stdout.contains("5/7"));
assert!(output.stdout.contains("6/7"));
assert!(output.stdout.contains("* 7/7"));
assert!(output.stdout.contains("(End)"));
}
#[test]
fn status_number_padding() {
let dir = git::init("status_number_padding");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
git::commit(&dir, "Slide 5");
git::commit(&dir, "Slide 6");
git::commit(&dir, "Slide 7");
git::commit(&dir, "Slide 8");
git::commit(&dir, "Slide 9");
git::commit(&dir, "Slide 10");
run(&dir, &["start"]);
// Padded, even if bigger number not in output.
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(output.stdout.contains("* 1/10"));
assert!(output.stdout.contains(" 2/10"));
assert!(!output.stdout.contains(" 10/10"));
run(&dir, &["go", "9"]);
let output = run(&dir, &["status"]);
println!("{}", output.stdout);
assert!(!output.stdout.contains("6/10"));
assert!(output.stdout.contains("* 9/10"));
assert!(output.stdout.contains(" 10/10"));
}
#[test]
fn status_error_getting_current_commit() {
let dir = git::init("status_error_getting_current_commit");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Not in presentation");
run(&dir, &["start", "HEAD~"]);
git::checkout(&dir, "main"); // Commit not in presentation.
let output = run(&dir, &["status"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Current HEAD not part of presentation.\n"
);
}
#[test]
fn list() {
let dir = git::init("list");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
run(&dir, &["start"]);
let output = run(&dir, &["list"]);
println!("{}", output.stdout);
assert_eq!(output.exit_code, 0);
assert!(output.stdout.contains("* 1/3"));
assert!(output.stdout.contains("2/3"));
assert!(output.stdout.contains("3/3"));
}
#[test]
fn list_number_padding() {
let dir = git::init("list_number_padding");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Slide 2");
git::commit(&dir, "Slide 3");
git::commit(&dir, "Slide 4");
git::commit(&dir, "Slide 5");
git::commit(&dir, "Slide 6");
git::commit(&dir, "Slide 7");
git::commit(&dir, "Slide 8");
git::commit(&dir, "Slide 9");
git::commit(&dir, "Slide 10");
run(&dir, &["start"]);
let output = run(&dir, &["list"]);
println!("{}", output.stdout);
assert!(output.stdout.contains("* 1/10"));
assert!(output.stdout.contains(" 10/10"));
}
#[test]
fn list_error_getting_current_commit() {
let dir = git::init("list_error_getting_current_commit");
git::commit(&dir, "Slide 1");
git::commit(&dir, "Not in presentation");
run(&dir, &["start", "HEAD~"]);
git::checkout(&dir, "main"); // Commit not in presentation.
let output = run(&dir, &["list"]);
assert_eq!(output.exit_code, 1);
assert_eq!(
output.stderr,
"error: Current HEAD not part of presentation.\n"
);
}
]