//! Tests which use upstream nix as an oracle to test evaluation against use std::{env, path::PathBuf, process::Command}; use pretty_assertions::assert_eq; use tempdir::TempDir; fn nix_binary_path() -> PathBuf { env::var("NIX_INSTANTIATE_BINARY_PATH") .unwrap_or_else(|_| "nix-instantiate".to_owned()) .into() } #[derive(Clone, Copy)] enum Strictness { Lazy, Strict, } fn nix_eval(expr: &str, strictness: Strictness) -> String { let store_dir = TempDir::new("store-dir").unwrap(); let mut args = match strictness { Strictness::Lazy => vec![], Strictness::Strict => vec!["--strict"], }; args.extend_from_slice(&["--eval", "-E"]); let output = Command::new(nix_binary_path()) .args(&args[..]) .arg(format!("({expr})")) .env( "NIX_REMOTE", format!("local?root={}", store_dir.path().display()), ) .output() .unwrap(); if !output.status.success() { panic!( "nix eval {expr} failed!\n stdout: {}\n stderr: {}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ) } String::from_utf8(output.stdout).unwrap() } /// Compare the evaluation of the given nix expression in nix (using the /// `NIX_INSTANTIATE_BINARY_PATH` env var to resolve the `nix-instantiate` binary) and tvix, and /// assert that the result is identical #[track_caller] fn compare_eval(expr: &str, strictness: Strictness) { let nix_result = nix_eval(expr, strictness); let mut eval = tvix_eval::Evaluation::new(expr, None); eval.strict = matches!(strictness, Strictness::Strict); eval.io_handle = Box::new(tvix_eval::StdIO); let tvix_result = eval .evaluate() .value .expect("tvix evaluation should succeed") .to_string(); assert_eq!(nix_result.trim(), tvix_result); } /// Generate a suite of tests which call [`compare_eval`] on expressions, checking that nix and tvix /// return identical results. macro_rules! compare_eval_tests { ($strictness:expr, {}) => {}; ($strictness:expr, {$(#[$meta:meta])* $test_name: ident($expr: expr); $($rest:tt)*}) => { #[test] $(#[$meta])* fn $test_name() { compare_eval($expr, $strictness); } compare_eval_tests!($strictness, { $($rest)* }); } } macro_rules! compare_strict_eval_tests { ($($tests:tt)*) => { compare_eval_tests!(Strictness::Lazy, { $($tests)* }); } } compare_strict_eval_tests! { literal_int("1"); add_ints("1 + 1"); add_lists("[1 2] ++ [3 4]"); add_paths(r#"[ (./. + "/") (./foo + "bar") (let name = "bar"; in ./foo + name) (let name = "bar"; in ./foo + "${name}") (let name = "bar"; in ./foo + "/" + "${name}") (let name = "bar"; in ./foo + "/${name}") (./. + ./.) ]"#); }