use std::path::PathBuf; use std::process::{Command, Output}; use googletest::matchers::eq; use googletest::{OrFail, expect_that, gtest}; use serde_json::Value; #[gtest] fn debug_matches_fixture_representations() -> googletest::Result<()> { for name in ["clean", "lint_findings"] { let output = run_debug(&[&format!("testdata/{name}.nix")])?; expect_that!(output.status.code(), eq(Some(0))); expect_that!(output.stderr.is_empty(), eq(true)); let actual: Value = serde_json::from_slice(&output.stdout).or_fail()?; let expected: Value = serde_json::from_str( &std::fs::read_to_string(fixture_dir()?.join(format!("testdata/{name}.json"))) .or_fail()?, ) .or_fail()?; expect_that!(actual, eq(&expected)); } Ok(()) } #[gtest] fn debug_retains_parse_errors() -> googletest::Result<()> { let output = run_debug(&["testdata/invalid.nix"])?; expect_that!(output.status.code(), eq(Some(2))); expect_that!(output.stderr.is_empty(), eq(true)); let actual: Value = serde_json::from_slice(&output.stdout).or_fail()?; expect_that!( actual["files"][0]["parsed"]["errors"] .as_array() .or_fail()? .is_empty(), eq(false) ); Ok(()) } #[gtest] fn debug_orders_and_deduplicates_files() -> googletest::Result<()> { let first = run_debug(&[ "testdata/lint_findings.nix", "testdata/clean.nix", "testdata/clean.nix", ])?; let second = run_debug(&["testdata/clean.nix", "testdata/lint_findings.nix"])?; expect_that!(first.status.code(), eq(Some(0))); expect_that!(second.status.code(), eq(Some(0))); expect_that!(first.stdout, eq(&second.stdout)); Ok(()) } #[gtest] fn debug_reports_io_errors_on_stderr() -> googletest::Result<()> { let output = run_debug(&["testdata/missing.nix"])?; expect_that!(output.status.code(), eq(Some(2))); expect_that!(output.stdout.is_empty(), eq(true)); expect_that!(output.stderr.is_empty(), eq(false)); Ok(()) } fn run_debug(paths: &[&str]) -> googletest::Result { let directory = fixture_dir()?; static NEXT_ID: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); let id = NEXT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let working = PathBuf::from(std::env::var("TEST_TMPDIR").or_fail()?).join(id.to_string()); std::fs::create_dir_all(working.join("testdata")).or_fail()?; // Discovery skips symlinks, including Bazel's fixture runfiles. for name in ["clean", "lint_findings", "invalid"] { let path = format!("testdata/{name}.nix"); std::fs::copy(directory.join(&path), working.join(&path)).or_fail()?; } let output = Command::new(directory.join("xatu")) .current_dir(&working) .env_remove("BUILD_WORKSPACE_DIRECTORY") .arg("--debug") .args(paths) .output(); std::fs::remove_dir_all(working).or_fail()?; output.or_fail() } fn fixture_dir() -> googletest::Result { Ok(PathBuf::from(std::env::var("TEST_SRCDIR").or_fail()?) .join(std::env::var("TEST_WORKSPACE").or_fail()?) .join("src/xatu")) }