feat(nix): add style linter scaffold

This commit was merged in pull request #58.
This commit is contained in:
alexion committed 2026-09-16 16:03:59 -04:00
1 parent 82ad65a3a4
commit 7b09cea3af
8 files changed
+457

No files matched your search

+133
View File
@@ -0,0 +1,133 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};
use nix_style_linter::RunStatus;
static TEST_DIRECTORY_ID: AtomicUsize = AtomicUsize::new(0);
#[test]
fn malformed_file_reports_parse_error() {
let workspace = TestWorkspace::new("malformed_file_reports_parse_error");
let path = workspace.write("broken.nix", "let x = ; in x\n");
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Diagnostics);
assert_eq!(output.stderr, "");
assert!(output.stdout.starts_with(&format!(
"{}:1:9: parse-error: unexpected TOKEN_SEMICOLON at 8..9",
workspace.path("broken.nix").display()
)));
}
#[test]
fn valid_file_exits_clean() {
let workspace = TestWorkspace::new("valid_file_exits_clean");
let path = workspace.write("valid.nix", "let x = 1; in x\n");
let output = run_linter(&[path]);
assert_eq!(output.status, RunStatus::Clean);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "");
}
#[test]
fn missing_path_reports_tool_error() {
let workspace = TestWorkspace::new("missing_path_reports_tool_error");
let path = workspace.path("missing.nix");
let output = run_linter(&[path.clone()]);
assert_eq!(output.status, RunStatus::ToolError);
assert_eq!(output.stdout, "");
assert!(output.stderr.starts_with(&format!(
"error: failed to inspect {}:",
path.display()
)));
}
#[test]
fn directory_inputs_recurse_through_nix_files() {
let workspace = TestWorkspace::new("directory_inputs_recurse_through_nix_files");
workspace.write("ignored.txt", "let x = ; in x\n");
workspace.write("nested/broken.nix", "let\n x = 1;\n y = ;\nin y\n");
let output = run_linter(&[workspace.root.clone()]);
assert_eq!(output.status, RunStatus::Diagnostics);
assert_eq!(output.stderr, "");
assert!(output.stdout.starts_with(&format!(
"{}:3:7: parse-error: unexpected TOKEN_SEMICOLON at 19..20",
workspace.path("nested/broken.nix").display()
)));
assert!(!output.stdout.contains("ignored.txt"));
}
#[test]
fn empty_invocation_reports_tool_error() {
let output = run_linter(&[]);
assert_eq!(output.status, RunStatus::ToolError);
assert_eq!(output.stdout, "");
assert_eq!(output.stderr, "error: expected at least one path\n");
}
struct LinterOutput {
status: RunStatus,
stdout: String,
stderr: String,
}
fn run_linter(paths: &[PathBuf]) -> LinterOutput {
let mut stdout = Vec::new();
let mut stderr = Vec::new();
let status = nix_style_linter::run(paths, &mut stdout, &mut stderr).unwrap();
LinterOutput {
status,
stdout: String::from_utf8(stdout).unwrap(),
stderr: String::from_utf8(stderr).unwrap(),
}
}
struct TestWorkspace {
root: PathBuf,
}
impl TestWorkspace {
fn new(name: &str) -> Self {
let id = TEST_DIRECTORY_ID.fetch_add(1, Ordering::Relaxed);
let root = std::env::temp_dir().join(format!(
"nix-style-linter-{process}-{name}-{id}",
process = std::process::id()
));
if root.exists() {
fs::remove_dir_all(&root).unwrap();
}
fs::create_dir_all(&root).unwrap();
Self { root }
}
fn path(&self, relative: impl AsRef<Path>) -> PathBuf {
self.root.join(relative)
}
fn write(&self, relative: impl AsRef<Path>, contents: &str) -> PathBuf {
let path = self.path(relative);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).unwrap();
}
fs::write(&path, contents).unwrap();
path
}
}
impl Drop for TestWorkspace {
fn drop(&mut self) {
fs::remove_dir_all(&self.root).unwrap();
}
}