use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use chrono::Days;
use git_koi::behavior::{Behavior, select_behavior};
use git_koi::coat::KoiBody;
use git_koi::history::{AccountSnapshot, ContributionDay};
use git_koi::koi::{KoiDevelopment, derive_koi_development};
use git_koi::motion::frame_count;
use git_koi::output::{encode_gif, encode_png};
use git_koi::render::{KoiFrame, render_koi, render_swim_frame};
const HISTORY_FIXTURE: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/account-with-history.json"
);
#[test]
fn render_writes_the_days_loop_still_and_preview_as_the_renderer_draws_them() {
let mut account_snapshot = read_fixture_snapshot();
let contribution_history = account_snapshot.contribution_history.as_mut().unwrap();
for day_offset in 1..=14 {
contribution_history.days.push(ContributionDay {
date: contribution_history.observed_on + Days::new(day_offset),
contribution_count: 0,
});
}
contribution_history.observed_on = contribution_history.days.last().unwrap().date;
let scratch_directory = fresh_directory("render-default");
let snapshot_path = scratch_directory.join("snapshot.json");
fs::write(
&snapshot_path,
serde_json::to_string(&account_snapshot).unwrap(),
)
.unwrap();
let output_directory = scratch_directory.join("koi");
let command_output = run_git_koi(&[
"render",
snapshot_path.to_str().unwrap(),
"--output-dir",
output_directory.to_str().unwrap(),
]);
assert_eq!(
command_output.status.code(),
Some(0),
"{}",
String::from_utf8_lossy(&command_output.stderr)
);
let koi_development = derive_koi_development(&account_snapshot).unwrap();
let behavior = select_behavior(&koi_development);
assert_eq!(behavior, Behavior::Rest);
let swim_frames: Vec<KoiFrame> = (0..frame_count(behavior))
.map(|frame_index| {
render_swim_frame(&koi_development, KoiBody::White, behavior, frame_index)
})
.collect();
assert_eq!(
fs::read(output_directory.join("koi.gif")).unwrap(),
encode_gif(&swim_frames).unwrap()
);
assert_eq!(
fs::read(output_directory.join("koi.png")).unwrap(),
encode_png(&render_koi(&koi_development, KoiBody::White)).unwrap()
);
let preview_html = fs::read_to_string(output_directory.join("preview.html")).unwrap();
assert!(preview_html.contains(r#"src="koi.gif""#));
assert!(preview_html.contains(r#"src="koi.png""#));
assert!(!preview_html.contains("<script"));
let summary_line = String::from_utf8_lossy(&command_output.stdout);
let observation_date = koi_development.observed_on.to_string();
assert!(summary_line.contains(&observation_date));
assert!(preview_html.contains(&observation_date));
}
#[test]
fn the_body_option_redraws_the_koi_in_that_body() {
let output_directory = fresh_directory("render-black");
let command_output = run_git_koi(&[
"render",
HISTORY_FIXTURE,
"--output-dir",
output_directory.to_str().unwrap(),
"--body",
"black",
]);
assert_eq!(command_output.status.code(), Some(0));
let koi_development = read_fixture_development();
let black_png = encode_png(&render_koi(&koi_development, KoiBody::Black)).unwrap();
assert_ne!(
black_png,
encode_png(&render_koi(&koi_development, KoiBody::White)).unwrap()
);
assert_eq!(
fs::read(output_directory.join("koi.png")).unwrap(),
black_png
);
assert!(String::from_utf8_lossy(&command_output.stdout).contains("black body"));
}
#[test]
fn a_failed_render_exits_one_and_keeps_the_previous_files() {
let output_directory = fresh_directory("render-failure");
for file_name in ["koi.gif", "koi.png", "preview.html"] {
fs::write(
output_directory.join(file_name),
format!("previous {file_name}"),
)
.unwrap();
}
let command_output = run_git_koi(&[
"render",
concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/account.json"),
"--output-dir",
output_directory.to_str().unwrap(),
]);
assert_eq!(command_output.status.code(), Some(1));
assert!(String::from_utf8_lossy(&command_output.stderr).starts_with("git-koi: "));
for file_name in ["koi.gif", "koi.png", "preview.html"] {
assert_eq!(
fs::read_to_string(output_directory.join(file_name)).unwrap(),
format!("previous {file_name}")
);
}
assert_eq!(fs::read_dir(&output_directory).unwrap().count(), 3);
}
#[test]
fn render_usage_errors_exit_two_and_help_exits_zero() {
for invalid_arguments in [
&["render", HISTORY_FIXTURE][..],
&["render", "--output-dir", "koi"],
&[
"render",
HISTORY_FIXTURE,
"--output-dir",
"koi",
"--body",
"red",
],
] {
let command_output = run_git_koi(invalid_arguments);
assert_eq!(
command_output.status.code(),
Some(2),
"{invalid_arguments:?}"
);
assert!(String::from_utf8_lossy(&command_output.stderr).starts_with("Usage:"));
}
let help_output = run_git_koi(&["render", "--help"]);
assert_eq!(help_output.status.code(), Some(0));
assert!(String::from_utf8_lossy(&help_output.stdout).contains("platinum"));
}
#[test]
fn version_prints_the_manifest_version_and_exits_zero() {
let version_output = run_git_koi(&["--version"]);
assert_eq!(version_output.status.code(), Some(0));
assert_eq!(
String::from_utf8_lossy(&version_output.stdout),
format!("git-koi {}\n", env!("CARGO_PKG_VERSION"))
);
}
fn read_fixture_snapshot() -> AccountSnapshot {
serde_json::from_str(&fs::read_to_string(HISTORY_FIXTURE).unwrap()).unwrap()
}
fn read_fixture_development() -> KoiDevelopment {
derive_koi_development(&read_fixture_snapshot()).unwrap()
}
fn fresh_directory(directory_name: &str) -> PathBuf {
let directory_path = Path::new(env!("CARGO_TARGET_TMPDIR")).join(directory_name);
let _ = fs::remove_dir_all(&directory_path);
fs::create_dir_all(&directory_path).unwrap();
directory_path
}
fn run_git_koi(arguments: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_git-koi"))
.args(arguments)
.output()
.unwrap()
}