import dataclasses import re import shutil import sys from pathlib import Path import pytest from testlib.fixtures.env import ManagedEnv from testlib.fixtures.env import _ManagedPath def test_env_inits_defaults(tmp_path: Path): env = ManagedEnv(tmp_path) assert env.get_env("GIT_CONFIG_SYSTEM") == "/dev/null" if sys.platform != "darwin": # Darwin doesn't like sandboxes so we don't have a sandbox shell here assert "bash" in env.get_env("SHELL") else: assert "/bin/sh" in env.get_env("SHELL") assert env.get_env("PAGER") == "cat" assert env.dirs.test_root == tmp_path assert env.dirs.home == tmp_path / "test-home" assert env.dirs.real_store_dir == tmp_path / "nix/store" def test_env_unknown_none(env: ManagedEnv): assert env.get_env("SOME_UNKNOWN_STUFF") is None def test_env_unknown_default(env: ManagedEnv): assert env.get_env("QUESTION_OF_LIFE", "42") == "42" def test_env_sets(env: ManagedEnv): assert env.get_env("DRGN") is None env.set_env("DRGN", "cute") assert env.get_env("DRGN") == "cute" def test_env_setitem_success(env: ManagedEnv): env["HELLO"] = "world" assert env.get_env("HELLO") == "world" def test_env_getitem_known(env: ManagedEnv): env["HELLO"] = "world" assert env["HELLO"] == "world" def test_env_getitem_unknown(env: ManagedEnv): with pytest.raises(KeyError): _ = env["HELLO"] def test_env_overrides_custom(env: ManagedEnv): env.set_env("VALID", "you") assert env.get_env("VALID") == "you" env.set_env("VALID", "every creature") assert env.get_env("VALID") == "every creature" def test_env_overrides_defaults(env: ManagedEnv): assert env.get_env("PAGER") == "cat" env.set_env("PAGER", "bat") assert env.get_env("PAGER") == "bat" def test_env_unset_custom(env: ManagedEnv): env.set_env("FAILURE", "me") assert env.get_env("FAILURE") == "me" env.unset_env("FAILURE") assert env.get_env("FAILURE") is None def test_env_unset_default(env: ManagedEnv): assert env.get_env("PAGER") == "cat" env.unset_env("PAGER") assert env.get_env("PAGER") is None def test_env_set_none_errors(env: ManagedEnv): with pytest.raises(ValueError, match=r".+env.unset_env.+"): env.set_env("PAGER", None) # type: ignore def test_env_set_no_dirs(env: ManagedEnv): with pytest.raises(ValueError, match=r"Overriding paths should be done .+"): env.set_env("HOME", "/home/zelda") def test_env_get_dir_works(env: ManagedEnv): assert env.get_env("HOME") == env.dirs.home def test_env_dirs_created(env: ManagedEnv): fields = dataclasses.asdict(env.dirs).items() assert len(fields) > 1 for name, field in fields: field.exists() def test_env_get_path_fails(env: ManagedEnv): with pytest.raises(ValueError, match=r".+env\.path.+"): _ = env.get_env("PATH") def test_env_set_path_fails(env: ManagedEnv): with pytest.raises(ValueError, match=r".+env\.path.+"): env.set_env("PATH", "a:b") def test_env_to_env(tmp_path: Path): env = ManagedEnv(tmp_path) assert set(env.to_env().keys()) == { "GIT_CONFIG_SYSTEM", "SHELL", "PAGER", "HOME", "TEST_ROOT", "NIX_LOG_DIR", "NIX_STATE_DIR", "NIX_CONF_DIR", "NIX_BIN_DIR", "NIX_STORE_DIR", "REAL_STORE_DIR", "CACHE_DIR", "XDG_CACHE_HOME", "PATH", "BUILD_TEST_SHELL", "TMPDIR", "BUILD_TEST_ENV", } | ({"_NIX_TEST_NO_SANDBOX"} if sys.platform == "darwin" else set()) def test_path_inits_build_shell(): path = _ManagedPath("/path/to/build_shell") assert len(path._path) == 1 assert path._path[0] == "/path/to/build_shell" def test_path_appends_at_end(): path = _ManagedPath("/path/to/build_shell") path.append("other_path") assert len(path._path) == 2 assert path._path[1] == "other_path" def test_path_prepends_at_front(): path = _ManagedPath("/path/to/build_shell") path.prepend("other/path") assert len(path._path) == 2 assert path._path[0] == "other/path" def test_remove_path_known(): path = _ManagedPath("/path/to/build_shell") path.append("other/path") assert len(path._path) == 2 path.remove_path("other/path") assert path._path == ["/path/to/build_shell"] def test_remove_path_unknown(): path = _ManagedPath("/path/to/build_shell") path.append("other/path") assert len(path._path) == 2 with pytest.raises(ValueError, match=r".+ x not in list"): path.remove_path("/home/nest/builder") assert len(path._path) == 2 def test_path_add_program_resolves(): path = _ManagedPath("/path/to/build_shell") pytest_path = shutil.which("pytest") path.add_program("pytest", False) assert len(path._path) == 2 assert path._path[1] == pytest_path def test_path_add_program_entire_dir(): path = _ManagedPath("/path/to/build_shell") pytest_path = shutil.which("pytest") path.add_program("pytest", True) assert len(path._path) == 2 actual = path._path[1] assert actual != pytest_path assert actual == pytest_path.rpartition("/")[0] def test_path_remove_program_specific(): path = _ManagedPath("/path/to/build_shell") path.add_program("pytest", False) assert len(path._path) == 2 path.remove_program("pytest") assert len(path._path) == 1 def test_path_remove_program_associated(): path = _ManagedPath("/path/to/build_shell") path.add_program("pytest", True) assert len(path._path) == 2 path.remove_program("pytest") assert len(path._path) == 1 def test_path_to_path(): path = _ManagedPath("/path/to/build_shell") path.prepend("pictures/latest/kate") path.prepend("out/bin/lix") path.append("fops/with/noms") assert path.to_path() == "out/bin/lix:pictures/latest/kate:/path/to/build_shell:fops/with/noms" def test_path_sandbox_entire_store_package(): path = _ManagedPath("/path/to/build_shell") path.add_program(shutil.which("pytest")) sb_paths = path.to_sandbox_paths() assert len(sb_paths) == 2 assert sb_paths[0] == "/path/to/build_shell" assert re.fullmatch(r"^/nix/store/\w{32}-python3-3\.\d{1,2}\.\d{1,2}-[^/]+$", sb_paths[1]) def test_path_nontrivial_build_env(): path = _ManagedPath("/path/to/build_shell", "/path/to/busybox/bin:/path/to/acl/bin") sb_paths = path.to_sandbox_paths() assert len(sb_paths) == 3 assert sb_paths[0] == "/path/to/build_shell" def test_path_which(): path = _ManagedPath("/path/to/build_shell") with pytest.raises(ValueError, match="not in configured path"): path.which("pytest") wanted = shutil.which("pytest") path.add_program(wanted) assert str(path.which("pytest")) == wanted