use std::env;
use std::fs;
use std::iter;
use std::path::Path;
use std::process::Command;
use git_checks_core::{BranchCheck, Check, CheckResult, TopicCheck};
use git_workarea::{CommitId, GitContext, Identity};
use log::{Level, LevelFilter, Log, Metadata, Record};
use tempfile::TempDir;
pub use git_checks_core::GitCheckConfiguration;
pub const FILLER_COMMIT: &str = "d02f015907371738253a22b9a7fec78607a969b2";
pub const TARGET_COMMIT: &str = "27ff3ef5532d76afa046f76f4dd8f588dc3e83c3";
pub const SUBMODULE_TARGET_COMMIT: &str = "fe90ee22ae3ce4b4dc41f8d0876e59355ff1e21c";
fn setup_logging() {
struct SimpleLogger;
impl Log for SimpleLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= Level::Debug
}
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
println!("[{}] {}", record.level(), record.args());
}
}
fn flush(&self) {}
}
static LOGGER: SimpleLogger = SimpleLogger;
let _ = log::set_logger(&LOGGER);
log::set_max_level(LevelFilter::Debug);
}
fn make_context() -> GitContext {
setup_logging();
let gitdir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../.git"));
if !gitdir.exists() {
panic!("The tests must be run from a git checkout.");
}
GitContext::new(gitdir)
}
pub fn make_temp_dir() -> TempDir {
let mut working_dir = env::current_exe().unwrap();
working_dir.pop();
TempDir::new_in(working_dir).unwrap()
}
pub fn make_context_submodule(gitdir: &Path, commit: &CommitId) -> GitContext {
let gitdir = gitdir.join("origin");
let clone = Command::new("git")
.arg("clone")
.arg(concat!(env!("CARGO_MANIFEST_DIR"), "/../.git"))
.arg(&gitdir)
.output()
.unwrap();
if !clone.status.success() {
panic!(
"origin clone failed: {}",
String::from_utf8_lossy(&clone.stderr),
);
}
let ctx = GitContext::new(gitdir.join(".git"));
let checkout = ctx
.git()
.arg("checkout")
.arg(commit.as_str())
.current_dir(&gitdir)
.output()
.unwrap();
if !checkout.status.success() {
panic!(
"checkout failed: {}",
String::from_utf8_lossy(&checkout.stderr),
);
}
let submodule_update = ctx
.git()
.arg("submodule")
.arg("update")
.arg("--init")
.current_dir(&gitdir)
.output()
.unwrap();
if !submodule_update.status.success() {
panic!(
"submodule update failed: {}",
String::from_utf8_lossy(&submodule_update.stderr),
);
}
ctx
}
fn test_check_custom(
ctx: &GitContext,
test_name: &str,
topic: &str,
base: &str,
conf: &GitCheckConfiguration,
) -> CheckResult {
test_check_custom_ident(
ctx,
test_name,
topic,
base,
conf,
Identity::new("Rust Git Checks Tests", "rust-git-checks@example.com"),
)
}
fn test_check_custom_ident(
ctx: &GitContext,
test_name: &str,
topic: &str,
base: &str,
conf: &GitCheckConfiguration,
identity: Identity,
) -> CheckResult {
conf.run_topic(
ctx,
test_name,
&CommitId::new(base),
&CommitId::new(topic),
&identity,
)
.unwrap()
.into()
}
pub fn test_check_base(
test_name: &str,
topic: &str,
base: &str,
conf: &GitCheckConfiguration,
) -> CheckResult {
let ctx = make_context();
test_check_custom(&ctx, test_name, topic, base, conf)
}
pub fn test_check(test_name: &str, topic: &str, conf: &GitCheckConfiguration) -> CheckResult {
test_check_base(test_name, topic, TARGET_COMMIT, conf)
}
pub fn test_check_submodule_base(
test_name: &str,
topic: &str,
base: &str,
conf: &GitCheckConfiguration,
) -> CheckResult {
let tempdir = make_temp_dir();
let ctx = make_context_submodule(tempdir.path(), &CommitId::new(base));
test_check_custom(&ctx, test_name, topic, base, conf)
}
pub fn test_check_submodule(
test_name: &str,
topic: &str,
conf: &GitCheckConfiguration,
) -> CheckResult {
test_check_submodule_base(test_name, topic, SUBMODULE_TARGET_COMMIT, conf)
}
pub fn test_check_submodule_base_configure(
test_name: &str,
topic: &str,
base: &str,
conf: &GitCheckConfiguration,
module: &str,
) -> CheckResult {
let tempdir = make_temp_dir();
let ctx = make_context_submodule(tempdir.path(), &CommitId::new(base));
let moduledir = ctx.gitdir().join("modules").join(module);
fs::create_dir_all(moduledir).unwrap();
test_check_custom(&ctx, test_name, topic, base, conf)
}
pub fn test_check_submodule_configure(
test_name: &str,
topic: &str,
conf: &GitCheckConfiguration,
module: &str,
) -> CheckResult {
test_check_submodule_base_configure(test_name, topic, SUBMODULE_TARGET_COMMIT, conf, module)
}
fn no_strings<'a>() -> iter::Empty<&'a String> {
iter::empty()
}
pub fn test_result_warnings(result: CheckResult, warnings: &[&str]) {
itertools::assert_equal(result.warnings(), warnings);
itertools::assert_equal(result.alerts(), no_strings());
itertools::assert_equal(result.errors(), no_strings());
assert!(!result.temporary());
assert!(!result.allowed());
assert!(result.pass());
}
pub fn test_result_errors(result: CheckResult, errors: &[&str]) {
itertools::assert_equal(result.warnings(), no_strings());
itertools::assert_equal(result.alerts(), no_strings());
itertools::assert_equal(result.errors(), errors);
assert!(!result.temporary());
assert!(!result.allowed());
assert!(!result.pass());
}
pub fn test_result_ok(result: CheckResult) {
itertools::assert_equal(result.warnings(), no_strings());
itertools::assert_equal(result.alerts(), no_strings());
itertools::assert_equal(result.errors(), no_strings());
assert!(!result.temporary());
assert!(!result.allowed());
assert!(result.pass());
}
pub fn make_check_conf<T>(check: &T) -> GitCheckConfiguration<'_>
where
T: Check,
{
let mut conf = GitCheckConfiguration::new();
conf.add_check(check);
conf
}
pub fn run_check<T>(name: &str, commit: &str, check: T) -> CheckResult
where
T: Check,
{
let conf = make_check_conf(&check);
test_check(name, commit, &conf)
}
pub fn run_check_ok<T>(name: &str, commit: &str, check: T)
where
T: Check,
{
test_result_ok(run_check(name, commit, check));
}
pub fn make_branch_check_conf<T>(check: &T) -> GitCheckConfiguration<'_>
where
T: BranchCheck,
{
let mut conf = GitCheckConfiguration::new();
conf.add_branch_check(check);
conf
}
pub fn run_branch_check_ident<T>(
name: &str,
commit: &str,
check: T,
identity: Identity,
) -> CheckResult
where
T: BranchCheck,
{
let conf = make_branch_check_conf(&check);
let ctx = make_context();
test_check_custom_ident(&ctx, name, commit, TARGET_COMMIT, &conf, identity)
}
pub fn run_branch_check_ident_ok<T>(name: &str, commit: &str, check: T, identity: Identity)
where
T: BranchCheck,
{
test_result_ok(run_branch_check_ident(name, commit, check, identity));
}
pub fn run_branch_check<T>(name: &str, commit: &str, check: T) -> CheckResult
where
T: BranchCheck,
{
let conf = make_branch_check_conf(&check);
test_check(name, commit, &conf)
}
pub fn run_branch_check_ok<T>(name: &str, commit: &str, check: T)
where
T: BranchCheck,
{
test_result_ok(run_branch_check(name, commit, check));
}
pub fn make_topic_check_conf<T>(check: &T) -> GitCheckConfiguration<'_>
where
T: TopicCheck,
{
let mut conf = GitCheckConfiguration::new();
conf.add_topic_check(check);
conf
}
pub fn run_topic_check<T>(name: &str, commit: &str, check: T) -> CheckResult
where
T: TopicCheck,
{
let conf = make_topic_check_conf(&check);
test_check(name, commit, &conf)
}
pub fn run_topic_check_ok<T>(name: &str, commit: &str, check: T)
where
T: TopicCheck,
{
test_result_ok(run_topic_check(name, commit, check));
}
#[cfg(feature = "config")]
pub fn check_missing_json_field(err: serde_json::Error, field: &str) {
assert!(!err.is_io());
assert!(!err.is_syntax());
assert!(err.is_data());
assert!(!err.is_eof());
assert_eq!(err.to_string(), format!("missing field `{}`", field));
}