This commit is contained in:
imxyy1soope1 committed 2026-03-14 10:28:43 +08:00
1 parent 7a7229d70e
commit 0c9a391618
511 files changed
+234 -12772

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
use fix::value::Value;
use crate::utils::{eval, eval_result};
#[test_log::test]
fn arithmetic() {
assert_eq!(eval("1 + 1"), Value::Int(2));
}
#[test_log::test]
fn simple_function_application() {
assert_eq!(eval("(x: x) 1"), Value::Int(1));
}
#[test_log::test]
fn curried_function() {
assert_eq!(eval("(x: y: x - y) 2 1"), Value::Int(1));
}
#[test_log::test]
fn rec_attrset() {
assert_eq!(eval("rec { b = a; a = 1; }.b"), Value::Int(1));
}
#[test_log::test]
fn let_binding() {
assert_eq!(eval("let b = a; a = 1; in b"), Value::Int(1));
}
#[test_log::test]
fn fibonacci() {
assert_eq!(
eval(
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30"
),
Value::Int(832040)
);
}
#[test_log::test]
fn fixed_point_combinator() {
assert_eq!(
eval("((f: let x = f x; in x)(self: { x = 1; y = self.x + 1; })).y"),
Value::Int(2)
);
}
#[test_log::test]
fn conditional_true() {
assert_eq!(eval("if true then 1 else 0"), Value::Int(1));
}
#[test_log::test]
fn conditional_false() {
assert_eq!(eval("if false then 1 else 0"), Value::Int(0));
}
#[test_log::test]
fn nested_let() {
assert_eq!(
eval("let x = 1; in let y = x + 1; z = y + 1; in z"),
Value::Int(3)
);
}
#[test_log::test]
fn rec_inherit_fails() {
assert!(eval_result("{ inherit x; }").is_err());
}
+326
View File
@@ -0,0 +1,326 @@
use std::collections::BTreeMap;
use fix::value::{AttrSet, List, Value};
use crate::utils::eval;
#[test_log::test]
fn builtins_accessible() {
let result = eval("builtins");
assert!(matches!(result, Value::AttrSet(_)));
}
#[test_log::test]
fn builtins_self_reference() {
let result = eval("builtins.builtins");
assert!(matches!(result, Value::AttrSet(_)));
}
#[test_log::test]
fn builtins_add() {
assert_eq!(eval("builtins.add 1 2"), Value::Int(3));
}
#[test_log::test]
fn builtins_length() {
assert_eq!(eval("builtins.length [1 2 3]"), Value::Int(3));
}
#[test_log::test]
fn builtins_map() {
assert_eq!(
eval("builtins.map (x: x * 2) [1 2 3]"),
Value::List(List::new(vec![Value::Int(2), Value::Int(4), Value::Int(6)]))
);
}
#[test_log::test]
fn builtins_filter() {
assert_eq!(
eval("builtins.filter (x: x > 1) [1 2 3]"),
Value::List(List::new(vec![Value::Int(2), Value::Int(3)]))
);
}
#[test_log::test]
fn builtins_attrnames() {
let result = eval("builtins.attrNames { a = 1; b = 2; }");
assert!(matches!(result, Value::List(_)));
if let Value::List(list) = result {
assert_eq!(format!("{:?}", list).matches(',').count() + 1, 2);
}
}
#[test_log::test]
fn builtins_head() {
assert_eq!(eval("builtins.head [1 2 3]"), Value::Int(1));
}
#[test_log::test]
fn builtins_tail() {
assert_eq!(
eval("builtins.tail [1 2 3]"),
Value::List(List::new(vec![Value::Int(2), Value::Int(3)]))
);
}
#[test_log::test]
fn builtins_in_let() {
assert_eq!(eval("let b = builtins; in b.add 5 3"), Value::Int(8));
}
#[test_log::test]
fn builtins_in_with() {
assert_eq!(eval("with builtins; add 10 20"), Value::Int(30));
}
#[test_log::test]
fn builtins_nested_calls() {
assert_eq!(
eval("builtins.add (builtins.mul 2 3) (builtins.sub 10 5)"),
Value::Int(11)
);
}
#[test_log::test]
fn builtins_is_list() {
assert_eq!(eval("builtins.isList [1 2 3]"), Value::Bool(true));
}
#[test_log::test]
fn builtins_is_attrs() {
assert_eq!(eval("builtins.isAttrs { a = 1; }"), Value::Bool(true));
}
#[test_log::test]
fn builtins_is_function() {
assert_eq!(eval("builtins.isFunction (x: x)"), Value::Bool(true));
}
#[test_log::test]
fn builtins_is_null() {
assert_eq!(eval("builtins.isNull null"), Value::Bool(true));
}
#[test_log::test]
fn builtins_is_bool() {
assert_eq!(eval("builtins.isBool true"), Value::Bool(true));
}
#[test_log::test]
fn builtins_shadowing() {
assert_eq!(
eval("let builtins = { add = x: y: x - y; }; in builtins.add 5 3"),
Value::Int(2)
);
}
#[test_log::test]
fn builtins_lazy_evaluation() {
let result = eval("builtins.builtins.builtins.add 1 1");
assert_eq!(result, Value::Int(2));
}
#[test_log::test]
fn builtins_foldl() {
assert_eq!(
eval("builtins.foldl' (acc: x: acc + x) 0 [1 2 3 4 5]"),
Value::Int(15)
);
}
#[test_log::test]
fn builtins_elem() {
assert_eq!(eval("builtins.elem 2 [1 2 3]"), Value::Bool(true));
assert_eq!(eval("builtins.elem 5 [1 2 3]"), Value::Bool(false));
}
#[test_log::test]
fn builtins_concat_lists() {
assert_eq!(
eval("builtins.concatLists [[1 2] [3 4] [5]]"),
Value::List(List::new(vec![
Value::Int(1),
Value::Int(2),
Value::Int(3),
Value::Int(4),
Value::Int(5)
]))
);
}
#[test_log::test]
fn builtins_compare_versions_basic() {
assert_eq!(
eval("builtins.compareVersions \"1.0\" \"2.3\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.1\" \"2.3\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3\" \"2.3\""),
Value::Int(0)
);
assert_eq!(
eval("builtins.compareVersions \"2.5\" \"2.3\""),
Value::Int(1)
);
assert_eq!(
eval("builtins.compareVersions \"3.1\" \"2.3\""),
Value::Int(1)
);
}
#[test_log::test]
fn builtins_compare_versions_components() {
assert_eq!(
eval("builtins.compareVersions \"2.3.1\" \"2.3\""),
Value::Int(1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3\" \"2.3.1\""),
Value::Int(-1)
);
}
#[test_log::test]
fn builtins_compare_versions_numeric_vs_alpha() {
// Numeric component comes before alpha component
assert_eq!(
eval("builtins.compareVersions \"2.3.1\" \"2.3a\""),
Value::Int(1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3a\" \"2.3.1\""),
Value::Int(-1)
);
}
#[test_log::test]
fn builtins_compare_versions_pre() {
// "pre" is special: comes before everything except another "pre"
assert_eq!(
eval("builtins.compareVersions \"2.3pre1\" \"2.3\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3pre3\" \"2.3pre12\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3pre1\" \"2.3c\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3pre1\" \"2.3q\""),
Value::Int(-1)
);
}
#[test_log::test]
fn builtins_compare_versions_alpha() {
// Alphabetic comparison
assert_eq!(
eval("builtins.compareVersions \"2.3a\" \"2.3c\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3c\" \"2.3a\""),
Value::Int(1)
);
}
#[test_log::test]
fn builtins_compare_versions_symmetry() {
// Test symmetry: compareVersions(a, b) == -compareVersions(b, a)
assert_eq!(
eval("builtins.compareVersions \"1.0\" \"2.3\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"2.3\" \"1.0\""),
Value::Int(1)
);
}
#[test_log::test]
fn builtins_compare_versions_complex() {
// Complex version strings with multiple components
assert_eq!(
eval("builtins.compareVersions \"1.2.3.4\" \"1.2.3.5\""),
Value::Int(-1)
);
assert_eq!(
eval("builtins.compareVersions \"1.2.10\" \"1.2.9\""),
Value::Int(1)
);
assert_eq!(
eval("builtins.compareVersions \"1.2a3\" \"1.2a10\""),
Value::Int(-1)
);
}
#[test_log::test]
fn builtins_generic_closure() {
assert_eq!(
eval(
"with builtins; length (genericClosure { startSet = [ { key = 1; } ]; operator = { key }: [ { key = key / 1.; } ]; a = 1; })"
),
Value::Int(1),
);
assert_eq!(
eval(
"with builtins; (elemAt (genericClosure { startSet = [ { key = 1; } ]; operator = { key }: [ { key = key / 1.; } ]; a = 1; }) 0).key"
),
Value::Int(1),
);
}
#[test_log::test]
fn builtins_function_args() {
assert_eq!(
eval("builtins.functionArgs (x: 1)"),
Value::AttrSet(AttrSet::default())
);
assert_eq!(
eval("builtins.functionArgs ({}: 1)"),
Value::AttrSet(AttrSet::default())
);
assert_eq!(
eval("builtins.functionArgs ({...}: 1)"),
Value::AttrSet(AttrSet::default())
);
assert_eq!(
eval("builtins.functionArgs ({a}: 1)"),
Value::AttrSet(AttrSet::new(BTreeMap::from([(
"a".into(),
Value::Bool(false)
)])))
);
assert_eq!(
eval("builtins.functionArgs ({a, b ? 1}: 1)"),
Value::AttrSet(AttrSet::new(BTreeMap::from([
("a".into(), Value::Bool(false)),
("b".into(), Value::Bool(true))
])))
);
assert_eq!(
eval("builtins.functionArgs ({a, b ? 1, ...}: 1)"),
Value::AttrSet(AttrSet::new(BTreeMap::from([
("a".into(), Value::Bool(false)),
("b".into(), Value::Bool(true))
])))
);
}
#[test_log::test]
fn builtins_parse_drv_name() {
let result = eval(r#"builtins.parseDrvName "nix-js-0.1.0pre""#).unwrap_attr_set();
assert_eq!(result.get("name"), Some(&Value::String("nix-js".into())));
assert_eq!(
result.get("version"),
Some(&Value::String("0.1.0pre".into()))
);
}
+193
View File
@@ -0,0 +1,193 @@
use fix::value::Value;
use crate::utils::eval_result;
#[test_log::test]
fn to_file_simple() {
let result =
eval_result(r#"builtins.toFile "hello.txt" "Hello, World!""#).expect("Failed to evaluate");
match result {
Value::String(path) => {
assert!(path.contains("-hello.txt"));
assert!(std::path::Path::new(&path).exists());
let contents = std::fs::read_to_string(&path).expect("Failed to read file");
assert_eq!(contents, "Hello, World!");
}
_ => panic!("Expected string, got {:?}", result),
}
}
#[test_log::test]
fn to_file_with_references() {
let result = eval_result(
r#"
let
dep = builtins.toFile "dep.txt" "dependency";
in
builtins.toFile "main.txt" "Reference: ${dep}"
"#,
)
.expect("Failed to evaluate");
match result {
Value::String(path) => {
assert!(path.contains("-main.txt"));
let contents = std::fs::read_to_string(&path).expect("Failed to read file");
assert!(contents.contains("Reference: "));
assert!(contents.contains("-dep.txt"));
}
_ => panic!("Expected string"),
}
}
#[test_log::test]
fn to_file_invalid_name_with_slash() {
let result = eval_result(r#"builtins.toFile "foo/bar.txt" "content""#);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("name cannot contain '/'")
);
}
#[test_log::test]
fn to_file_invalid_name_dot() {
let result = eval_result(r#"builtins.toFile "." "content""#);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("invalid name"));
}
#[test_log::test]
fn to_file_invalid_name_dotdot() {
let result = eval_result(r#"builtins.toFile ".." "content""#);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("invalid name"));
}
#[test_log::test]
fn store_path_validation_not_in_store() {
let result = eval_result(r#"builtins.storePath "/tmp/foo""#);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("not in the Nix store")
);
}
#[test_log::test]
fn store_path_validation_malformed_hash() {
let dummy_file_result = eval_result(r#"builtins.toFile "dummy.txt" "content""#)
.expect("Failed to create dummy file");
let dummy_path = match dummy_file_result {
Value::String(ref p) => p.clone(),
_ => panic!("Expected string"),
};
let store_dir = std::path::Path::new(&dummy_path)
.parent()
.expect("Failed to get parent dir")
.to_str()
.expect("Failed to convert to string");
let test_path = format!("{}/invalid-hash-hello", store_dir);
let result = eval_result(&format!(r#"builtins.storePath "{}""#, test_path));
assert!(result.is_err());
let err_str = result.unwrap_err().to_string();
assert!(
err_str.contains("invalid") || err_str.contains("hash"),
"Expected hash validation error, got: {}",
err_str
);
}
#[test_log::test]
fn store_path_validation_missing_name() {
let dummy_file_result = eval_result(r#"builtins.toFile "dummy.txt" "content""#)
.expect("Failed to create dummy file");
let dummy_path = match dummy_file_result {
Value::String(ref p) => p.clone(),
_ => panic!("Expected string"),
};
let store_dir = std::path::Path::new(&dummy_path)
.parent()
.expect("Failed to get parent dir")
.to_str()
.expect("Failed to convert to string");
let test_path = format!("{}/abcd1234abcd1234abcd1234abcd1234", store_dir);
let result = eval_result(&format!(r#"builtins.storePath "{}""#, test_path));
assert!(result.is_err());
let err_str = result.unwrap_err().to_string();
assert!(
err_str.contains("missing name") || err_str.contains("format"),
"Expected missing name error, got: {}",
err_str
);
}
#[test_log::test]
fn to_file_curried_application() {
let result = eval_result(
r#"
let
makeFile = builtins.toFile "test.txt";
in
makeFile "test content"
"#,
)
.expect("Failed to evaluate");
match result {
Value::String(path) => {
assert!(path.contains("-test.txt"));
let contents = std::fs::read_to_string(&path).expect("Failed to read file");
assert_eq!(contents, "test content");
}
_ => panic!("Expected string"),
}
}
#[test_log::test]
fn to_file_number_conversion() {
let result = eval_result(r#"builtins.toFile "number.txt" (builtins.toString 42)"#)
.expect("Failed to evaluate");
match result {
Value::String(path) => {
let contents = std::fs::read_to_string(&path).expect("Failed to read file");
assert_eq!(contents, "42");
}
_ => panic!("Expected string"),
}
}
#[test_log::test]
fn to_file_list_conversion() {
let result = eval_result(
r#"builtins.toFile "list.txt" (builtins.concatStringsSep "\n" ["line1" "line2" "line3"])"#,
)
.expect("Failed to evaluate");
match result {
Value::String(path) => {
let contents = std::fs::read_to_string(&path).expect("Failed to read file");
assert_eq!(contents, "line1\nline2\nline3");
}
_ => panic!("Expected string"),
}
}
+687
View File
@@ -0,0 +1,687 @@
use fix::value::Value;
use crate::utils::{eval_deep, eval_deep_result};
#[test_log::test]
fn add_operator_preserves_derivation_context() {
let result = eval_deep(
r#"
let
dep = derivation { name = "dep"; builder = "/bin/sh"; system = "x86_64-linux"; outputs = ["out" "dev"]; };
getOutput = output: pkg: pkg.${output} or pkg.out or pkg;
user = derivation {
name = "user";
builder = "/bin/sh";
system = "x86_64-linux";
libPath = (getOutput "lib" dep) + "/lib";
devPath = dep.dev + "/include";
};
in user.drvPath
"#,
);
let nix_result = eval_deep(
r#"
let
dep = derivation { name = "dep"; builder = "/bin/sh"; system = "x86_64-linux"; outputs = ["out" "dev"]; };
user = derivation {
name = "user";
builder = "/bin/sh";
system = "x86_64-linux";
libPath = "${dep.out}/lib";
devPath = "${dep.dev}/include";
};
in user.drvPath
"#,
);
assert_eq!(result, nix_result);
}
#[test_log::test]
fn derivation_minimal() {
let result = eval_deep(
r#"derivation { name = "hello"; builder = "/bin/sh"; system = "x86_64-linux"; }"#,
);
match result {
Value::AttrSet(attrs) => {
assert_eq!(attrs.get("type"), Some(&Value::String("derivation".into())));
assert_eq!(attrs.get("name"), Some(&Value::String("hello".into())));
assert_eq!(attrs.get("builder"), Some(&Value::String("/bin/sh".into())));
assert_eq!(
attrs.get("system"),
Some(&Value::String("x86_64-linux".into()))
);
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
if let Some(Value::String(path)) = attrs.get("outPath") {
assert_eq!(path, "/nix/store/pnwh4xsfs4j508bs9iw6bpkyc4zw6ryx-hello");
} else {
panic!("outPath should be a string");
}
if let Some(Value::String(path)) = attrs.get("drvPath") {
assert_eq!(
path,
"/nix/store/x0sj6ynccvc1a8kxr8fifnlf7qlxw6hd-hello.drv"
);
} else {
panic!("drvPath should be a string");
}
}
_ => panic!("Expected AttrSet, got {:?}", result),
}
}
#[test_log::test]
fn derivation_with_args() {
let result = eval_deep(
r#"derivation {
name = "test";
builder = "/bin/sh";
system = "x86_64-linux";
args = ["-c" "echo hello"];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("args"));
if let Some(Value::List(args)) = attrs.get("args") {
assert_eq!(args.len(), 2);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn derivation_to_string() {
let result = eval_deep(
r#"toString (derivation { name = "foo"; builder = "/bin/sh"; system = "x86_64-linux"; })"#,
);
match result {
Value::String(s) => assert_eq!(s, "/nix/store/xpcvxsx5sw4rbq666blz6sxqlmsqphmr-foo"),
_ => panic!("Expected String, got {:?}", result),
}
}
#[test_log::test]
fn derivation_missing_name() {
let result =
eval_deep_result(r#"derivation { builder = "/bin/sh"; system = "x86_64-linux"; }"#);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("missing required attribute 'name'"));
}
#[test_log::test]
fn derivation_invalid_name_with_drv_suffix() {
let result = eval_deep_result(
r#"derivation { name = "foo.drv"; builder = "/bin/sh"; system = "x86_64-linux"; }"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("cannot end with .drv"));
}
#[test_log::test]
fn derivation_missing_builder() {
let result = eval_deep_result(r#"derivation { name = "test"; system = "x86_64-linux"; }"#);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("missing required attribute 'builder'"));
}
#[test_log::test]
fn derivation_missing_system() {
let result = eval_deep_result(r#"derivation { name = "test"; builder = "/bin/sh"; }"#);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("missing required attribute 'system'"));
}
#[test_log::test]
fn derivation_with_env_vars() {
let result = eval_deep(
r#"derivation {
name = "test";
builder = "/bin/sh";
system = "x86_64-linux";
MY_VAR = "hello";
ANOTHER = "world";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert_eq!(attrs.get("MY_VAR"), Some(&Value::String("hello".into())));
assert_eq!(attrs.get("ANOTHER"), Some(&Value::String("world".into())));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn derivation_strict() {
let result = eval_deep(
r#"builtins.derivationStrict { name = "test"; builder = "/bin/sh"; system = "x86_64-linux"; }"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("out"));
assert!(!attrs.contains_key("type"));
assert!(!attrs.contains_key("outPath"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn derivation_deterministic_paths() {
let expr = r#"derivation { name = "hello"; builder = "/bin/sh"; system = "x86_64-linux"; }"#;
let result1 = eval_deep(expr);
let result2 = eval_deep(expr);
match (result1, result2) {
(Value::AttrSet(attrs1), Value::AttrSet(attrs2)) => {
assert_eq!(attrs1.get("drvPath"), attrs2.get("drvPath"));
assert_eq!(attrs1.get("outPath"), attrs2.get("outPath"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn derivation_escaping_in_aterm() {
let result = eval_deep(
r#"derivation {
name = "test";
builder = "/bin/sh";
system = "x86_64-linux";
args = ["-c" "echo \"hello\nworld\""];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("outPath"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn multi_output_two_outputs() {
let drv = eval_deep(
r#"derivation {
name = "multi";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
}"#,
);
match drv {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("out"));
assert!(attrs.contains_key("dev"));
assert!(attrs.contains_key("outPath"));
if let Some(Value::String(drv_path)) = attrs.get("drvPath") {
assert_eq!(
drv_path,
"/nix/store/vmyjryfipkn9ss3ya23hk8p3m58l6dsl-multi.drv"
);
} else {
panic!(
"drvPath should be a string, got: {:?}",
attrs.get("drvPath")
);
}
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert_eq!(
out_path,
"/nix/store/a3d95yg9d215c54n0ybr4npmpnj29229-multi"
);
} else {
panic!("outPath should be a string");
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn multi_output_three_outputs() {
let result = eval_deep(
r#"derivation {
name = "three";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev" "doc"];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("out"));
assert!(attrs.contains_key("dev"));
assert!(attrs.contains_key("doc"));
// Verify exact paths match CppNix
if let Some(Value::String(drv_path)) = attrs.get("drvPath") {
assert_eq!(
drv_path,
"/nix/store/w08rpwvs5j9yxvdx5f5yg0p5i3ncazdx-three.drv"
);
}
if let Some(Value::String(out_path)) = attrs.get("out") {
assert_eq!(
out_path,
"/nix/store/i479clih5pb6bn2d2b758sbaylvbs2cl-three"
);
}
if let Some(Value::String(dev_path)) = attrs.get("dev") {
assert_eq!(
dev_path,
"/nix/store/gg8v395vci5xg1i9grc8ifh5xagw5f2j-three-dev"
);
}
if let Some(Value::String(doc_path)) = attrs.get("doc") {
assert_eq!(
doc_path,
"/nix/store/p2avgz16qx5k2jgnq3ch04k154xj1ac0-three-doc"
);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn multi_output_backward_compat() {
let result = eval_deep(
r#"derivation {
name = "compat";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out"];
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("out"));
if let (Some(Value::String(out_path)), Some(Value::String(out))) =
(attrs.get("outPath"), attrs.get("out"))
{
assert_eq!(out_path, out);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn multi_output_deterministic() {
let result1 = eval_deep(
r#"derivation {
name = "determ";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
}"#,
);
let result2 = eval_deep(
r#"derivation {
name = "determ";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
}"#,
);
assert_eq!(result1, result2);
}
#[test_log::test]
fn fixed_output_sha256_flat() {
let result = eval_deep(
r#"derivation {
name = "fixed";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputHashAlgo = "sha256";
outputHashMode = "flat";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
// Verify exact paths match CppNix
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert_eq!(
out_path,
"/nix/store/ap9h69qwrm5060ldi96axyklh3pr3yjn-fixed"
);
}
if let Some(Value::String(drv_path)) = attrs.get("drvPath") {
assert_eq!(
drv_path,
"/nix/store/kj9gsfz5cngc38n1xlf6ljlgvnsfg0cj-fixed.drv"
);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn fixed_output_missing_hashalgo() {
assert!(
eval_deep_result(
r#"derivation {
name = "default";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
}"#,
)
.is_err()
);
}
#[test_log::test]
fn fixed_output_recursive_mode() {
let result = eval_deep(
r#"derivation {
name = "recursive";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "1111111111111111111111111111111111111111111111111111111111111111";
outputHashAlgo = "sha256";
outputHashMode = "recursive";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
// Verify exact path matches CppNix
if let Some(Value::String(out_path)) = attrs.get("outPath") {
assert_eq!(
out_path,
"/nix/store/qyal5s16hfwxhz5zwpf8h8yv2bs84z56-recursive"
);
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn fixed_output_rejects_multi_output() {
let result = eval_deep_result(
r#"derivation {
name = "invalid";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputHashAlgo = "sha256";
outputs = ["out" "dev"];
}"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("fixed-output") && err_msg.contains("one"));
}
#[test_log::test]
fn fixed_output_invalid_hash_mode() {
let result = eval_deep_result(
r#"derivation {
name = "invalid";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputHashMode = "invalid";
}"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("outputHashMode") && err_msg.contains("invalid"));
}
#[test_log::test]
fn structured_attrs_basic() {
let result = eval_deep(
r#"derivation {
name = "struct";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
foo = "bar";
count = 42;
enabled = true;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("foo"));
assert!(attrs.contains_key("count"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn structured_attrs_nested() {
let result = eval_deep(
r#"derivation {
name = "nested";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
data = { x = 1; y = [2 3]; };
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("data"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn structured_attrs_rejects_functions() {
let result = eval_deep_result(
r#"derivation {
name = "invalid";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
func = x: x + 1;
}"#,
);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("cannot convert lambda to JSON"));
}
#[test_log::test]
fn structured_attrs_false() {
let result = eval_deep(
r#"derivation {
name = "normal";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = false;
foo = "bar";
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("foo"));
if let Some(Value::String(val)) = attrs.get("foo") {
assert_eq!(val, "bar");
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn ignore_nulls_true() {
let result = eval_deep(
r#"derivation {
name = "ignore";
builder = "/bin/sh";
system = "x86_64-linux";
__ignoreNulls = true;
foo = "bar";
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("foo"));
assert!(attrs.contains_key("nullValue"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn ignore_nulls_false() {
let result = eval_deep(
r#"derivation {
name = "keep";
builder = "/bin/sh";
system = "x86_64-linux";
__ignoreNulls = false;
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("nullValue"));
if let Some(Value::String(val)) = attrs.get("nullValue") {
assert_eq!(val, "");
}
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn ignore_nulls_with_structured_attrs() {
let result = eval_deep(
r#"derivation {
name = "combined";
builder = "/bin/sh";
system = "x86_64-linux";
__structuredAttrs = true;
__ignoreNulls = true;
foo = "bar";
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("foo"));
assert!(attrs.contains_key("nullValue"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn all_features_combined() {
let result = eval_deep(
r#"derivation {
name = "all";
builder = "/bin/sh";
system = "x86_64-linux";
outputs = ["out" "dev"];
__structuredAttrs = true;
__ignoreNulls = true;
data = { x = 1; };
nullValue = null;
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("out"));
assert!(attrs.contains_key("dev"));
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("data"));
assert!(attrs.contains_key("nullValue"));
}
_ => panic!("Expected AttrSet"),
}
}
#[test_log::test]
fn fixed_output_with_structured_attrs() {
let result = eval_deep(
r#"derivation {
name = "fixstruct";
builder = "/bin/sh";
system = "x86_64-linux";
outputHash = "0000000000000000000000000000000000000000000000000000000000000000";
outputHashAlgo = "sha256";
__structuredAttrs = true;
data = { key = "value"; };
}"#,
);
match result {
Value::AttrSet(attrs) => {
assert!(attrs.contains_key("outPath"));
assert!(attrs.contains_key("drvPath"));
assert!(attrs.contains_key("data"));
}
_ => panic!("Expected AttrSet"),
}
}
+36
View File
@@ -0,0 +1,36 @@
use crate::utils::eval;
#[test_log::test]
fn test_find_file_corepkg_fetchurl() {
let result = eval(
r#"
let
searchPath = [];
lookupPath = "nix/fetchurl.nix";
in
builtins.findFile searchPath lookupPath
"#,
);
assert!(result.to_string().contains("fetchurl.nix"));
}
#[test_log::test]
fn test_lookup_path_syntax() {
let result = eval(r#"<nix/fetchurl.nix>"#);
assert!(result.to_string().contains("fetchurl.nix"));
}
#[test_log::test]
fn test_import_corepkg() {
let result = eval(
r#"
let
fetchurl = import <nix/fetchurl.nix>;
in
builtins.typeOf fetchurl
"#,
);
assert_eq!(result.to_string(), "\"lambda\"");
}
+74
View File
@@ -0,0 +1,74 @@
use fix::value::{List, Value};
use crate::utils::{eval, eval_result};
#[test_log::test]
fn true_literal() {
assert_eq!(eval("true"), Value::Bool(true));
}
#[test_log::test]
fn false_literal() {
assert_eq!(eval("false"), Value::Bool(false));
}
#[test_log::test]
fn null_literal() {
assert_eq!(eval("null"), Value::Null);
}
#[test_log::test]
fn map_function() {
assert_eq!(
eval("map (x: x * 2) [1 2 3]"),
Value::List(List::new(vec![Value::Int(2), Value::Int(4), Value::Int(6)]))
);
}
#[test_log::test]
fn is_null_function() {
assert_eq!(eval("isNull null"), Value::Bool(true));
assert_eq!(eval("isNull 5"), Value::Bool(false));
}
#[test_log::test]
fn shadow_true() {
assert_eq!(eval("let true = false; in true"), Value::Bool(false));
}
#[test_log::test]
fn shadow_map() {
assert_eq!(eval("let map = x: y: x; in map 1 2"), Value::Int(1));
}
#[test_log::test]
fn mixed_usage() {
assert_eq!(
eval("if true then map (x: x + 1) [1 2] else []"),
Value::List(List::new(vec![Value::Int(2), Value::Int(3)]))
);
}
#[test_log::test]
fn in_let_bindings() {
assert_eq!(
eval("let x = true; y = false; in x && y"),
Value::Bool(false)
);
}
#[test_log::test]
fn shadow_in_function() {
assert_eq!(eval("(true: true) false"), Value::Bool(false));
}
#[test_log::test]
fn throw_function() {
let result = eval_result("throw \"error message\"");
assert!(result.is_err());
}
#[test_log::test]
fn to_string_function() {
assert_eq!(eval("toString 42"), Value::String("42".to_string()));
}
+120
View File
@@ -0,0 +1,120 @@
use fix::value::Value;
use crate::utils::{eval, eval_result};
#[test_log::test]
fn required_parameters() {
assert_eq!(eval("({ a, b }: a + b) { a = 1; b = 2; }"), Value::Int(3));
}
#[test_log::test]
fn missing_required_parameter() {
let result = eval_result("({ a, b }: a + b) { a = 1; }");
assert!(result.is_err());
}
#[test_log::test]
fn all_required_parameters_present() {
assert_eq!(
eval("({ x, y, z }: x + y + z) { x = 1; y = 2; z = 3; }"),
Value::Int(6)
);
}
#[test_log::test]
fn reject_unexpected_arguments() {
let result = eval_result("({ a, b }: a + b) { a = 1; b = 2; c = 3; }");
assert!(result.is_err());
}
#[test_log::test]
fn ellipsis_accepts_extra_arguments() {
assert_eq!(
eval("({ a, b, ... }: a + b) { a = 1; b = 2; c = 3; }"),
Value::Int(3)
);
}
#[test_log::test]
fn default_parameters() {
assert_eq!(eval("({ a, b ? 5 }: a + b) { a = 1; }"), Value::Int(6));
}
#[test_log::test]
fn override_default_parameter() {
assert_eq!(
eval("({ a, b ? 5 }: a + b) { a = 1; b = 10; }"),
Value::Int(11)
);
}
#[test_log::test]
fn at_pattern_alias() {
assert_eq!(
eval("(args@{ a, b }: args.a + args.b) { a = 1; b = 2; }"),
Value::Int(3)
);
}
#[test_log::test]
fn simple_parameter_no_validation() {
assert_eq!(eval("(x: x.a + x.b) { a = 1; b = 2; }"), Value::Int(3));
}
#[test_log::test]
fn simple_parameter_accepts_any_argument() {
assert_eq!(eval("(x: x) 42"), Value::Int(42));
}
#[test_log::test]
fn nested_function_parameters() {
assert_eq!(
eval("({ a }: { b }: a + b) { a = 5; } { b = 3; }"),
Value::Int(8)
);
}
#[test_log::test]
fn pattern_param_simple_reference_in_default() {
assert_eq!(eval("({ a, b ? a }: b) { a = 10; }"), Value::Int(10));
}
#[test_log::test]
fn pattern_param_multiple_references_in_default() {
assert_eq!(
eval("({ a, b ? a + 5, c ? 1 }: b + c) { a = 10; }"),
Value::Int(16)
);
}
#[test_log::test]
fn pattern_param_mutual_reference() {
assert_eq!(
eval("({ a, b ? c + 1, c ? 5 }: b) { a = 1; }"),
Value::Int(6)
);
}
#[test_log::test]
fn pattern_param_override_mutual_reference() {
assert_eq!(
eval("({ a, b ? c + 1, c ? 5 }: b) { a = 1; c = 10; }"),
Value::Int(11)
);
}
#[test_log::test]
fn pattern_param_reference_list() {
assert_eq!(
eval("({ a, b ? [ a 2 ] }: builtins.elemAt b 0) { a = 42; }"),
Value::Int(42)
);
}
#[test_log::test]
fn pattern_param_alias_in_default() {
assert_eq!(
eval("(args@{ a, b ? args.a + 10 }: b) { a = 5; }"),
Value::Int(15)
);
}
+368
View File
@@ -0,0 +1,368 @@
use fix::context::Context;
use fix::error::Source;
use fix::value::Value;
use crate::utils::{eval, eval_result};
#[test_log::test]
fn import_absolute_path() {
let temp_dir = tempfile::tempdir().unwrap();
let lib_path = temp_dir.path().join("nix_test_lib.nix");
std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap();
let expr = format!(r#"(import "{}").add 3 5"#, lib_path.display());
assert_eq!(eval(&expr), Value::Int(8));
}
#[test_log::test]
fn import_nested() {
let temp_dir = tempfile::tempdir().unwrap();
let lib_path = temp_dir.path().join("lib.nix");
std::fs::write(&lib_path, "{ add = a: b: a + b; }").unwrap();
let main_path = temp_dir.path().join("main.nix");
let main_content = format!(
r#"let lib = import {}; in {{ result = lib.add 10 20; }}"#,
lib_path.display()
);
std::fs::write(&main_path, main_content).unwrap();
let expr = format!(r#"(import "{}").result"#, main_path.display());
assert_eq!(eval(&expr), Value::Int(30));
}
#[test_log::test]
fn import_relative_path() {
let temp_dir = tempfile::tempdir().unwrap();
let subdir = temp_dir.path().join("subdir");
std::fs::create_dir_all(&subdir).unwrap();
let lib_path = temp_dir.path().join("lib.nix");
std::fs::write(&lib_path, "{ multiply = a: b: a * b; }").unwrap();
let helper_path = subdir.join("helper.nix");
std::fs::write(&helper_path, "{ subtract = a: b: a - b; }").unwrap();
let main_path = temp_dir.path().join("main.nix");
let main_content = r#"
let
lib = import ./lib.nix;
helper = import ./subdir/helper.nix;
in {
result1 = lib.multiply 3 4;
result2 = helper.subtract 10 3;
}
"#;
std::fs::write(&main_path, main_content).unwrap();
let expr = format!(r#"let x = import "{}"; in x.result1"#, main_path.display());
assert_eq!(eval(&expr), Value::Int(12));
let expr = format!(r#"let x = import "{}"; in x.result2"#, main_path.display());
assert_eq!(eval(&expr), Value::Int(7));
}
#[test_log::test]
fn import_returns_function() {
let temp_dir = tempfile::tempdir().unwrap();
let func_path = temp_dir.path().join("nix_test_func.nix");
std::fs::write(&func_path, "x: x * 2").unwrap();
let expr = format!(r#"(import "{}") 5"#, func_path.display());
assert_eq!(eval(&expr), Value::Int(10));
}
#[test_log::test]
fn import_with_complex_dependency_graph() {
let temp_dir = tempfile::tempdir().unwrap();
let utils_path = temp_dir.path().join("utils.nix");
std::fs::write(&utils_path, "{ double = x: x * 2; }").unwrap();
let math_path = temp_dir.path().join("math.nix");
let math_content = r#"let utils = import ./utils.nix; in { triple = x: x + utils.double x; }"#;
std::fs::write(&math_path, math_content).unwrap();
let main_path = temp_dir.path().join("main.nix");
let main_content = r#"let math = import ./math.nix; in math.triple 5"#;
std::fs::write(&main_path, main_content).unwrap();
let expr = format!(r#"import "{}""#, main_path.display());
assert_eq!(eval(&expr), Value::Int(15));
}
// Tests for builtins.path
#[test_log::test]
fn path_with_file() {
let mut ctx = Context::new().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "Hello, World!").unwrap();
let expr = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
// Should return a store path string
if let Value::String(store_path) = result {
assert!(store_path.starts_with(ctx.get_store_dir()));
assert!(store_path.contains("test.txt"));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test_log::test]
fn path_with_custom_name() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("original.txt");
std::fs::write(&test_file, "Content").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; name = "custom-name"; }}"#,
test_file.display()
);
let result = eval(&expr);
if let Value::String(store_path) = result {
assert!(store_path.contains("custom-name"));
assert!(!store_path.contains("original.txt"));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test_log::test]
fn path_with_directory_recursive() {
let mut ctx = Context::new().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("mydir");
std::fs::create_dir_all(&test_dir).unwrap();
std::fs::write(test_dir.join("file1.txt"), "Content 1").unwrap();
std::fs::write(test_dir.join("file2.txt"), "Content 2").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; recursive = true; }}"#,
test_dir.display()
);
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
if let Value::String(store_path) = result {
assert!(store_path.starts_with(ctx.get_store_dir()));
assert!(store_path.contains("mydir"));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test_log::test]
fn path_flat_with_file() {
let mut ctx = Context::new().unwrap();
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("flat.txt");
std::fs::write(&test_file, "Flat content").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; recursive = false; }}"#,
test_file.display()
);
let result = ctx.eval(Source::new_eval(expr).unwrap()).unwrap();
if let Value::String(store_path) = result {
assert!(store_path.starts_with(ctx.get_store_dir()));
} else {
panic!("Expected string, got {:?}", result);
}
}
#[test_log::test]
fn path_flat_with_directory_fails() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("mydir");
std::fs::create_dir_all(&test_dir).unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; recursive = false; }}"#,
test_dir.display()
);
let result = eval_result(&expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("recursive") || err_msg.contains("regular file"));
}
#[test_log::test]
fn path_nonexistent_fails() {
let expr = r#"builtins.path { path = "/nonexistent/path/that/should/not/exist"; }"#;
let result = eval_result(expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("does not exist"));
}
#[test_log::test]
fn path_missing_path_param() {
let expr = r#"builtins.path { name = "test"; }"#;
let result = eval_result(expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("path") && err_msg.contains("required"));
}
#[test_log::test]
fn path_with_sha256() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("hash_test.txt");
std::fs::write(&test_file, "Test content for hashing").unwrap();
// First, get the hash by calling without sha256
let expr1 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
let result1 = eval(&expr1);
let store_path1 = match result1 {
Value::String(s) => s,
_ => panic!("Expected string"),
};
// Compute the actual hash (for testing, we'll just verify the same path is returned)
// In real usage, the user would know the hash beforehand
let expr2 = format!(r#"builtins.path {{ path = {}; }}"#, test_file.display());
let result2 = eval(&expr2);
let store_path2 = match result2 {
Value::String(s) => s,
_ => panic!("Expected string"),
};
// Same input should produce same output
assert_eq!(store_path1, store_path2);
}
#[test_log::test]
fn path_deterministic() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("deterministic.txt");
std::fs::write(&test_file, "Same content").unwrap();
let expr = format!(
r#"builtins.path {{ path = {}; name = "myfile"; }}"#,
test_file.display()
);
let result1 = eval(&expr);
let result2 = eval(&expr);
// Same inputs should produce same store path
assert_eq!(result1, result2);
}
#[test_log::test]
fn read_file_type_regular_file() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "Test content").unwrap();
let expr = format!(r#"builtins.readFileType {}"#, test_file.display());
assert_eq!(eval(&expr), Value::String("regular".to_string()));
}
#[test_log::test]
fn read_file_type_directory() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("testdir");
std::fs::create_dir(&test_dir).unwrap();
let expr = format!(r#"builtins.readFileType {}"#, test_dir.display());
assert_eq!(eval(&expr), Value::String("directory".to_string()));
}
#[test_log::test]
fn read_file_type_symlink() {
let temp_dir = tempfile::tempdir().unwrap();
let target = temp_dir.path().join("target.txt");
let symlink = temp_dir.path().join("link.txt");
std::fs::write(&target, "Target content").unwrap();
#[cfg(unix)]
std::os::unix::fs::symlink(&target, &symlink).unwrap();
#[cfg(unix)]
{
let expr = format!(r#"builtins.readFileType {}"#, symlink.display());
assert_eq!(eval(&expr), Value::String("symlink".to_string()));
}
}
#[test_log::test]
fn read_dir_basic() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("readdir_test");
std::fs::create_dir(&test_dir).unwrap();
std::fs::write(test_dir.join("file1.txt"), "Content 1").unwrap();
std::fs::write(test_dir.join("file2.txt"), "Content 2").unwrap();
std::fs::create_dir(test_dir.join("subdir")).unwrap();
let expr = format!(r#"builtins.readDir {}"#, test_dir.display());
let result = eval(&expr);
if let Value::AttrSet(attrs) = result {
assert_eq!(
attrs.get("file1.txt"),
Some(&Value::String("regular".to_string()))
);
assert_eq!(
attrs.get("file2.txt"),
Some(&Value::String("regular".to_string()))
);
assert_eq!(
attrs.get("subdir"),
Some(&Value::String("directory".to_string()))
);
assert_eq!(attrs.len(), 3);
} else {
panic!("Expected AttrSet, got {:?}", result);
}
}
#[test_log::test]
fn read_dir_empty() {
let temp_dir = tempfile::tempdir().unwrap();
let test_dir = temp_dir.path().join("empty_dir");
std::fs::create_dir(&test_dir).unwrap();
let expr = format!(r#"builtins.readDir {}"#, test_dir.display());
let result = eval(&expr);
if let Value::AttrSet(attrs) = result {
assert_eq!(attrs.len(), 0);
} else {
panic!("Expected AttrSet, got {:?}", result);
}
}
#[test_log::test]
fn read_dir_nonexistent_fails() {
let expr = r#"builtins.readDir "/nonexistent/directory""#;
let result = eval_result(expr);
assert!(result.is_err());
}
#[test_log::test]
fn read_dir_on_file_fails() {
let temp_dir = tempfile::tempdir().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "Test content").unwrap();
let expr = format!(r#"builtins.readDir {}"#, test_file.display());
let result = eval_result(&expr);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("not a directory"));
}
+319
View File
@@ -0,0 +1,319 @@
#![allow(non_snake_case)]
use std::path::PathBuf;
use fix::context::Context;
use fix::error::Source;
use fix::value::Value;
fn get_lang_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/tests/lang")
}
fn eval_file(name: &str) -> Result<(Value, Source), String> {
let lang_dir = get_lang_dir();
let nix_path = lang_dir.join(format!("{name}.nix"));
let expr = format!(r#"import "{}""#, nix_path.display());
let mut ctx = Context::new().map_err(|e| e.to_string())?;
let source = Source {
ty: fix::error::SourceType::File(nix_path.into()),
src: expr.into(),
};
ctx.eval_deep(source.clone())
.map(|val| (val, source))
.map_err(|e| e.to_string())
}
fn read_expected(name: &str) -> String {
let lang_dir = get_lang_dir();
let exp_path = lang_dir.join(format!("{name}.exp"));
std::fs::read_to_string(exp_path)
.expect("expected file should exist")
.trim_end()
.to_string()
}
fn format_value(value: &Value) -> String {
value.display_compat().to_string()
}
macro_rules! eval_okay_test {
($(#[$attr:meta])* $name:ident$(, $pre:expr)?) => {
$(#[$attr])*
#[test_log::test]
fn $name() {
$(($pre)();)?
let test_name = concat!("eval-okay-", stringify!($name))
.replace("_", "-")
.replace("r#", "");
let result = eval_file(&test_name);
match result {
Ok((value, source)) => {
let actual = format_value(&value);
let actual = actual.replace(
source
.get_dir()
.parent()
.unwrap()
.to_string_lossy()
.as_ref(),
"/pwd",
);
let expected = read_expected(&test_name);
assert_eq!(actual, expected, "Output mismatch for {}", test_name);
}
Err(e) => {
panic!("Test {} failed to evaluate: {}", test_name, e);
}
}
}
};
}
macro_rules! eval_fail_test {
($name:ident) => {
#[test_log::test]
fn $name() {
let test_name = concat!("eval-fail-", stringify!($name))
.replace("_", "-")
.replace("r#", "");
let result = eval_file(&test_name);
assert!(
result.is_err(),
"Test {} should have failed but succeeded with: {:?}",
test_name,
result
);
}
};
}
eval_okay_test!(any_all);
eval_okay_test!(arithmetic);
eval_okay_test!(attrnames);
eval_okay_test!(attrs);
eval_okay_test!(attrs2);
eval_okay_test!(attrs3);
eval_okay_test!(attrs4);
eval_okay_test!(attrs5);
eval_okay_test!(
#[ignore = "__overrides is not supported"]
attrs6
);
eval_okay_test!(
#[ignore = "requires --arg/--argstr CLI flags"]
autoargs
);
eval_okay_test!(backslash_newline_1);
eval_okay_test!(backslash_newline_2);
eval_okay_test!(baseNameOf);
eval_okay_test!(builtins);
eval_okay_test!(builtins_add);
eval_okay_test!(callable_attrs);
eval_okay_test!(catattrs);
eval_okay_test!(closure);
eval_okay_test!(comments);
eval_okay_test!(concat);
eval_okay_test!(concatmap);
eval_okay_test!(concatstringssep);
eval_okay_test!(context);
eval_okay_test!(context_introspection);
eval_okay_test!(convertHash);
eval_okay_test!(curpos);
eval_okay_test!(deepseq);
eval_okay_test!(delayed_with);
eval_okay_test!(delayed_with_inherit);
eval_okay_test!(deprecate_cursed_or);
eval_okay_test!(derivation_legacy);
eval_okay_test!(dynamic_attrs);
eval_okay_test!(dynamic_attrs_2);
eval_okay_test!(dynamic_attrs_bare);
eval_okay_test!(elem);
eval_okay_test!(empty_args);
eval_okay_test!(eq);
eval_okay_test!(eq_derivations);
eval_okay_test!(filter);
eval_okay_test!(
#[ignore = "not implemented: flakeRefToString"]
flake_ref_to_string
);
eval_okay_test!(flatten);
eval_okay_test!(float);
eval_okay_test!(floor_ceil);
eval_okay_test!(foldlStrict);
eval_okay_test!(foldlStrict_lazy_elements);
eval_okay_test!(foldlStrict_lazy_initial_accumulator);
eval_okay_test!(fromjson);
eval_okay_test!(fromjson_escapes);
eval_okay_test!(fromTOML);
eval_okay_test!(
#[ignore = "timestamps are not supported"]
fromTOML_timestamps
);
eval_okay_test!(functionargs);
eval_okay_test!(hashfile);
eval_okay_test!(hashstring);
eval_okay_test!(getattrpos);
eval_okay_test!(getattrpos_functionargs);
eval_okay_test!(getattrpos_undefined);
eval_okay_test!(getenv, || {
unsafe { std::env::set_var("TEST_VAR", "foo") };
});
eval_okay_test!(groupBy);
eval_okay_test!(r#if);
eval_okay_test!(ind_string);
eval_okay_test!(import);
eval_okay_test!(inherit_attr_pos);
eval_okay_test!(
#[ignore = "__overrides is not supported"]
inherit_from
);
eval_okay_test!(intersectAttrs);
eval_okay_test!(r#let);
eval_okay_test!(list);
eval_okay_test!(listtoattrs);
eval_okay_test!(logic);
eval_okay_test!(map);
eval_okay_test!(mapattrs);
eval_okay_test!(merge_dynamic_attrs);
eval_okay_test!(nested_with);
eval_okay_test!(new_let);
eval_okay_test!(null_dynamic_attrs);
eval_okay_test!(
#[ignore = "__overrides is not supported"]
overrides
);
eval_okay_test!(
#[ignore = "not implemented: parseFlakeRef"]
parse_flake_ref
);
eval_okay_test!(partition);
eval_okay_test!(path);
eval_okay_test!(pathexists);
eval_okay_test!(path_string_interpolation, || {
unsafe {
std::env::set_var("HOME", "/fake-home");
}
});
eval_okay_test!(patterns);
eval_okay_test!(print);
eval_okay_test!(readDir);
eval_okay_test!(readfile);
eval_okay_test!(readFileType);
eval_okay_test!(redefine_builtin);
eval_okay_test!(regex_match);
eval_okay_test!(regex_split);
eval_okay_test!(regression_20220122);
eval_okay_test!(regression_20220125);
eval_okay_test!(regrettable_rec_attrset_merge);
eval_okay_test!(remove);
eval_okay_test!(repeated_empty_attrs);
eval_okay_test!(repeated_empty_list);
eval_okay_test!(replacestrings);
eval_okay_test!(
#[ignore = "requires -I CLI flags"]
search_path
);
eval_okay_test!(scope_1);
eval_okay_test!(scope_2);
eval_okay_test!(scope_3);
eval_okay_test!(scope_4);
eval_okay_test!(scope_6);
eval_okay_test!(scope_7);
eval_okay_test!(seq);
eval_okay_test!(sort);
eval_okay_test!(splitversion);
eval_okay_test!(string);
eval_okay_test!(strings_as_attrs_names);
eval_okay_test!(substring);
eval_okay_test!(substring_context);
eval_okay_test!(symlink_resolution);
eval_okay_test!(
#[ignore = "TCO not implemented, also disabled in CppNix"]
tail_call_1
);
eval_okay_test!(tojson);
eval_okay_test!(toxml);
eval_okay_test!(toxml2);
eval_okay_test!(tryeval);
eval_okay_test!(types);
eval_okay_test!(versions);
eval_okay_test!(with);
eval_okay_test!(zipAttrsWith);
eval_fail_test!(fail_abort);
eval_fail_test!(fail_addDrvOutputDependencies_empty_context);
eval_fail_test!(fail_addDrvOutputDependencies_multi_elem_context);
eval_fail_test!(fail_addDrvOutputDependencies_wrong_element_kind);
eval_fail_test!(fail_addErrorContext_example);
eval_fail_test!(fail_assert);
eval_fail_test!(fail_assert_equal_attrs_names);
eval_fail_test!(fail_assert_equal_attrs_names_2);
eval_fail_test!(fail_assert_equal_derivations);
eval_fail_test!(fail_assert_equal_derivations_extra);
eval_fail_test!(fail_assert_equal_floats);
eval_fail_test!(fail_assert_equal_function_direct);
eval_fail_test!(fail_assert_equal_int_float);
eval_fail_test!(fail_assert_equal_ints);
eval_fail_test!(fail_assert_equal_list_length);
eval_fail_test!(fail_assert_equal_paths);
eval_fail_test!(fail_assert_equal_type);
eval_fail_test!(fail_assert_equal_type_nested);
eval_fail_test!(fail_assert_nested_bool);
eval_fail_test!(fail_attr_name_type);
eval_fail_test!(fail_attrset_merge_drops_later_rec);
eval_fail_test!(fail_bad_string_interpolation_1);
eval_fail_test!(fail_bad_string_interpolation_2);
eval_fail_test!(fail_bad_string_interpolation_3);
eval_fail_test!(fail_bad_string_interpolation_4);
eval_fail_test!(fail_blackhole);
eval_fail_test!(fail_call_primop);
eval_fail_test!(fail_deepseq);
eval_fail_test!(fail_derivation_name);
eval_fail_test!(fail_dup_dynamic_attrs);
eval_fail_test!(fail_duplicate_traces);
eval_fail_test!(fail_eol_1);
eval_fail_test!(fail_eol_2);
eval_fail_test!(fail_eol_3);
eval_fail_test!(fail_fetchTree_negative);
eval_fail_test!(fail_fetchurl_baseName);
eval_fail_test!(fail_fetchurl_baseName_attrs);
eval_fail_test!(fail_fetchurl_baseName_attrs_name);
eval_fail_test!(fail_flake_ref_to_string_negative_integer);
eval_fail_test!(fail_foldlStrict_strict_op_application);
eval_fail_test!(fail_fromJSON_keyWithNullByte);
eval_fail_test!(fail_fromJSON_overflowing);
eval_fail_test!(fail_fromJSON_valueWithNullByte);
eval_fail_test!(fail_fromTOML_keyWithNullByte);
eval_fail_test!(fail_fromTOML_timestamps);
eval_fail_test!(fail_fromTOML_valueWithNullByte);
eval_fail_test!(fail_hashfile_missing);
eval_fail_test!(fail_infinite_recursion_lambda);
eval_fail_test!(fail_list);
eval_fail_test!(fail_missing_arg);
eval_fail_test!(fail_mutual_recursion);
eval_fail_test!(fail_nested_list_items);
eval_fail_test!(fail_nonexist_path);
eval_fail_test!(fail_not_throws);
eval_fail_test!(fail_overflowing_add);
eval_fail_test!(fail_overflowing_div);
eval_fail_test!(fail_overflowing_mul);
eval_fail_test!(fail_overflowing_sub);
eval_fail_test!(fail_path_slash);
eval_fail_test!(fail_pipe_operators);
eval_fail_test!(fail_recursion);
eval_fail_test!(fail_remove);
eval_fail_test!(fail_scope_5);
eval_fail_test!(fail_seq);
eval_fail_test!(fail_set);
eval_fail_test!(fail_set_override);
eval_fail_test!(fail_string_nul_1);
eval_fail_test!(fail_string_nul_2);
eval_fail_test!(fail_substring);
eval_fail_test!(fail_toJSON);
eval_fail_test!(fail_toJSON_non_utf_8);
eval_fail_test!(fail_to_path);
eval_fail_test!(fail_undeclared_arg);
eval_fail_test!(fail_using_set_as_attr_name);
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
foo
+1
View File
@@ -0,0 +1 @@
"a"
+1
View File
@@ -0,0 +1 @@
"X"
+1
View File
@@ -0,0 +1 @@
"b"
+1
View File
@@ -0,0 +1 @@
"X"
+1
View File
@@ -0,0 +1 @@
"X"
+1
View File
@@ -0,0 +1 @@
"c"
+1
View File
@@ -0,0 +1 @@
"X"
+1
View File
@@ -0,0 +1 @@
"X"
@@ -0,0 +1,8 @@
error:
… while calling the 'abort' builtin
at /pwd/lang/eval-fail-abort.nix:1:14:
1| if true then abort "this should fail" else 1
| ^
2|
error: evaluation aborted with the following error message: 'this should fail'
+1
View File
@@ -0,0 +1 @@
if true then abort "this should fail" else 1
@@ -0,0 +1,8 @@
error:
… while calling the 'addDrvOutputDependencies' builtin
at /pwd/lang/eval-fail-addDrvOutputDependencies-empty-context.nix:1:1:
1| builtins.addDrvOutputDependencies ""
| ^
2|
error: context of string '' must have exactly one element, but has 0
@@ -0,0 +1 @@
builtins.addDrvOutputDependencies ""
@@ -0,0 +1,9 @@
error:
… while calling the 'addDrvOutputDependencies' builtin
at /pwd/lang/eval-fail-addDrvOutputDependencies-multi-elem-context.nix:25:1:
24| in
25| builtins.addDrvOutputDependencies combo-path
| ^
26|
error: context of string '/nix/store/pg9yqs4yd85yhdm3f4i5dyaqp5jahrsz-fail.drv/nix/store/2dxd5frb715z451vbf7s8birlf3argbk-fail-2.drv' must have exactly one element, but has 2
@@ -0,0 +1,25 @@
let
drv0 = derivation {
name = "fail";
builder = "/bin/false";
system = "x86_64-linux";
outputs = [
"out"
"foo"
];
};
drv1 = derivation {
name = "fail-2";
builder = "/bin/false";
system = "x86_64-linux";
outputs = [
"out"
"foo"
];
};
combo-path = "${drv0.drvPath}${drv1.drvPath}";
in
builtins.addDrvOutputDependencies combo-path
@@ -0,0 +1,9 @@
error:
… while calling the 'addDrvOutputDependencies' builtin
at /pwd/lang/eval-fail-addDrvOutputDependencies-wrong-element-kind.nix:13:1:
12| in
13| builtins.addDrvOutputDependencies drv.outPath
| ^
14|
error: `addDrvOutputDependencies` can only act on derivations, not on a derivation output such as 'out'
@@ -0,0 +1,13 @@
let
drv = derivation {
name = "fail";
builder = "/bin/false";
system = "x86_64-linux";
outputs = [
"out"
"foo"
];
};
in
builtins.addDrvOutputDependencies drv.outPath
@@ -0,0 +1,24 @@
error:
… while counting down; n = 10
… while counting down; n = 9
… while counting down; n = 8
… while counting down; n = 7
… while counting down; n = 6
… while counting down; n = 5
… while counting down; n = 4
… while counting down; n = 3
… while counting down; n = 2
… while counting down; n = 1
(stack trace truncated; use '--show-trace' to show the full, detailed trace)
error: kaboom
@@ -0,0 +1,9 @@
let
countDown =
n:
if n == 0 then
throw "kaboom"
else
builtins.addErrorContext "while counting down; n = ${toString n}" ("x" + countDown (n - 1));
in
countDown 10
@@ -0,0 +1,8 @@
error:
… while evaluating the condition of the assertion '({ a = true; } == { a = true; b = true; })'
at /pwd/lang/eval-fail-assert-equal-attrs-names-2.nix:1:1:
1| assert
| ^
2| {
error: attribute names of attribute set '{ a = true; }' differs from attribute set '{ a = true; b = true; }'
@@ -0,0 +1,8 @@
assert
{
a = true;
} == {
a = true;
b = true;
};
throw "unreachable"
@@ -0,0 +1,8 @@
error:
… while evaluating the condition of the assertion '({ a = true; b = true; } == { a = true; })'
at /pwd/lang/eval-fail-assert-equal-attrs-names.nix:1:1:
1| assert
| ^
2| {
error: attribute names of attribute set '{ a = true; b = true; }' differs from attribute set '{ a = true; }'
@@ -0,0 +1,8 @@
assert
{
a = true;
b = true;
} == {
a = true;
};
throw "unreachable"
@@ -0,0 +1,26 @@
error:
… while evaluating the condition of the assertion '({ foo = { outPath = "/nix/store/0"; type = "derivation"; }; } == { foo = { devious = true; outPath = "/nix/store/1"; type = "derivation"; }; })'
at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:1:1:
1| assert
| ^
2| {
… while comparing attribute 'foo'
… where left hand side is
at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:3:5:
2| {
3| foo = {
| ^
4| type = "derivation";
… where right hand side is
at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:8:5:
7| } == {
8| foo = {
| ^
9| type = "derivation";
… while comparing a derivation by its 'outPath' attribute
error: string '"/nix/store/0"' is not equal to string '"/nix/store/1"'
@@ -0,0 +1,14 @@
assert
{
foo = {
type = "derivation";
outPath = "/nix/store/0";
};
} == {
foo = {
type = "derivation";
outPath = "/nix/store/1";
devious = true;
};
};
throw "unreachable"
@@ -0,0 +1,26 @@
error:
… while evaluating the condition of the assertion '({ foo = { ignored = (abort "not ignored"); outPath = "/nix/store/0"; type = "derivation"; }; } == { foo = { ignored = (abort "not ignored"); outPath = "/nix/store/1"; type = "derivation"; }; })'
at /pwd/lang/eval-fail-assert-equal-derivations.nix:1:1:
1| assert
| ^
2| {
… while comparing attribute 'foo'
… where left hand side is
at /pwd/lang/eval-fail-assert-equal-derivations.nix:3:5:
2| {
3| foo = {
| ^
4| type = "derivation";
… where right hand side is
at /pwd/lang/eval-fail-assert-equal-derivations.nix:9:5:
8| } == {
9| foo = {
| ^
10| type = "derivation";
… while comparing a derivation by its 'outPath' attribute
error: string '"/nix/store/0"' is not equal to string '"/nix/store/1"'
@@ -0,0 +1,15 @@
assert
{
foo = {
type = "derivation";
outPath = "/nix/store/0";
ignored = abort "not ignored";
};
} == {
foo = {
type = "derivation";
outPath = "/nix/store/1";
ignored = abort "not ignored";
};
};
throw "unreachable"
@@ -0,0 +1,22 @@
error:
… while evaluating the condition of the assertion '({ b = 1; } == { b = 1.01; })'
at /pwd/lang/eval-fail-assert-equal-floats.nix:1:1:
1| assert { b = 1.0; } == { b = 1.01; };
| ^
2| abort "unreachable"
… while comparing attribute 'b'
… where left hand side is
at /pwd/lang/eval-fail-assert-equal-floats.nix:1:10:
1| assert { b = 1.0; } == { b = 1.01; };
| ^
2| abort "unreachable"
… where right hand side is
at /pwd/lang/eval-fail-assert-equal-floats.nix:1:26:
1| assert { b = 1.0; } == { b = 1.01; };
| ^
2| abort "unreachable"
error: a float with value '1' is not equal to a float with value '1.01'
@@ -0,0 +1,2 @@
assert { b = 1.0; } == { b = 1.01; };
abort "unreachable"
@@ -0,0 +1,9 @@
error:
… while evaluating the condition of the assertion '((x: x) == (x: x))'
at /pwd/lang/eval-fail-assert-equal-function-direct.nix:3:1:
2| # This only compares a direct comparison and makes no claims about functions in nested structures.
3| assert (x: x) == (x: x);
| ^
4| abort "unreachable"
error: distinct functions and immediate comparisons of identical functions compare as unequal
@@ -0,0 +1,4 @@
# Note: functions in nested structures, e.g. attributes, may be optimized away by pointer identity optimization.
# This only compares a direct comparison and makes no claims about functions in nested structures.
assert (x: x) == (x: x);
abort "unreachable"
@@ -0,0 +1,8 @@
error:
… while evaluating the condition of the assertion '(1 == 1.1)'
at /pwd/lang/eval-fail-assert-equal-int-float.nix:1:1:
1| assert 1 == 1.1;
| ^
2| throw "unreachable"
error: an integer with value '1' is not equal to a float with value '1.1'
@@ -0,0 +1,2 @@
assert 1 == 1.1;
throw "unreachable"
@@ -0,0 +1,22 @@
error:
… while evaluating the condition of the assertion '({ b = 1; } == { b = 2; })'
at /pwd/lang/eval-fail-assert-equal-ints.nix:1:1:
1| assert { b = 1; } == { b = 2; };
| ^
2| abort "unreachable"
… while comparing attribute 'b'
… where left hand side is
at /pwd/lang/eval-fail-assert-equal-ints.nix:1:10:
1| assert { b = 1; } == { b = 2; };
| ^
2| abort "unreachable"
… where right hand side is
at /pwd/lang/eval-fail-assert-equal-ints.nix:1:24:
1| assert { b = 1; } == { b = 2; };
| ^
2| abort "unreachable"
error: an integer with value '1' is not equal to an integer with value '2'
@@ -0,0 +1,2 @@
assert { b = 1; } == { b = 2; };
abort "unreachable"
@@ -0,0 +1,8 @@
error:
… while evaluating the condition of the assertion '([ (1) (0) ] == [ (10) ])'
at /pwd/lang/eval-fail-assert-equal-list-length.nix:1:1:
1| assert
| ^
2| [
error: list of size '2' is not equal to list of size '1', left hand side is '[ 1 0 ]', right hand side is '[ 10 ]'
@@ -0,0 +1,6 @@
assert
[
1
0
] == [ 10 ];
throw "unreachable"
@@ -0,0 +1,8 @@
error:
… while evaluating the condition of the assertion '(/pwd/lang/foo == /pwd/lang/bar)'
at /pwd/lang/eval-fail-assert-equal-paths.nix:1:1:
1| assert ./foo == ./bar;
| ^
2| throw "unreachable"
error: path '/pwd/lang/foo' is not equal to path '/pwd/lang/bar'
@@ -0,0 +1,2 @@
assert ./foo == ./bar;
throw "unreachable"
@@ -0,0 +1,22 @@
error:
… while evaluating the condition of the assertion '({ ding = false; } == { ding = null; })'
at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:1:
1| assert { ding = false; } == { ding = null; };
| ^
2| abort "unreachable"
… while comparing attribute 'ding'
… where left hand side is
at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:10:
1| assert { ding = false; } == { ding = null; };
| ^
2| abort "unreachable"
… where right hand side is
at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:31:
1| assert { ding = false; } == { ding = null; };
| ^
2| abort "unreachable"
error: a Boolean of value 'false' is not equal to null of value 'null'
@@ -0,0 +1,2 @@
assert { ding = false; } == { ding = null; };
abort "unreachable"
@@ -0,0 +1,8 @@
error:
… while evaluating the condition of the assertion '(false == null)'
at /pwd/lang/eval-fail-assert-equal-type.nix:1:1:
1| assert false == null;
| ^
2| abort "unreachable"
error: a Boolean of value 'false' is not equal to null of value 'null'
@@ -0,0 +1,2 @@
assert false == null;
abort "unreachable"
@@ -0,0 +1,66 @@
error:
… while evaluating the condition of the assertion '({ a = { b = [ ({ c = { d = true; }; }) ]; }; } == { a = { b = [ ({ c = { d = false; }; }) ]; }; })'
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:1:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… while comparing attribute 'a'
… where left hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:10:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… where right hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:44:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… while comparing attribute 'b'
… where left hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:10:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… where right hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:44:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… while comparing list element 0
… while comparing attribute 'c'
… where left hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:20:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… where right hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:54:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… while comparing attribute 'd'
… where left hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:20:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
… where right hand side is
at /pwd/lang/eval-fail-assert-nested-bool.nix:1:54:
1| assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
| ^
2|
error: boolean 'true' is not equal to boolean 'false'
@@ -0,0 +1,3 @@
assert { a.b = [ { c.d = true; } ]; } == { a.b = [ { c.d = false; } ]; };
abort "unreachable"
@@ -0,0 +1,30 @@
error:
… while evaluating the attribute 'body'
at /pwd/lang/eval-fail-assert.nix:7:3:
6|
7| body = x "x";
| ^
8| }
… from call site
at /pwd/lang/eval-fail-assert.nix:7:10:
6|
7| body = x "x";
| ^
8| }
… while calling 'x'
at /pwd/lang/eval-fail-assert.nix:3:5:
2| x =
3| arg:
| ^
4| assert arg == "y";
… while evaluating the condition of the assertion '(arg == "y")'
at /pwd/lang/eval-fail-assert.nix:4:5:
3| arg:
4| assert arg == "y";
| ^
5| 123;
error: string '"x"' is not equal to string '"y"'
@@ -0,0 +1,8 @@
let {
x =
arg:
assert arg == "y";
123;
body = x "x";
}
@@ -0,0 +1,21 @@
error:
… while evaluating the attribute 'puppy."${key}"'
at /pwd/lang/eval-fail-attr-name-type.nix:3:5:
2| attrs = {
3| puppy.doggy = { };
| ^
4| };
… while evaluating an attribute name
at /pwd/lang/eval-fail-attr-name-type.nix:7:15:
6| in
7| attrs.puppy.${key}
| ^
8|
error: expected a string but found an integer: 1
at /pwd/lang/eval-fail-attr-name-type.nix:7:15:
6| in
7| attrs.puppy.${key}
| ^
8|
@@ -0,0 +1,7 @@
let
attrs = {
puppy.doggy = { };
};
key = 1;
in
attrs.puppy.${key}
@@ -0,0 +1,6 @@
error: undefined variable 'd'
at /pwd/lang/eval-fail-attrset-merge-drops-later-rec.nix:4:9:
3| a = rec {
4| c = d + 2;
| ^
5| d = 3;
@@ -0,0 +1,8 @@
{
a.b = 1;
a = rec {
c = d + 2;
d = 3;
};
}
.c
@@ -0,0 +1,8 @@
error:
… while evaluating a path segment
at /pwd/lang/eval-fail-bad-string-interpolation-1.nix:1:2:
1| "${x: x}"
| ^
2|
error: cannot coerce a function to a string: «lambda @ /pwd/lang/eval-fail-bad-string-interpolation-1.nix:1:4»
@@ -0,0 +1 @@
"${x: x}"
@@ -0,0 +1 @@
error: path '/pwd/lang/fnord' does not exist
@@ -0,0 +1 @@
"${./fnord}"
@@ -0,0 +1,8 @@
error:
… while evaluating a path segment
at /pwd/lang/eval-fail-bad-string-interpolation-3.nix:1:3:
1| ''${x: x}''
| ^
2|
error: cannot coerce a function to a string: «lambda @ /pwd/lang/eval-fail-bad-string-interpolation-3.nix:1:5»
@@ -0,0 +1 @@
''${x: x}''
@@ -0,0 +1,9 @@
error:
… while evaluating a path segment
at /pwd/lang/eval-fail-bad-string-interpolation-4.nix:19:3:
18| # The error message should not be too long.
19| ''${pkgs}''
| ^
20|
error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «8 attributes elided» }; «8 attributes elided» }; «8 attributes elided» }
@@ -0,0 +1,19 @@
let
# Basically a "billion laughs" attack, but toned down to simulated `pkgs`.
ha = x: y: {
a = x y;
b = x y;
c = x y;
d = x y;
e = x y;
f = x y;
g = x y;
h = x y;
j = x y;
};
has = ha (ha (ha (ha (x: x)))) "ha";
# A large structure that has already been evaluated.
pkgs = builtins.deepSeq has has;
in
# The error message should not be too long.
''${pkgs}''
@@ -0,0 +1,14 @@
error:
… while evaluating the attribute 'body'
at /pwd/lang/eval-fail-blackhole.nix:2:3:
1| let {
2| body = x;
| ^
3| x = y;
error: infinite recursion encountered
at /pwd/lang/eval-fail-blackhole.nix:3:7:
2| body = x;
3| x = y;
| ^
4| y = x;
@@ -0,0 +1,5 @@
let {
body = x;
x = y;
y = x;
}
@@ -0,0 +1,10 @@
error:
… while calling the 'length' builtin
at /pwd/lang/eval-fail-call-primop.nix:1:1:
1| builtins.length 1
| ^
2|
… while evaluating the first argument passed to builtins.length
error: expected a list but found an integer: 1
@@ -0,0 +1 @@
builtins.length 1
@@ -0,0 +1,20 @@
error:
… while calling the 'deepSeq' builtin
at /pwd/lang/eval-fail-deepseq.nix:1:1:
1| builtins.deepSeq { x = abort "foo"; } 456
| ^
2|
… while evaluating the attribute 'x'
at /pwd/lang/eval-fail-deepseq.nix:1:20:
1| builtins.deepSeq { x = abort "foo"; } 456
| ^
2|
… while calling the 'abort' builtin
at /pwd/lang/eval-fail-deepseq.nix:1:24:
1| builtins.deepSeq { x = abort "foo"; } 456
| ^
2|
error: evaluation aborted with the following error message: 'foo'
@@ -0,0 +1 @@
builtins.deepSeq { x = abort "foo"; } 456
@@ -0,0 +1,26 @@
error:
… while evaluating the attribute 'outPath'
at <nix/derivation-internal.nix>:<number>:<number>:
<number>| value = commonAttrs // {
<number>| outPath = builtins.getAttr outputName strict;
| ^
<number>| drvPath = strict.drvPath;
… while calling the 'getAttr' builtin
at <nix/derivation-internal.nix>:<number>:<number>:
<number>| value = commonAttrs // {
<number>| outPath = builtins.getAttr outputName strict;
| ^
<number>| drvPath = strict.drvPath;
… while calling the 'derivationStrict' builtin
at <nix/derivation-internal.nix>:<number>:<number>:
<number>|
<number>| strict = derivationStrict drvAttrs;
| ^
<number>|
… while evaluating derivation '~jiggle~'
whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:<number>:<number>
error: invalid derivation name: name '~jiggle~' contains illegal character '~'. Please pass a different 'name'.
@@ -0,0 +1,5 @@
derivation {
name = "~jiggle~";
system = "some-system";
builder = "/dontcare";
}
@@ -0,0 +1,14 @@
error:
… while evaluating the attribute 'set'
at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:3:
1| {
2| set = {
| ^
3| "${"" + "b"}" = 1;
error: dynamic attribute 'b' already defined at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:3:5
at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:6:5:
5| set = {
6| "${"b" + ""}" = 2;
| ^
7| };
@@ -0,0 +1,8 @@
{
set = {
"${"" + "b"}" = 1;
};
set = {
"${"b" + ""}" = 2;
};
}
@@ -0,0 +1,51 @@
error:
… from call site
at /pwd/lang/eval-fail-duplicate-traces.nix:6:1:
5| in
6| throwAfter 2
| ^
7|
… while calling 'throwAfter'
at /pwd/lang/eval-fail-duplicate-traces.nix:4:16:
3| let
4| throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
| ^
5| in
… from call site
at /pwd/lang/eval-fail-duplicate-traces.nix:4:33:
3| let
4| throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
| ^
5| in
… while calling 'throwAfter'
at /pwd/lang/eval-fail-duplicate-traces.nix:4:16:
3| let
4| throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
| ^
5| in
… from call site
at /pwd/lang/eval-fail-duplicate-traces.nix:4:33:
3| let
4| throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
| ^
5| in
… while calling 'throwAfter'
at /pwd/lang/eval-fail-duplicate-traces.nix:4:16:
3| let
4| throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
| ^
5| in
… while calling the 'throw' builtin
at /pwd/lang/eval-fail-duplicate-traces.nix:4:57:
3| let
4| throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
| ^
5| in
error: Uh oh!
@@ -0,0 +1,6 @@
# Check that we only omit duplicate stack traces when there's a bunch of them.
# Here, there's only a couple duplicate entries, so we output them all.
let
throwAfter = n: if n > 0 then throwAfter (n - 1) else throw "Uh oh!";
in
throwAfter 2
@@ -0,0 +1,6 @@
error: undefined variable 'invalid'
at /pwd/lang/eval-fail-eol-1.nix:2:1:
1| # foo
2| invalid
| ^
3| # bar
+3
View File
@@ -0,0 +1,3 @@
# foo
invalid
# bar
@@ -0,0 +1,6 @@
error: undefined variable 'invalid'
at /pwd/lang/eval-fail-eol-2.nix:2:1:
1| # foo
2| invalid
| ^
3| # bar
+2
View File
@@ -0,0 +1,2 @@
# foo␍invalid
# bar
@@ -0,0 +1,6 @@
error: undefined variable 'invalid'
at /pwd/lang/eval-fail-eol-3.nix:2:1:
1| # foo
2| invalid
| ^
3| # bar
+3
View File
@@ -0,0 +1,3 @@
# foo
invalid
# bar
@@ -0,0 +1,8 @@
error:
… while calling the 'fetchTree' builtin
at /pwd/lang/eval-fail-fetchTree-negative.nix:1:1:
1| builtins.fetchTree {
| ^
2| type = "file";
error: negative value given for 'fetchTree' argument 'owner': -1
@@ -0,0 +1,5 @@
builtins.fetchTree {
type = "file";
url = "file://eval-fail-fetchTree-negative.nix";
owner = -1;
}
@@ -0,0 +1,8 @@
error:
… while calling the 'fetchurl' builtin
at /pwd/lang/eval-fail-fetchurl-baseName-attrs-name.nix:1:1:
1| builtins.fetchurl {
| ^
2| url = "https://example.com/foo.tar.gz";
error: invalid store path name when fetching URL 'https://example.com/foo.tar.gz': name '~wobble~' contains illegal character '~'. Please change the value for the 'name' attribute passed to 'fetchurl', so that it can create a valid store path.
@@ -0,0 +1,4 @@
builtins.fetchurl {
url = "https://example.com/foo.tar.gz";
name = "~wobble~";
}
@@ -0,0 +1,8 @@
error:
… while calling the 'fetchurl' builtin
at /pwd/lang/eval-fail-fetchurl-baseName-attrs.nix:1:1:
1| builtins.fetchurl { url = "https://example.com/~wiggle~"; }
| ^
2|
error: invalid store path name when fetching URL 'https://example.com/~wiggle~': name '~wiggle~' contains illegal character '~'. Please add a valid 'name' attribute to the argument for 'fetchurl', so that it can create a valid store path.
@@ -0,0 +1 @@
builtins.fetchurl { url = "https://example.com/~wiggle~"; }
@@ -0,0 +1,8 @@
error:
… while calling the 'fetchurl' builtin
at /pwd/lang/eval-fail-fetchurl-baseName.nix:1:1:
1| builtins.fetchurl "https://example.com/~wiggle~"
| ^
2|
error: invalid store path name when fetching URL 'https://example.com/~wiggle~': name '~wiggle~' contains illegal character '~'. Please pass an attribute set with 'url' and 'name' attributes to 'fetchurl', so that it can create a valid store path.
@@ -0,0 +1 @@
builtins.fetchurl "https://example.com/~wiggle~"
@@ -0,0 +1,16 @@
error:
… while calling the 'seq' builtin
at /pwd/lang/eval-fail-flake-ref-to-string-negative-integer.nix:4:1:
3| in
4| builtins.seq n (
| ^
5| builtins.flakeRefToString {
… while calling the 'flakeRefToString' builtin
at /pwd/lang/eval-fail-flake-ref-to-string-negative-integer.nix:5:3:
4| builtins.seq n (
5| builtins.flakeRefToString {
| ^
6| type = "github";
error: negative value given for flake ref attr repo: -1
@@ -0,0 +1,12 @@
let
n = -1;
in
builtins.seq n (
builtins.flakeRefToString {
type = "github";
owner = "NixOS";
repo = n;
ref = "23.05";
dir = "lib";
}
)
@@ -0,0 +1,37 @@
error:
… while calling the 'foldl'' builtin
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:2:1:
1| # Tests that the result of applying op is forced even if the value is never used
2| builtins.foldl' (_: f: f null) null [
| ^
3| (_: throw "Not the final value, but is still forced!")
… while calling anonymous lambda
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:2:21:
1| # Tests that the result of applying op is forced even if the value is never used
2| builtins.foldl' (_: f: f null) null [
| ^
3| (_: throw "Not the final value, but is still forced!")
… from call site
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:2:24:
1| # Tests that the result of applying op is forced even if the value is never used
2| builtins.foldl' (_: f: f null) null [
| ^
3| (_: throw "Not the final value, but is still forced!")
… while calling anonymous lambda
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:4:
2| builtins.foldl' (_: f: f null) null [
3| (_: throw "Not the final value, but is still forced!")
| ^
4| (_: 23)
… while calling the 'throw' builtin
at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:7:
2| builtins.foldl' (_: f: f null) null [
3| (_: throw "Not the final value, but is still forced!")
| ^
4| (_: 23)
error: Not the final value, but is still forced!
@@ -0,0 +1,5 @@
# Tests that the result of applying op is forced even if the value is never used
builtins.foldl' (_: f: f null) null [
(_: throw "Not the final value, but is still forced!")
(_: 23)
]
@@ -0,0 +1,8 @@
error:
… while calling the 'fromJSON' builtin
at /pwd/lang/eval-fail-fromJSON-keyWithNullByte.nix:1:1:
1| builtins.fromJSON ''{"a\u0000b": 1}''
| ^
2|
error: input string 'a␀b' cannot be represented as Nix string because it contains null bytes
Loaded 100 of 511 files, more files were not shown because too many files have changed in this diff. Show more