remove redundant tests
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
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());
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
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()))
|
||||
);
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
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"),
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
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()));
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
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)
|
||||
);
|
||||
}
|
||||
@@ -366,3 +366,193 @@ fn read_dir_on_file_fails() {
|
||||
let err_msg = result.unwrap_err().to_string();
|
||||
assert!(err_msg.contains("not a directory"));
|
||||
}
|
||||
|
||||
#[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"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
mod basic_eval;
|
||||
mod builtins;
|
||||
mod builtins_store;
|
||||
mod derivation;
|
||||
mod findfile;
|
||||
mod free_globals;
|
||||
mod functions;
|
||||
mod io_operations;
|
||||
mod lang;
|
||||
mod numeric_types;
|
||||
mod operators;
|
||||
mod path_operations;
|
||||
mod regex;
|
||||
mod string_context;
|
||||
mod thunk_scope;
|
||||
mod to_string;
|
||||
mod utils;
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
use fix::value::Value;
|
||||
|
||||
use crate::utils::eval;
|
||||
|
||||
#[test_log::test]
|
||||
fn large_i64_max() {
|
||||
assert_eq!(eval("9223372036854775807"), Value::Int(9223372036854775807));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn large_i64_negative() {
|
||||
assert_eq!(
|
||||
eval("-9223372036854775807"),
|
||||
Value::Int(-9223372036854775807)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn large_number_arithmetic() {
|
||||
assert_eq!(
|
||||
eval("5000000000000000000 + 3000000000000000000"),
|
||||
Value::Int(8000000000000000000i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_int_with_int() {
|
||||
assert_eq!(eval("builtins.isInt 42"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_int_with_float() {
|
||||
assert_eq!(eval("builtins.isInt 42.0"), Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_float_with_int() {
|
||||
assert_eq!(eval("builtins.isFloat 42"), Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_float_with_float() {
|
||||
assert_eq!(eval("builtins.isFloat 42.5"), Value::Bool(true));
|
||||
assert_eq!(eval("builtins.isFloat 1.0"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn typeof_int() {
|
||||
assert_eq!(eval("builtins.typeOf 1"), Value::String("int".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn typeof_float() {
|
||||
assert_eq!(
|
||||
eval("builtins.typeOf 1.0"),
|
||||
Value::String("float".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
eval("builtins.typeOf 3.14"),
|
||||
Value::String("float".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn int_literal() {
|
||||
assert_eq!(eval("1"), Value::Int(1));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn float_literal() {
|
||||
assert_eq!(eval("1."), Value::Float(1.));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn int_plus_int() {
|
||||
assert_eq!(
|
||||
eval("builtins.typeOf (1 + 2)"),
|
||||
Value::String("int".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn int_plus_float() {
|
||||
assert_eq!(
|
||||
eval("builtins.typeOf (1 + 2.0)"),
|
||||
Value::String("float".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn int_times_int() {
|
||||
assert_eq!(
|
||||
eval("builtins.typeOf (3 * 4)"),
|
||||
Value::String("int".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn int_times_float() {
|
||||
assert_eq!(
|
||||
eval("builtins.typeOf (3 * 4.0)"),
|
||||
Value::String("float".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn integer_division() {
|
||||
assert_eq!(eval("5 / 2"), Value::Int(2));
|
||||
assert_eq!(eval("7 / 3"), Value::Int(2));
|
||||
assert_eq!(eval("10 / 3"), Value::Int(3));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn float_division() {
|
||||
assert_eq!(eval("5 / 2.0"), Value::Float(2.5));
|
||||
assert_eq!(eval("7.0 / 2"), Value::Float(3.5));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn negative_integer_division() {
|
||||
assert_eq!(eval("(-7) / 3"), Value::Int(-2));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn builtin_add_with_large_numbers() {
|
||||
assert_eq!(
|
||||
eval("builtins.add 5000000000000000000 3000000000000000000"),
|
||||
Value::Int(8000000000000000000i64)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn builtin_mul_with_large_numbers() {
|
||||
assert_eq!(
|
||||
eval("builtins.mul 1000000000 1000000000"),
|
||||
Value::Int(1000000000000000000i64)
|
||||
);
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use fix::value::{AttrSet, List, Symbol, Value};
|
||||
|
||||
use crate::utils::eval;
|
||||
|
||||
#[test_log::test]
|
||||
fn addition() {
|
||||
assert_eq!(eval("1 + 1"), Value::Int(2));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn subtraction() {
|
||||
assert_eq!(eval("2 - 1"), Value::Int(1));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn multiplication() {
|
||||
assert_eq!(eval("1. * 1"), Value::Float(1.));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn division() {
|
||||
assert_eq!(eval("1 / 1."), Value::Float(1.));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn equality() {
|
||||
assert_eq!(eval("1 == 1"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn inequality() {
|
||||
assert_eq!(eval("1 != 1"), Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn less_than() {
|
||||
assert_eq!(eval("2 < 1"), Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn greater_than() {
|
||||
assert_eq!(eval("2 > 1"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn less_than_or_equal() {
|
||||
assert_eq!(eval("1 <= 1"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn greater_than_or_equal() {
|
||||
assert_eq!(eval("1 >= 1"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn logical_or_short_circuit() {
|
||||
assert_eq!(eval("true || (1 / 0)"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn logical_and() {
|
||||
assert_eq!(eval("true && 1 == 0"), Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn list_concatenation() {
|
||||
assert_eq!(
|
||||
eval("[ 1 2 3 ] ++ [ 4 5 6 ]"),
|
||||
Value::List(List::new((1..=6).map(Value::Int).collect()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn attrset_update() {
|
||||
assert_eq!(
|
||||
eval("{ a.b = 1; b = 2; } // { a.c = 2; }"),
|
||||
Value::AttrSet(AttrSet::new(BTreeMap::from([
|
||||
(
|
||||
Symbol::from("a"),
|
||||
Value::AttrSet(AttrSet::new(BTreeMap::from([(
|
||||
Symbol::from("c"),
|
||||
Value::Int(2),
|
||||
)]))),
|
||||
),
|
||||
(Symbol::from("b"), Value::Int(2)),
|
||||
])))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn unary_negation() {
|
||||
assert_eq!(eval("-5"), Value::Int(-5));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn logical_not() {
|
||||
assert_eq!(eval("!true"), Value::Bool(false));
|
||||
assert_eq!(eval("!false"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn select_with_default_lazy_evaluation() {
|
||||
assert_eq!(eval("{ a = 1; }.a or (1 / 0)"), Value::Int(1));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn select_with_default_nested_lazy() {
|
||||
assert_eq!(
|
||||
eval("{ a.b = 42; }.a.b or (builtins.abort \"should not evaluate\")"),
|
||||
Value::Int(42)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn select_with_default_fallback() {
|
||||
assert_eq!(eval("{ a = 1; }.b or 999"), Value::Int(999));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn implication_false_false() {
|
||||
assert_eq!(eval("false -> false"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn implication_false_true() {
|
||||
assert_eq!(eval("false -> true"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn implication_true_false() {
|
||||
assert_eq!(eval("true -> false"), Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn implication_true_true() {
|
||||
assert_eq!(eval("true -> true"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn implication_short_circuit() {
|
||||
assert_eq!(eval("false -> (1 / 0)"), Value::Bool(true));
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
use fix::value::Value;
|
||||
|
||||
use crate::utils::{eval, eval_result};
|
||||
|
||||
#[test_log::test]
|
||||
fn path_type_of() {
|
||||
let result = eval("builtins.typeOf ./foo");
|
||||
assert_eq!(result, Value::String("path".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_path_true() {
|
||||
let result = eval("builtins.isPath ./foo");
|
||||
assert_eq!(result, Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_path_false_string() {
|
||||
let result = eval(r#"builtins.isPath "./foo""#);
|
||||
assert_eq!(result, Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn is_path_false_number() {
|
||||
let result = eval("builtins.isPath 42");
|
||||
assert_eq!(result, Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn path_concat_type() {
|
||||
// path + string = path
|
||||
let result = eval(r#"builtins.typeOf (./foo + "/bar")"#);
|
||||
assert_eq!(result, Value::String("path".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn string_path_concat_type() {
|
||||
// string + path = string
|
||||
let result = eval(r#"builtins.typeOf ("prefix-" + ./foo)"#);
|
||||
assert_eq!(result, Value::String("string".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn basename_of_path() {
|
||||
let result = eval("builtins.baseNameOf ./path/to/file.nix");
|
||||
assert!(matches!(result, Value::String(s) if s == "file.nix"));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn basename_of_string() {
|
||||
let result = eval(r#"builtins.baseNameOf "/path/to/file.nix""#);
|
||||
assert_eq!(result, Value::String("file.nix".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn dir_of_path_type() {
|
||||
// dirOf preserves path type
|
||||
let result = eval("builtins.typeOf (builtins.dirOf ./path/to/file.nix)");
|
||||
assert_eq!(result, Value::String("path".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn dir_of_string_type() {
|
||||
// dirOf preserves string type
|
||||
let result = eval(r#"builtins.typeOf (builtins.dirOf "/path/to/file.nix")"#);
|
||||
assert_eq!(result, Value::String("string".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn path_equality() {
|
||||
// Same path should be equal
|
||||
let result = eval("./foo == ./foo");
|
||||
assert_eq!(result, Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn path_not_equal_string() {
|
||||
// Paths and strings are different types - should not be equal
|
||||
let result = eval(r#"./foo == "./foo""#);
|
||||
assert_eq!(result, Value::Bool(false));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_path_absolute() {
|
||||
// toPath with absolute path returns string
|
||||
let result = eval(r#"builtins.toPath "/foo/bar""#);
|
||||
assert_eq!(result, Value::String("/foo/bar".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_path_type_is_string() {
|
||||
// toPath returns a string, not a path
|
||||
let result = eval(r#"builtins.typeOf (builtins.toPath "/foo")"#);
|
||||
assert_eq!(result, Value::String("string".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_path_relative_fails() {
|
||||
// toPath with relative path should fail
|
||||
let result = eval_result(r#"builtins.toPath "foo/bar""#);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_path_empty_fails() {
|
||||
// toPath with empty string should fail
|
||||
let result = eval_result(r#"builtins.toPath """#);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_path_from_path_value() {
|
||||
// toPath can accept a path value too (coerces to string first)
|
||||
let result = eval("builtins.toPath ./foo");
|
||||
// Should succeed and return the absolute path as a string
|
||||
assert!(matches!(result, Value::String(s) if s.starts_with("/")));
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
use fix::value::{List, Value};
|
||||
|
||||
use crate::utils::eval;
|
||||
use crate::utils::eval_result;
|
||||
|
||||
#[test_log::test]
|
||||
fn match_exact_full_string() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "foobar" "foobar""#),
|
||||
Value::List(List::new(vec![]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_partial_returns_null() {
|
||||
assert_eq!(eval(r#"builtins.match "foo" "foobar""#), Value::Null);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_with_capture_groups() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "(.*)\\.nix" "foobar.nix""#),
|
||||
Value::List(List::new(vec![Value::String("foobar".into())]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_multiple_capture_groups() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "((.*)/)?([^/]*)\\.nix" "foobar.nix""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::Null,
|
||||
Value::Null,
|
||||
Value::String("foobar".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_with_path() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "((.*)/)?([^/]*)\\.nix" "/path/to/foobar.nix""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("/path/to/".into()),
|
||||
Value::String("/path/to".into()),
|
||||
Value::String("foobar".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_posix_space_class() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "[[:space:]]+([^[:space:]]+)[[:space:]]+" " foo ""#),
|
||||
Value::List(List::new(vec![Value::String("foo".into())]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_posix_upper_class() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " foo ""#),
|
||||
Value::Null
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO ""#),
|
||||
Value::List(List::new(vec![Value::String("FOO".into())]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn match_quantifiers() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "fo*" "f""#),
|
||||
Value::List(List::new(vec![]))
|
||||
);
|
||||
|
||||
assert_eq!(eval(r#"builtins.match "fo+" "f""#), Value::Null);
|
||||
|
||||
assert_eq!(
|
||||
eval(r#"builtins.match "fo{1,2}" "foo""#),
|
||||
Value::List(List::new(vec![]))
|
||||
);
|
||||
|
||||
assert_eq!(eval(r#"builtins.match "fo{1,2}" "fooo""#), Value::Null);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_non_capturing() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "foobar" "foobar""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("".into()),
|
||||
Value::List(List::new(vec![])),
|
||||
Value::String("".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_no_match() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "fo+" "f""#),
|
||||
Value::List(List::new(vec![Value::String("f".into())]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_with_capture_group() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "(fo*)" "foobar""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("".into()),
|
||||
Value::List(List::new(vec![Value::String("foo".into())])),
|
||||
Value::String("bar".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_multiple_matches() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "(b)" "foobarbaz""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("foo".into()),
|
||||
Value::List(List::new(vec![Value::String("b".into())])),
|
||||
Value::String("ar".into()),
|
||||
Value::List(List::new(vec![Value::String("b".into())])),
|
||||
Value::String("az".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_with_multiple_groups() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "(f)(o*)" "foo""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("".into()),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("f".into()),
|
||||
Value::String("oo".into())
|
||||
])),
|
||||
Value::String("".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_with_optional_groups() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "(a)|(c)" "abc""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("".into()),
|
||||
Value::List(List::new(vec![Value::String("a".into()), Value::Null])),
|
||||
Value::String("b".into()),
|
||||
Value::List(List::new(vec![Value::Null, Value::String("c".into())])),
|
||||
Value::String("".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_greedy_matching() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "(o+)" "oooofoooo""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("".into()),
|
||||
Value::List(List::new(vec![Value::String("oooo".into())])),
|
||||
Value::String("f".into()),
|
||||
Value::List(List::new(vec![Value::String("oooo".into())])),
|
||||
Value::String("".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_posix_classes() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.split "([[:upper:]]+)" " FOO ""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String(" ".into()),
|
||||
Value::List(List::new(vec![Value::String("FOO".into())])),
|
||||
Value::String(" ".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_basic() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings ["o"] ["a"] "foobar""#),
|
||||
Value::String("faabar".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_with_empty() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings ["o"] [""] "foobar""#),
|
||||
Value::String("fbar".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_multiple_patterns() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings ["oo" "a"] ["a" "oo"] "foobar""#),
|
||||
Value::String("faboor".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_first_match_wins() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings ["oo" "oo"] ["u" "i"] "foobar""#),
|
||||
Value::String("fubar".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_empty_pattern() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings [""] ["X"] "abc""#),
|
||||
Value::String("XaXbXcX".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_empty_pattern_empty_string() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings [""] ["X"] """#),
|
||||
Value::String("X".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_simple_char() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings ["-"] ["_"] "a-b""#),
|
||||
Value::String("a_b".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_longer_pattern() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.replaceStrings ["oo"] ["u"] "foobar""#),
|
||||
Value::String("fubar".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn replace_different_lengths() {
|
||||
let result = eval_result(r#"builtins.replaceStrings ["a" "b"] ["x"] "test""#);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_version_simple() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.splitVersion "1.2.3""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("1".into()),
|
||||
Value::String("2".into()),
|
||||
Value::String("3".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_version_with_pre() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.splitVersion "2.3.0pre1234""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("2".into()),
|
||||
Value::String("3".into()),
|
||||
Value::String("0".into()),
|
||||
Value::String("pre".into()),
|
||||
Value::String("1234".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_version_with_letters() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.splitVersion "2.3a""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("2".into()),
|
||||
Value::String("3".into()),
|
||||
Value::String("a".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_version_with_dashes() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.splitVersion "2.3-beta1""#),
|
||||
Value::List(List::new(vec![
|
||||
Value::String("2".into()),
|
||||
Value::String("3".into()),
|
||||
Value::String("beta".into()),
|
||||
Value::String("1".into())
|
||||
]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn split_version_empty() {
|
||||
assert_eq!(
|
||||
eval(r#"builtins.splitVersion """#),
|
||||
Value::List(List::new(vec![]))
|
||||
);
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
use fix::value::Value;
|
||||
|
||||
use crate::utils::eval;
|
||||
|
||||
#[test_log::test]
|
||||
fn non_recursive_bindings() {
|
||||
assert_eq!(eval("let x = 1; y = 2; z = x + y; in z"), Value::Int(3));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn non_recursive_multiple_bindings() {
|
||||
assert_eq!(
|
||||
eval("let a = 10; b = 20; c = 30; d = a + b + c; in d"),
|
||||
Value::Int(60)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn recursive_fibonacci() {
|
||||
assert_eq!(
|
||||
eval("let fib = n: if n <= 1 then 1 else fib (n - 1) + fib (n - 2); in fib 5"),
|
||||
Value::Int(8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn recursive_factorial() {
|
||||
assert_eq!(
|
||||
eval("let factorial = n: if n == 0 then 1 else n * factorial (n - 1); in factorial 5"),
|
||||
Value::Int(120)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn mutual_recursion_simple() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
"let f = n: if n == 0 then 0 else g (n - 1); g = n: if n == 0 then 1 else f (n - 1); in f 5"
|
||||
),
|
||||
Value::Int(1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn mutual_recursion_even_odd() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
"let even = n: if n == 0 then true else odd (n - 1); odd = n: if n == 0 then false else even (n - 1); in even 4"
|
||||
),
|
||||
Value::Bool(true)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn mixed_recursive_and_non_recursive() {
|
||||
assert_eq!(
|
||||
eval("let x = 1; f = n: if n == 0 then x else f (n - 1); in f 5"),
|
||||
Value::Int(1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn mixed_with_multiple_non_recursive() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
"let a = 10; b = 20; sum = a + b; countdown = n: if n == 0 then sum else countdown (n - 1); in countdown 3"
|
||||
),
|
||||
Value::Int(30)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn rec_attrset_non_recursive() {
|
||||
assert_eq!(eval("rec { x = 1; y = 2; z = x + y; }.z"), Value::Int(3));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn rec_attrset_recursive() {
|
||||
assert_eq!(
|
||||
eval("rec { f = n: if n == 0 then 0 else f (n - 1); }.f 10"),
|
||||
Value::Int(0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn nested_let_non_recursive() {
|
||||
assert_eq!(
|
||||
eval("let x = 1; in let y = x + 1; z = y + 1; in z"),
|
||||
Value::Int(3)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn nested_let_with_recursive() {
|
||||
assert_eq!(
|
||||
eval("let f = n: if n == 0 then 0 else f (n - 1); in let g = m: f m; in g 5"),
|
||||
Value::Int(0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn three_way_mutual_recursion() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
"let a = n: if n == 0 then 1 else b (n - 1); b = n: if n == 0 then 2 else c (n - 1); c = n: if n == 0 then 3 else a (n - 1); in a 6"
|
||||
),
|
||||
Value::Int(1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn complex_mixed_dependencies() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
"let x = 5; y = 10; sum = x + y; fib = n: if n <= 1 then 1 else fib (n - 1) + fib (n - 2); result = sum + fib 5; in result"
|
||||
),
|
||||
Value::Int(23)
|
||||
);
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
use fix::value::Value;
|
||||
|
||||
use crate::utils::{eval, eval_result};
|
||||
|
||||
#[test_log::test]
|
||||
fn string_returns_as_is() {
|
||||
assert_eq!(
|
||||
eval(r#"toString "hello""#),
|
||||
Value::String("hello".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn integer_to_string() {
|
||||
assert_eq!(eval("toString 42"), Value::String("42".to_string()));
|
||||
assert_eq!(eval("toString (-5)"), Value::String("-5".to_string()));
|
||||
assert_eq!(eval("toString 0"), Value::String("0".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn float_to_string() {
|
||||
assert_eq!(eval("toString 3.14"), Value::String("3.14".to_string()));
|
||||
assert_eq!(eval("toString 0.0"), Value::String("0".to_string()));
|
||||
assert_eq!(eval("toString (-2.5)"), Value::String("-2.5".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn bool_to_string() {
|
||||
assert_eq!(eval("toString true"), Value::String("1".to_string()));
|
||||
assert_eq!(eval("toString false"), Value::String("".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn null_to_string() {
|
||||
assert_eq!(eval("toString null"), Value::String("".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn simple_list_to_string() {
|
||||
assert_eq!(eval("toString [1 2 3]"), Value::String("1 2 3".to_string()));
|
||||
assert_eq!(
|
||||
eval(r#"toString ["a" "b" "c"]"#),
|
||||
Value::String("a b c".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn nested_list_flattens() {
|
||||
assert_eq!(
|
||||
eval("toString [[1 2] [3 4]]"),
|
||||
Value::String("1 2 3 4".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
eval("toString [1 [2 3] 4]"),
|
||||
Value::String("1 2 3 4".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn empty_list_in_list_no_extra_space() {
|
||||
assert_eq!(eval("toString [1 [] 2]"), Value::String("1 2".to_string()));
|
||||
assert_eq!(eval("toString [[] 1 2]"), Value::String("1 2".to_string()));
|
||||
assert_eq!(eval("toString [1 2 []]"), Value::String("1 2 ".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn list_with_multiple_empty_lists() {
|
||||
assert_eq!(
|
||||
eval("toString [1 [] [] 2]"),
|
||||
Value::String("1 2".to_string())
|
||||
);
|
||||
assert_eq!(eval("toString [[] [] 1]"), Value::String("1".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn list_with_bool_and_null() {
|
||||
assert_eq!(
|
||||
eval("toString [true false null]"),
|
||||
Value::String("1 ".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
eval("toString [1 true 2 false 3]"),
|
||||
Value::String("1 1 2 3".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn mixed_type_list() {
|
||||
assert_eq!(
|
||||
eval(r#"toString [1 "hello" 2.5 true]"#),
|
||||
Value::String("1 hello 2.5 1".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn attrs_with_out_path() {
|
||||
assert_eq!(
|
||||
eval(r#"toString { outPath = "/nix/store/foo"; }"#),
|
||||
Value::String("/nix/store/foo".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn attrs_with_to_string_method() {
|
||||
assert_eq!(
|
||||
eval(r#"toString { __toString = self: "custom"; }"#),
|
||||
Value::String("custom".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn attrs_to_string_self_reference() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
r#"let obj = { x = 42; __toString = self: "x is ${toString self.x}"; }; in toString obj"#
|
||||
),
|
||||
Value::String("x is 42".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn attrs_to_string_priority() {
|
||||
assert_eq!(
|
||||
eval(r#"toString { __toString = self: "custom"; outPath = "/nix/store/foo"; }"#),
|
||||
Value::String("custom".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn derivation_like_object() {
|
||||
assert_eq!(
|
||||
eval(
|
||||
r#"let drv = { type = "derivation"; outPath = "/nix/store/hash-pkg"; }; in toString drv"#
|
||||
),
|
||||
Value::String("/nix/store/hash-pkg".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn string_interpolation_with_int() {
|
||||
assert_eq!(
|
||||
eval(r#""value: ${toString 42}""#),
|
||||
Value::String("value: 42".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn string_interpolation_with_list() {
|
||||
assert_eq!(
|
||||
eval(r#""items: ${toString [1 2 3]}""#),
|
||||
Value::String("items: 1 2 3".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn nested_to_string_calls() {
|
||||
assert_eq!(
|
||||
eval(r#"toString (toString 42)"#),
|
||||
Value::String("42".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_string_in_let_binding() {
|
||||
assert_eq!(
|
||||
eval(r#"let x = toString 42; y = toString 10; in "${x}-${y}""#),
|
||||
Value::String("42-10".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn empty_string() {
|
||||
assert_eq!(eval(r#"toString """#), Value::String("".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn empty_list() {
|
||||
assert_eq!(eval("toString []"), Value::String("".to_string()));
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_string_preserves_spaces_in_strings() {
|
||||
assert_eq!(
|
||||
eval(r#"toString "hello world""#),
|
||||
Value::String("hello world".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn list_of_empty_strings() {
|
||||
assert_eq!(
|
||||
eval(r#"toString ["" "" ""]"#),
|
||||
Value::String(" ".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn deeply_nested_lists() {
|
||||
assert_eq!(
|
||||
eval("toString [[[1] [2]] [[3] [4]]]"),
|
||||
Value::String("1 2 3 4".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn list_with_nested_empty_lists() {
|
||||
assert_eq!(
|
||||
eval("toString [1 [[]] 2]"),
|
||||
Value::String("1 2".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn attrs_without_out_path_or_to_string_fails() {
|
||||
let result = eval_result(r#"toString { foo = "bar"; }"#);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn function_to_string_fails() {
|
||||
let result = eval_result("toString (x: x)");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn to_string_method_must_return_string() {
|
||||
assert_eq!(
|
||||
eval(r#"toString { __toString = self: 42; }"#),
|
||||
Value::String("42".into())
|
||||
);
|
||||
assert_eq!(
|
||||
eval(r#"toString { __toString = self: true; }"#),
|
||||
Value::String("1".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn out_path_can_be_nested() {
|
||||
assert_eq!(
|
||||
eval(r#"toString { outPath = { outPath = "/final/path"; }; }"#),
|
||||
Value::String("/final/path".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test]
|
||||
fn list_spacing_matches_nix_behavior() {
|
||||
assert_eq!(
|
||||
eval(r#"toString ["a" "b"]"#),
|
||||
Value::String("a b".to_string())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
eval(r#"toString ["a" ["b" "c"] "d"]"#),
|
||||
Value::String("a b c d".to_string())
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,13 @@ pub fn eval(expr: &str) -> Value {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn eval_shallow(expr: &str) -> Value {
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.eval_shallow(Source::new_eval(expr.into()).unwrap())
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn eval_deep(expr: &str) -> Value {
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
|
||||
Reference in New Issue
Block a user