they're no longer used since non-diverted stores are the default now. Change-Id: I277b819340c2b69e1fd06607e562221469b77926
223 lines
6.8 KiB
Python
223 lines
6.8 KiB
Python
import os
|
||
import shutil
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
from _pytest.fixtures import FixtureRequest
|
||
|
||
from testlib.fixtures.command import Command
|
||
from testlib.fixtures.file_helper import with_files, Symlink, CopyFile
|
||
from testlib.fixtures.nix import Nix
|
||
from testlib.utils import get_global_asset_pack, get_global_asset
|
||
|
||
|
||
TAR_FILES = {
|
||
"tarball": get_global_asset_pack("dependencies")
|
||
| {"default.nix": get_global_asset("dependencies/dependencies.nix")}
|
||
}
|
||
|
||
|
||
def _set_mtime_of_folder(path: Path, mtime: int = 1000000000):
|
||
for file in [path] + list(path.iterdir()) if path.is_dir() else []:
|
||
os.utime(file, (mtime, mtime))
|
||
|
||
|
||
@pytest.fixture(params=[("", "cat"), (".xz", "xz"), (".gz", "gzip")])
|
||
def tarball(request: FixtureRequest, nix: Nix, files: Path) -> Path:
|
||
ext, compressor = request.param
|
||
# setup
|
||
_set_mtime_of_folder(files / "tarball")
|
||
env = nix.env
|
||
env["GNUTAR_REPRODUCIBLE"] = ""
|
||
tar_exe = shutil.which("tar")
|
||
env.path.add_program("tar")
|
||
tarball_name = f"tarball.tar{ext}"
|
||
tarball_path = files / tarball_name
|
||
|
||
# Create tarball
|
||
tarball_content = (
|
||
Command(
|
||
[
|
||
"tar",
|
||
f"--mtime={files / 'tarball' / 'default.nix'}",
|
||
"--owner=0",
|
||
"--group=0",
|
||
"--numeric-owner",
|
||
"--sort=name",
|
||
f"--to-command={compressor}",
|
||
"-c",
|
||
"-f",
|
||
"-",
|
||
"tarball",
|
||
],
|
||
env,
|
||
exe=tar_exe,
|
||
)
|
||
.run()
|
||
.ok()
|
||
.stdout
|
||
)
|
||
tarball_path.write_bytes(tarball_content)
|
||
|
||
res = nix.nix_env(["-f", f"file://{tarball_path}", "-qa", "--out-path"]).run().ok()
|
||
assert "dependencies" in res.stdout_plain
|
||
|
||
return tarball_path
|
||
|
||
|
||
@pytest.fixture
|
||
def tar_hash(files: Path, nix: Nix) -> str:
|
||
return nix.hash_path(files / "tarball")
|
||
|
||
|
||
@with_files(TAR_FILES)
|
||
@pytest.mark.parametrize(
|
||
"flags",
|
||
[
|
||
["file://{tarball}"], # noqa: RUF027 # yes, true, but we don't have the variables here
|
||
["<foo>", "-I", "foo=file://{tarball}"], # noqa: RUF027
|
||
["-E", 'import (fetchTarball "file://{tarball}")'], # noqa: RUF027
|
||
# Do not re-fetch paths already present
|
||
[
|
||
"-E",
|
||
'import (fetchTarball {{ url = "file:///does-not-exist/must-remain-unused/{tarball}"; sha256 = "{tar_hash}"; }})', # noqa: RUF027
|
||
],
|
||
],
|
||
)
|
||
def test_fetch_tarball(nix: Nix, tarball: Path, tar_hash: str, flags: list[str]):
|
||
# HACK(rootile): if we don't create a copy, we'd try to format it twice, as it is the same list used in other test calls
|
||
flags = flags[:]
|
||
|
||
flags[-1] = flags[-1].format(tarball=tarball, tar_hash=tar_hash)
|
||
nix.nix_build(["-o", "result", *flags]).run().ok()
|
||
|
||
|
||
@with_files(TAR_FILES | {"actual-tmp-dir": {}, "tmp-dir": Symlink("./actual-tmp-dir")})
|
||
def test_tarball_symlink_extraction(nix: Nix, files: Path, tarball: Path):
|
||
nix.env.dirs.tmpdir = files / "tmp-dir"
|
||
|
||
nix.nix_build(
|
||
["-o", "result", "-E", f'import (fetchTarball "file://{files / tarball.name}")']
|
||
).run().ok()
|
||
|
||
real_tmp_dir = nix.env.dirs.test_root / "tmp"
|
||
real_tmp_dir.mkdir(exist_ok=True)
|
||
nix.env.dirs.tmpdir = real_tmp_dir
|
||
|
||
nix.nix_build(
|
||
[
|
||
"-o",
|
||
"result",
|
||
"--temp-dir",
|
||
f"{files}/tmp-dir",
|
||
"-E",
|
||
f'import (fetchTarball "file://{files / tarball.name}")',
|
||
]
|
||
).run().ok()
|
||
|
||
|
||
@with_files(TAR_FILES)
|
||
@pytest.mark.parametrize(
|
||
"expr",
|
||
[
|
||
'import (fetchTree "file://{tarball}")', # noqa: RUF027 # yes, true, but we don't have the variables here
|
||
'import (fetchTree {{ type = "tarball"; url = "file://{tarball}"; }})', # noqa: RUF027
|
||
'import (fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "{tar_hash}"; }})', # noqa: RUF027
|
||
],
|
||
)
|
||
def test_fetch_tree(nix: Nix, tarball: Path, tar_hash: str, expr: str):
|
||
expr = expr.format(tarball=tarball, tar_hash=tar_hash)
|
||
nix.nix_build(["-o", "result", "-E", expr], flake=True).run().ok()
|
||
|
||
|
||
@with_files(TAR_FILES)
|
||
def test_fetch_tree_hash_mismatch(nix: Nix, tarball: Path):
|
||
res = (
|
||
nix.nix_build(
|
||
[
|
||
"-o",
|
||
"result",
|
||
"-E",
|
||
f'import (fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc="; }})',
|
||
],
|
||
flake=True,
|
||
)
|
||
.run()
|
||
.expect(102)
|
||
)
|
||
assert "NAR hash mismatch in input" in res.stderr_plain
|
||
|
||
|
||
@with_files(TAR_FILES)
|
||
def test_last_modified(nix: Nix, tarball: Path):
|
||
res = (
|
||
nix.nix(
|
||
["eval", "--impure", "--expr", f'(fetchTree "file://{tarball}").lastModified'],
|
||
flake=True,
|
||
)
|
||
.run()
|
||
.ok()
|
||
)
|
||
assert res.stdout_plain == "1000000000"
|
||
|
||
|
||
@with_files({"config.nix": get_global_asset("config.nix")})
|
||
@pytest.mark.parametrize(
|
||
("flags", "exit_code"),
|
||
[
|
||
(["1 + 2"], 0),
|
||
(["with <fnord/xyzzy>; 1 + 2"], 0),
|
||
(["<fnord/config.nix>", "-I", "fnord=."], 0),
|
||
(["<fnord/xyzzy> 1"], 1),
|
||
],
|
||
)
|
||
def test_no_accessing_tar(nix: Nix, flags: list[str], exit_code: int):
|
||
nix.nix_instantiate(
|
||
["--eval", "-I", "fnord=file://no-such-tarball.tar.gz", "-E", *flags]
|
||
).run().expect(exit_code)
|
||
|
||
|
||
@with_files(TAR_FILES)
|
||
def test_no_submodules(nix: Nix, tarball: Path, tar_hash: str):
|
||
res = (
|
||
nix.nix_instantiate(
|
||
[
|
||
"--strict",
|
||
"--eval",
|
||
"-E",
|
||
f'!((fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "{tar_hash}"; }})) ? submodules',
|
||
],
|
||
flake=True,
|
||
)
|
||
.run()
|
||
.ok()
|
||
)
|
||
assert res.stdout_plain == "true"
|
||
|
||
|
||
@with_files(TAR_FILES)
|
||
def test_no_accessing_name(nix: Nix, tarball: Path, tar_hash: str):
|
||
"""
|
||
Ensure that the `name` attribute isn't accepted as that would mess with the content-addressing
|
||
"""
|
||
res = (
|
||
nix.nix_instantiate(
|
||
[
|
||
"--eval",
|
||
"-E",
|
||
f'fetchTree {{ type = "tarball"; url = "file://{tarball}"; narHash = "{tar_hash}"; name = "foo"; }}',
|
||
],
|
||
flake=True,
|
||
)
|
||
.run()
|
||
.expect(1)
|
||
)
|
||
assert "error: attribute 'name' isn’t supported in call" in res.stderr_plain # noqa: RUF001 # for some reason, this error message wants to feel special
|
||
|
||
|
||
@with_files({"bad.tar.xz": CopyFile("assets/test_tarball/bad.tar.xz")})
|
||
def test_nix_env_bad_tarball(nix: Nix, files: Path):
|
||
res = nix.nix_env(["-f", f"file://{files / 'bad.tar.xz'}", "-qa", "--out-path"]).run().expect(1)
|
||
assert "error: failed to extract archive (Path contains '..')" in res.stderr_plain
|
||
assert not (nix.env.dirs.tmpdir / "bad").exists()
|