From b707403a308030739dfeacc5b0aaaeef8ba3f633 Mon Sep 17 00:00:00 2001 From: "Commentator2.0" Date: Sat, 18 Oct 2025 19:35:40 +0200 Subject: [PATCH] tests/functional2: migrate eval.sh Change-Id: Ib5fb9e3b5d76612e7259ec17fb890298198fc885 --- tests/functional/eval.sh | 66 ----------- tests/functional/meson.build | 1 - tests/functional2/eval/assets/eval.nix | 5 + tests/functional2/eval/test_eval_cli.py | 144 ++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 67 deletions(-) delete mode 100644 tests/functional/eval.sh create mode 100644 tests/functional2/eval/assets/eval.nix create mode 100644 tests/functional2/eval/test_eval_cli.py diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh deleted file mode 100644 index 0834efbf4..000000000 --- a/tests/functional/eval.sh +++ /dev/null @@ -1,66 +0,0 @@ -source common.sh - -clearStore - -testStdinHeredoc=$(nix eval -f - < $TEST_ROOT/xyzzy/default.nix -[[ $(nix eval --impure --expr "import $TEST_ROOT/foo/bar") = 123 ]] - -# Test that unknown settings are warned about -out="$(expectStderr 0 nix eval --option foobar baz --expr '""' --raw)" -[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 3addb04fe..4fbff5adc 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -108,7 +108,6 @@ functional_tests_scripts = [ 'build-jobless.sh', 'nar-access.sh', 'impure-eval.sh', - 'eval.sh', 'repl.sh', 'binary-cache-build-remote.sh', 'logging.sh', diff --git a/tests/functional2/eval/assets/eval.nix b/tests/functional2/eval/assets/eval.nix new file mode 100644 index 000000000..cabf28c29 --- /dev/null +++ b/tests/functional2/eval/assets/eval.nix @@ -0,0 +1,5 @@ +{ + int = 123; + str = "foo\nbar"; + attr.foo = "bar"; +} diff --git a/tests/functional2/eval/test_eval_cli.py b/tests/functional2/eval/test_eval_cli.py new file mode 100644 index 000000000..a00fbb977 --- /dev/null +++ b/tests/functional2/eval/test_eval_cli.py @@ -0,0 +1,144 @@ +from pathlib import Path +from typing import Any + +import pytest + +from functional2.testlib.fixtures.file_helper import with_files, CopyFile, Symlink, File +from functional2.testlib.fixtures.nix import Nix + + +@pytest.fixture(autouse=True) +def nix_command_feature(nix: Nix): + nix.settings.feature("nix-command") + + +@pytest.mark.parametrize( + ("stdin", "exp", "flags"), + [ + ( + b""" + { + bar = 3 + 1; + foo = 2 + 2; + } + """, + "{ bar = 4; foo = 4; }", + ["-f", "-"], + ) + ], +) +@with_files({"eval.nix": CopyFile("assets/eval.nix")}) +def test_valid_eval_stdin(nix: Nix, stdin: bytes, exp: Any, flags: list[str]): + cmd = nix.nix(["eval", *flags]).with_stdin(stdin) + res = cmd.run().ok() + assert res.stdout_plain == exp + + +@with_files({"eval.nix": CopyFile("assets/eval.nix")}) +def test_valid_filename_stdin(nix: Nix, files: Path): + stdin = (files / "eval.nix").read_bytes() + res = nix.nix(["eval", "int", "-f", "-"]).with_stdin(stdin).run().ok() + assert res.stdout_plain == "123" + + res = nix.nix_instantiate(["-A", "int", "--eval", "-"]).with_stdin(stdin).run().ok() + assert res.stdout_plain == "123" + + +@pytest.mark.parametrize( + ("flags", "exp"), + [ + (["int"], "123"), + (["str"], '"foo\\nbar"'), + (["str", "--raw"], "foo\nbar"), + (["attr"], '{ foo = "bar"; }'), + (["attr", "--json"], '{"foo":"bar"}'), + ], +) +@with_files({"eval.nix": CopyFile("assets/eval.nix")}) +def test_valid_eval_f(nix: Nix, flags: list[str], exp: str): + # nix3 cli + res = nix.nix(["eval", *flags, "-f", "./eval.nix"]).run().ok() + assert res.stdout_plain == exp + + # legacy cli + res = nix.nix_instantiate(["-A", *flags, "--eval", "./eval.nix"]).run().ok() + assert res.stdout_plain == exp + + +@pytest.mark.parametrize( + ("expr", "exp"), + [("assert 1 + 2 == 3; true", "true"), ('{"assert"=1;bar=2;}', '{ "assert" = 1; bar = 2; }')], +) +@pytest.mark.parametrize("long", [False, True]) +def test_valid_expr(nix: Nix, expr: str, exp: str, long: bool): + flag = "--expr" if long else "-E" + res = nix.nix(["eval", flag, expr]).run().ok() + assert res.stdout_plain == exp + + res = nix.nix_instantiate(["--eval", flag, expr]).run().ok() + assert res.stdout_plain == exp + + +@with_files({"eval.nix": CopyFile("assets/eval.nix")}) +def test_invalid_no_coercible_values(nix: Nix): + """Non-coercible values throws errors under `--raw`""" + res = nix.nix(["eval", "int", "--raw", "-f", "./eval.nix"]).run().expect(1) + assert "error: cannot coerce an integer to a string: 123" in res.stderr_plain + + res = nix.nix_instantiate(["-A", "int", "--raw", "./eval.nix"]).run().expect(1) + assert ( + "error: expression was expected to be a derivation or collection of derivations, but instead was an integer" + in res.stderr_plain + ) + + +def test_invalid_top_level_error(nix: Nix): + """Top-level eval errors should be printed to stderr with a traceback.""" + res = nix.nix(["eval", "--expr", 'throw "a sample throw message"']).run().expect(1) + assert "a sample throw message" in res.stderr_plain + assert "caused by explicit throw" in res.stderr_plain + + +def test_valid_nested_error(nix: Nix): + """But errors inside something should print an elided version, and exit with 0.""" + res = nix.nix(["eval", "--expr", '{ throws = throw "a sample throw message"; }']).run().ok() + assert res.stdout_plain == "{ throws = «error: a sample throw message»; }" + + +def test_valid_restricted_toFile(nix: Nix): # noqa: N802 # builtin name + """Check if toFile can be utilized during restricted eval""" + res = ( + nix.nix(["eval", "--restrict-eval", "--expr", 'import (builtins.toFile "source" "42")']) + .run() + .ok() + ) + assert res.stdout_plain == "42" + + +@with_files({"cycle.nix": Symlink("cycle.nix")}) +# timeout given in seconds +@pytest.mark.timeout(1) +def test_invalid_no_hang_symlink_cycle(nix: Nix): + """Check that symlink cycles don't cause a hang.""" + res = nix.nix(["eval", "--file", "cycle.nix"]).run().expect(1) + assert ( + "error: too many symbolic links encountered while traversing the path" in res.stderr_plain + ) + + +@with_files({"xyzzy": {"default.nix": File("123")}, "foo": {"bar": Symlink("../xyzzy")}}) +def test_valid_symlink_resolution(nix: Nix): + """Check that relative symlinks are resolved correctly.""" + res = nix.nix(["eval", "--impure", "--expr", "import ./foo/bar"]).run().ok() + assert res.stdout_plain == "123" + + +def test_valid_warn_unknown_setting(nix: Nix): + """Test that unknown settings are warned about""" + res = ( + nix.nix(["eval", "--option", "foobar", "baz", "--expr", '"foxes are cute"', "--raw"]) + .run() + .ok() + ) + assert res.stdout_plain == "foxes are cute" + assert "warning: unknown setting 'foobar'" in res.stderr_plain