tests/functional2: migrate eval.sh

Change-Id: Ib5fb9e3b5d76612e7259ec17fb890298198fc885
This commit is contained in:
Commentator2.0
2025-11-21 17:35:47 +00:00
committed by Rutile
parent 40416d103f
commit b707403a30
4 changed files with 149 additions and 67 deletions
-66
View File
@@ -1,66 +0,0 @@
source common.sh
clearStore
testStdinHeredoc=$(nix eval -f - <<EOF
{
bar = 3 + 1;
foo = 2 + 2;
}
EOF
)
[[ $testStdinHeredoc == '{ bar = 4; foo = 4; }' ]]
nix eval --expr 'assert 1 + 2 == 3; true'
nix eval -E 'assert 1 + 2 == 3; true'
[[ $(nix eval int -f "./eval.nix") == 123 ]]
[[ $(nix eval str -f "./eval.nix") == '"foo\nbar"' ]]
[[ $(nix eval str --raw -f "./eval.nix") == $'foo\nbar' ]]
[[ "$(nix eval attr -f "./eval.nix")" == '{ foo = "bar"; }' ]]
[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]]
[[ $(nix eval int -f - < "./eval.nix") == 123 ]]
[[ "$(nix eval --expr '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]]
# Non-coercible values throws errors under `--raw`
topLevelInteger="$(expectStderr 1 nix eval int --raw -f "./eval.nix")"
[[ "$topLevelInteger" =~ "error: cannot coerce an integer to a string: 123" ]]
# Top-level eval errors should be printed to stderr with a traceback.
topLevelThrow="$(expectStderr 1 nix eval --expr 'throw "a sample throw message"')"
[[ "$topLevelThrow" =~ "a sample throw message" ]]
[[ "$topLevelThrow" =~ "caused by explicit throw" ]]
# But errors inside something should print an elided version, and exit with 0.
outputOfNestedThrow="$(nix eval --expr '{ throws = throw "a sample throw message"; }')"
[[ "${outputOfNestedThrow}" == "{ throws = «error: a sample throw message»; }" ]]
# Check if toFile can be utilized during restricted eval
[[ $(nix eval --restrict-eval --expr 'import (builtins.toFile "source" "42")') == 42 ]]
nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]]
[[ $(nix-instantiate -A str --eval "./eval.nix") == '"foo\nbar"' ]]
[[ $(nix-instantiate -A str --raw --eval "./eval.nix") == $'foo\nbar' ]]
[[ "$(nix-instantiate -A attr --eval "./eval.nix")" == '{ foo = "bar"; }' ]]
[[ $(nix-instantiate -A attr --eval --json "./eval.nix") == '{"foo":"bar"}' ]]
[[ $(nix-instantiate -A int --eval - < "./eval.nix") == 123 ]]
[[ "$(nix-instantiate --eval -E '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]]
# Non-coercible values throws errors under `--raw`
topLevelInteger="$(expectStderr 1 nix-instantiate -A int --raw "./eval.nix")"
[[ "$topLevelInteger" =~ "error: expression was expected to be a derivation or collection of derivations, but instead was an integer" ]]
# Check that symlink cycles don't cause a hang.
ln -sfn cycle.nix $TEST_ROOT/cycle.nix
(! nix eval --file $TEST_ROOT/cycle.nix)
# Check that relative symlinks are resolved correctly.
mkdir -p $TEST_ROOT/xyzzy $TEST_ROOT/foo
ln -sfn ../xyzzy $TEST_ROOT/foo/bar
printf 123 > $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 ]]
-1
View File
@@ -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',
+5
View File
@@ -0,0 +1,5 @@
{
int = 123;
str = "foo\nbar";
attr.foo = "bar";
}
+144
View File
@@ -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