#![allow(clippy::expect_used, clippy::unwrap_used)]
use std::{
collections::BTreeSet,
fs,
path::{Path, PathBuf},
process::{Command, Output},
};
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct ExpectedResult {
#[serde(rename = "exit-code")]
exit_code: i32,
#[serde(default)]
stdout: String,
#[serde(default)]
stderr: String,
}
macro_rules! fixture_tests {
($($name:ident => $directory:literal),+ $(,)?) => {
const DECLARED: &[&str] = &[$($directory),+];
$(
#[test]
fn $name() {
assert_fixture($directory);
}
)+
};
}
fixture_tests! {
accountable_suppression => "accountable-suppression",
clean_repository => "clean",
decision_complexity => "decision-complexity",
documented_empty_body => "documented-empty-body",
else_if_chain => "else-if-chain",
empty_function => "empty-function",
enclosing_scope => "enclosing-scope",
excluded_path => "excluded-path",
file_size => "file-size",
function_nesting => "function-nesting",
function_size => "function-size",
function_statements => "function-statements",
invalid_syntax => "invalid-syntax",
marker_word_boundary => "marker-word-boundary",
multiway_branch => "multiway-branch",
no_comments => "no-comments",
no_comments_strict => "no-comments-strict",
parameter_count => "parameter-count",
receiver_parameters => "receiver-parameters",
return_count => "return-count",
restricted_call => "restricted-call",
restricted_call_clean => "restricted-call-clean",
no_dynamic_execution => "no-dynamic-execution",
no_dynamic_execution_clean => "no-dynamic-execution-clean",
direct_environment_read => "direct-environment-read",
direct_environment_read_clean => "direct-environment-read-clean",
explicit_timer_delay => "explicit-timer-delay",
no_production_log => "no-production-log",
dependency_boundary => "dependency-boundary",
filename_case => "filename-case",
filename_case_clean => "filename-case-clean",
forbidden_dependency => "forbidden-dependency",
forbidden_dependency_clean => "forbidden-dependency-clean",
dependency_boundary_clean => "dependency-boundary-clean",
restricted_import => "restricted-import",
restricted_import_clean => "restricted-import-clean",
no_production_log_clean => "no-production-log-clean",
explicit_timer_delay_clean => "explicit-timer-delay-clean",
rust_try_operator => "rust-try-operator",
severity_below_threshold => "severity-below-threshold",
suppression_applies => "suppression-applies",
todo_requires_reference => "todo-requires-reference",
unused_suppression => "unused-suppression",
unused_suppression_clean => "unused-suppression-clean",
}
#[test]
fn every_fixture_directory_is_declared() {
let present: BTreeSet<String> = fs::read_dir(fixtures_root())
.unwrap_or_else(|error| panic!("reads fixtures: {error}"))
.map(|entry| entry.unwrap_or_else(|error| panic!("reads entry: {error}")))
.filter(|entry| entry.path().is_dir())
.map(|entry| entry.file_name().to_string_lossy().into_owned())
.collect();
let declared: BTreeSet<String> = DECLARED.iter().map(|name| (*name).to_owned()).collect();
assert_eq!(present, declared, "fixture directories and tests disagree");
}
fn fixtures_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/rules")
}
fn assert_fixture(directory: &str) {
let fixture = fixtures_root().join(directory);
let expected = expected_result(&fixture);
let output = run(&fixture);
assert_eq!(
String::from_utf8_lossy(&output.stdout),
expected.stdout,
"{directory}: stdout"
);
assert_eq!(
String::from_utf8_lossy(&output.stderr),
expected.stderr,
"{directory}: stderr"
);
assert_eq!(
output.status.code(),
Some(expected.exit_code),
"{directory}: exit code"
);
}
fn expected_result(fixture: &Path) -> ExpectedResult {
let path = fixture.join("expected.yaml");
let source = fs::read_to_string(&path)
.unwrap_or_else(|error| panic!("reads {}: {error}", path.display()));
yaml_serde::from_str(&source)
.unwrap_or_else(|error| panic!("parses {}: {error}", path.display()))
}
fn run(fixture: &Path) -> Output {
Command::new(env!("CARGO_BIN_EXE_godlint"))
.current_dir(fixture)
.args(["check", "."])
.output()
.unwrap_or_else(|error| panic!("runs godlint: {error}"))
}