about summary refs log blame commit diff
path: root/tvix/eval/tests/nix_oracle.rs
blob: 5a5be56f70230f49ef6a8c03ad1603797adb6e89 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                        






                                                           

                                                       





                                               
                                                
                        





















                                                                                                

                                                     
                                                          
                                                           


                                                


                                                 
                     






                                                                                                    

                                                                                             


                         
                                             

         






                                                              


     
                            


                                








                                                      
 
//! 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}")
        (./. + ./.)
    ]"#);
}