use std::path::{Path, PathBuf};
use gdck_config::{CodeOrderFix, DeclarationGroup, IndentStyle};
struct Sandbox {
root: PathBuf,
}
impl Sandbox {
fn new(name: &str) -> Self {
let root = std::env::temp_dir().join(format!("gdck-{}-{name}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).expect("should create the sandbox");
let root = std::fs::canonicalize(&root).unwrap_or(root);
Self { root }
}
fn write(&self, relative: &str, text: &str) -> PathBuf {
let path = self.root.join(relative);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("should create the parent");
}
std::fs::write(&path, text).expect("should write the file");
path
}
fn dir(&self, relative: &str) -> PathBuf {
let path = self.root.join(relative);
std::fs::create_dir_all(&path).expect("should create the directory");
path
}
}
impl Drop for Sandbox {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}
fn resolve(start: &Path) -> gdck_config::Loaded {
gdck_config::resolve(start).expect("should resolve")
}
#[test]
fn nothing_found_leaves_the_style_guides_defaults_in_force() {
let sandbox = Sandbox::new("bare");
let loaded = resolve(&sandbox.root);
assert_eq!(loaded.config, gdck_config::Config::default());
assert!(loaded.files.is_empty());
assert!(loaded.notes.is_empty());
}
#[test]
fn a_config_is_found_from_a_directory_below_it() {
let sandbox = Sandbox::new("below");
let path = sandbox.write("gdck.toml", "[format]\nline-length = 120\n");
let deep = sandbox.dir("scenes/player/states");
let loaded = resolve(&deep);
assert_eq!(loaded.config.format.line_length, 120);
assert_eq!(loaded.files, [path]);
}
#[test]
fn the_nearest_config_is_the_one_that_applies() {
let sandbox = Sandbox::new("nearest");
sandbox.write("gdck.toml", "[format]\nline-length = 120\n");
let inner = sandbox.write("addon/gdck.toml", "[format]\nline-length = 80\n");
let loaded = resolve(&sandbox.root.join("addon"));
assert_eq!(loaded.config.format.line_length, 80);
assert_eq!(loaded.files, [inner]);
}
#[test]
fn a_dot_prefixed_name_is_found_too() {
let sandbox = Sandbox::new("dotted");
let path = sandbox.write(".gdck.toml", "[lint]\nmax-returns = 2\n");
assert_eq!(resolve(&sandbox.root).files, [path]);
}
#[test]
fn the_gdtoolkit_files_are_read_when_there_is_no_gdck_toml() {
let sandbox = Sandbox::new("gdtoolkit");
let gdformatrc = sandbox.write("gdformatrc", "line_length: 120\nuse_spaces: 4\n");
let gdlintrc = sandbox.write(
"gdlintrc",
"max-returns: 3\ndisable:\n - max-public-methods\n",
);
let loaded = resolve(&sandbox.root);
assert_eq!(loaded.config.format.line_length, 120);
assert_eq!(loaded.config.format.indent, IndentStyle::Spaces(4));
assert_eq!(loaded.config.lint.max_returns, 3);
assert_eq!(loaded.config.lint.disabled, ["max-public-methods"]);
assert_eq!(loaded.files, [gdformatrc, gdlintrc]);
assert!(loaded.notes.is_empty(), "{:?}", loaded.notes);
}
#[test]
fn a_gdck_toml_wins_outright_over_the_gdtoolkit_files() {
let sandbox = Sandbox::new("wins");
let path = sandbox.write("gdck.toml", "[lint]\nmax-returns = 2\n");
sandbox.write("gdlintrc", "max-returns: 9\nmax-file-lines: 50\n");
let loaded = resolve(&sandbox.root);
assert_eq!(loaded.config.lint.max_returns, 2);
assert_eq!(loaded.config.lint.max_file_lines, 1000, "the default");
assert_eq!(loaded.files, [path]);
}
#[test]
fn a_gdck_toml_further_up_still_wins() {
let sandbox = Sandbox::new("wins-up");
let path = sandbox.write("gdck.toml", "[lint]\nmax-returns = 2\n");
sandbox.write("game/gdlintrc", "max-returns: 9\n");
let loaded = resolve(&sandbox.root.join("game"));
assert_eq!(loaded.config.lint.max_returns, 2);
assert_eq!(loaded.files, [path]);
}
#[test]
fn a_setting_gdck_cannot_honour_comes_back_as_a_note() {
let sandbox = Sandbox::new("notes");
let path = sandbox.write("gdlintrc", "max-locals: 15\n");
let loaded = resolve(&sandbox.root);
assert_eq!(loaded.notes.len(), 1);
assert_eq!(loaded.notes[0].path, path);
assert_eq!(loaded.notes[0].line, 1);
assert!(loaded.notes[0].message.contains("max-locals"));
}
#[test]
fn a_broken_config_stops_the_run_and_says_where() {
let sandbox = Sandbox::new("broken");
let path = sandbox.write("gdck.toml", "[format]\n\nline-lenght = 100\n");
let error = gdck_config::resolve(&sandbox.root).expect_err("should not resolve");
assert_eq!(error.path, path);
assert_eq!(error.line, Some(3));
assert!(error.to_string().ends_with(
"gdck.toml:3: unknown setting `line-lenght`; did you mean `format.line-length`?"
));
}
#[test]
fn an_explicit_file_is_read_by_the_kind_its_name_says() {
let sandbox = Sandbox::new("explicit");
let gdlintrc = sandbox.write("elsewhere/gdlintrc", "max-returns: 4\n");
let toml = sandbox.write("elsewhere/settings.toml", "[lint]\nmax-returns = 5\n");
assert_eq!(
gdck_config::load(&gdlintrc)
.expect("should load")
.config
.lint
.max_returns,
4
);
assert_eq!(
gdck_config::load(&toml)
.expect("should load")
.config
.lint
.max_returns,
5
);
}
#[test]
fn a_file_that_is_not_there_is_an_error_rather_than_a_shrug() {
let sandbox = Sandbox::new("missing");
let error = gdck_config::load(&sandbox.root.join("nope.toml")).expect_err("should not load");
assert_eq!(error.line, None);
assert!(!error.message.is_empty());
}
#[test]
fn everything_a_project_can_set_survives_the_round_trip_to_disk() {
let sandbox = Sandbox::new("round-trip");
sandbox.write(
"gdck.toml",
"# A project that has made up its mind.\n\
[format]\n\
line-length = 88\n\
indent = \"spaces\"\n\
indent-width = 2\n\
safety-checks = false\n\
\n\
[lint]\n\
max-file-lines = 400\n\
code-order = \"fix-when-safe\"\n\
disable = [\"max-returns\"]\n\
\n\
[files]\n\
exclude = [\".git\", \"vendor\"]\n",
);
let config = resolve(&sandbox.root).config;
assert_eq!(config.format.line_length, 88);
assert_eq!(config.format.indent, IndentStyle::Spaces(2));
assert!(!config.format.safety_checks);
assert_eq!(config.lint.max_line_length, 88, "the linter follows suit");
assert_eq!(config.lint.max_file_lines, 400);
assert_eq!(config.lint.code_order, CodeOrderFix::WholeFileWhenSafe);
assert_eq!(config.lint.disabled, ["max-returns"]);
assert!(config.is_excluded_dir("vendor"));
assert!(
!config.is_excluded_dir("addons"),
"the list replaces, not adds"
);
sandbox.write("again/gdck.toml", &config.to_toml());
let again = resolve(&sandbox.root.join("again")).config;
assert_eq!(
again.lint.declaration_order,
Some(DeclarationGroup::GUIDE_ORDER.to_vec())
);
let mut normalised = again.clone();
normalised.lint.declaration_order = None;
assert_eq!(normalised, config);
}