1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
import stat
import sys
import pytest
import signal
from pathlib import Path
from textwrap import dedent
from testlib.fixtures.env import ManagedEnv
from testlib.fixtures.nix import Nix
COMMANDS = ["copy-closure", "collect-garbage"]
@pytest.fixture(scope="module")
def custom_sub_command_path(tmp_path_factory: pytest.TempPathFactory) -> Path:
return tmp_path_factory.mktemp(__name__)
@pytest.fixture(scope="module")
def failing_sub_command_path(tmp_path_factory: pytest.TempPathFactory) -> Path:
return tmp_path_factory.mktemp(__name__ + "-failing")
@pytest.fixture(scope="module", params=COMMANDS)
def custom_sub_command(request: pytest.FixtureRequest, custom_sub_command_path: Path) -> str:
# Create an external command that is an alias of an existing one
lix_cmd = request.param
executable = custom_sub_command_path / f"lix-{lix_cmd}"
executable.write_text(
dedent(f"""\
#!{sys.executable}
import os, sys
# Start with args[0] set to the actual nix command used for testing
# as we are not making Lix variants of those.
os.execvp("nix", [ "nix-{lix_cmd}" ] + sys.argv[1:])
""")
)
executable.chmod(stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
return lix_cmd
@pytest.fixture(scope="module")
def failing_sub_command(failing_sub_command_path: Path, custom_sub_command: str) -> str:
# Create an external command that will intentionally cause an error
executable = failing_sub_command_path / f"lix-{custom_sub_command}"
executable.write_text(
dedent(f"""\
#!{sys.executable}
import sys
sys.exit(42)
""")
)
executable.chmod(stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
return custom_sub_command
@pytest.fixture
def path(custom_sub_command_path: Path, env: ManagedEnv):
"""Provide a modified search path"""
env.path.append("/some/incorrect")
env.path.append("/location/for/fun")
env.path.append(str(custom_sub_command_path))
@pytest.fixture
def path_with_failure(
request: pytest.FixtureRequest,
custom_sub_command_path: Path,
failing_sub_command_path: Path,
env: ManagedEnv,
):
"""
Provide a search path with failing binaries inserted in the specified position
"""
(first, fail) = request.param
command_path = [str(custom_sub_command_path), str(failing_sub_command_path)]
if fail:
command_path.reverse()
if first:
env.path.prepend(command_path[1])
env.path.prepend(command_path[0])
else:
env.path.append(command_path[0])
env.path.append(command_path[1])
@pytest.mark.parametrize(
("nix_exe", "flag", "expected"),
[("nix", False, 1), ("lix", False, 1), ("nix", True, 1), ("lix", True, 0)],
)
@pytest.mark.usefixtures("path")
def test_sub_commands(nix: Nix, custom_sub_command: str, nix_exe: str, flag: bool, expected: int):
if flag:
nix.settings.add_xp_feature("lix-custom-sub-commands")
# Test custom sub commands in various configurations
nix.nix([custom_sub_command, "--version"], nix_exe=nix_exe).run().expect(expected)
@pytest.mark.parametrize(
("path_with_failure", "expected"),
[((True, True), 42), ((True, False), 0), ((False, True), 42), ((False, False), 0)],
indirect=["path_with_failure"],
)
@pytest.mark.usefixtures("path_with_failure")
def test_sub_command_path_order(nix: Nix, failing_sub_command: str, expected: int):
# Test handling of the order of the path for custom sub commands
# Incidentally also tests passing through exit codes
nix.settings.add_xp_feature("lix-custom-sub-commands")
nix.nix([failing_sub_command, "--version"], nix_exe="lix").run().expect(expected)
@pytest.mark.skip(
reason="TODO(raito): we do not support auto completion for custom sub commands for now"
)
def test_custom_sub_commands_auto_completion(nix: Nix, tmp_path: Path): ...
@pytest.mark.skip(
reason="TODO(raito): we do not test flag handling for custom sub commands for now"
)
def test_custom_sub_command_flag_handling(nix: Nix, tmp_path: Path):
# Short, long and multiple flags should be tested as well.
# `--` special flag
# test positional arguments, but only `nix-copy-closure` implements some, and it's pesky to test here.
...
@pytest.mark.usefixtures("path")
def test_custom_sub_command_signals(nix: Nix, custom_sub_command_path: Path):
command = custom_sub_command_path / "lix-signals"
command.write_text(
dedent(f"""\
#!{sys.executable}
import signal
print(signal.pthread_sigmask(signal.SIG_BLOCK, []))
""")
)
command.chmod(stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
current_mask = signal.pthread_sigmask(signal.SIG_BLOCK, [])
nix.settings.add_xp_feature("lix-custom-sub-commands")
result = nix.nix(["signals"], nix_exe="lix").run()
result.ok()
assert result.stdout_s.strip() == str(current_mask)
|