mirror of
https://github.com/nix-community/nix-direnv.git
synced 2025-11-08 11:36:11 +01:00
rewrite tests using pytest
this allows us to use fixtures that are more flexible
This commit is contained in:
parent
6dbc942fea
commit
748988f4b9
10 changed files with 195 additions and 113 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,2 +1,7 @@
|
||||||
.direnv/
|
.direnv/
|
||||||
/template/flake.lock
|
/template/flake.lock
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,5 @@ writeScript "run-tests" ''
|
||||||
${mypy}/bin/mypy tests
|
${mypy}/bin/mypy tests
|
||||||
|
|
||||||
echo -e "\x1b[32m## run unittest\x1b[0m"
|
echo -e "\x1b[32m## run unittest\x1b[0m"
|
||||||
${python3.interpreter} -m unittest discover tests
|
${python3.pkgs.pytest}/bin/pytest .
|
||||||
''
|
''
|
||||||
|
|
|
||||||
23
setup.cfg
Normal file
23
setup.cfg
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
[wheel]
|
||||||
|
universal = 1
|
||||||
|
|
||||||
|
[pycodestyle]
|
||||||
|
max-line-length = 88
|
||||||
|
ignore = E501,E741,W503
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 88
|
||||||
|
ignore = E501,E741,W503
|
||||||
|
exclude = .git,__pycache__,docs/source/conf.py,old,build,dist
|
||||||
|
|
||||||
|
[mypy]
|
||||||
|
warn_redundant_casts = true
|
||||||
|
disallow_untyped_calls = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
no_implicit_optional = true
|
||||||
|
|
||||||
|
[mypy-pytest.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[isort]
|
||||||
|
profile = black
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
with pkgs;
|
with pkgs;
|
||||||
mkShell {
|
mkShell {
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
python3
|
python3.pkgs.pytest
|
||||||
|
python3.pkgs.mypy
|
||||||
|
python3.pkgs.black
|
||||||
|
python3.pkgs.flake8
|
||||||
shellcheck
|
shellcheck
|
||||||
direnv
|
direnv
|
||||||
];
|
];
|
||||||
|
|
|
||||||
6
tests/conftest.py
Normal file
6
tests/conftest.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
pytest_plugins = [
|
||||||
|
"direnv_project",
|
||||||
|
"root",
|
||||||
|
]
|
||||||
47
tests/direnv_project.py
Normal file
47
tests/direnv_project.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import shutil
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from typing import Iterator
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from procs import run
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DirenvProject:
|
||||||
|
dir: Path
|
||||||
|
nix_direnv: Path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def envrc(self) -> Path:
|
||||||
|
return self.dir / ".envrc"
|
||||||
|
|
||||||
|
def setup_envrc(self, content: str) -> None:
|
||||||
|
self.envrc.write_text(
|
||||||
|
f"""
|
||||||
|
source {self.nix_direnv}
|
||||||
|
{content}
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
run(["direnv", "allow"], cwd=self.dir)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def direnv_project(test_root: Path, project_root: Path) -> Iterator[DirenvProject]:
|
||||||
|
"""
|
||||||
|
Setups a direnv test project
|
||||||
|
"""
|
||||||
|
with TemporaryDirectory() as _dir:
|
||||||
|
dir = Path(_dir) / "proj"
|
||||||
|
shutil.copytree(test_root / "testenv", dir)
|
||||||
|
nix_direnv = project_root / "direnvrc"
|
||||||
|
|
||||||
|
c = DirenvProject(Path(dir), nix_direnv)
|
||||||
|
try:
|
||||||
|
yield c
|
||||||
|
finally:
|
||||||
|
pass
|
||||||
25
tests/procs.py
Normal file
25
tests/procs.py
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from typing import List, Union, IO, Any
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
_FILE = Union[None, int, IO[Any]]
|
||||||
|
_DIR = Union[None, Path, str]
|
||||||
|
|
||||||
|
|
||||||
|
def run(
|
||||||
|
cmd: List[str],
|
||||||
|
text: bool = True,
|
||||||
|
check: bool = True,
|
||||||
|
cwd: _DIR = None,
|
||||||
|
stderr: _FILE = None,
|
||||||
|
stdout: _FILE = None,
|
||||||
|
) -> subprocess.CompletedProcess:
|
||||||
|
if cwd is not None:
|
||||||
|
print(f"cd {cwd}")
|
||||||
|
print("$ " + " ".join(cmd))
|
||||||
|
return subprocess.run(
|
||||||
|
cmd, text=text, check=check, cwd=cwd, stderr=stderr, stdout=stdout
|
||||||
|
)
|
||||||
24
tests/root.py
Normal file
24
tests/root.py
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
TEST_ROOT = Path(__file__).parent.resolve()
|
||||||
|
PROJECT_ROOT = TEST_ROOT.parent
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_root() -> Path:
|
||||||
|
"""
|
||||||
|
Root directory of the tests
|
||||||
|
"""
|
||||||
|
return TEST_ROOT
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def project_root() -> Path:
|
||||||
|
"""
|
||||||
|
Root directory of the tests
|
||||||
|
"""
|
||||||
|
return PROJECT_ROOT
|
||||||
111
tests/test.py
111
tests/test.py
|
|
@ -1,111 +0,0 @@
|
||||||
#!/usr/bin/env python2
|
|
||||||
|
|
||||||
from tempfile import TemporaryDirectory
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
import shutil
|
|
||||||
import unittest
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
TEST_ROOT = Path(__file__).resolve().parent
|
|
||||||
RENEWED_MESSAGE = "renewed cache"
|
|
||||||
CACHED_MESSAGE = "using cached dev shell"
|
|
||||||
|
|
||||||
|
|
||||||
def run(cmd: List[str], **kwargs) -> subprocess.CompletedProcess:
|
|
||||||
print("$ " + " ".join(cmd))
|
|
||||||
return subprocess.run(cmd, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class TestBaseNamespace:
|
|
||||||
"""Nested so test discovery doesn't run the base class tests directly."""
|
|
||||||
|
|
||||||
class TestBase(unittest.TestCase):
|
|
||||||
env: dict
|
|
||||||
dir: TemporaryDirectory
|
|
||||||
testenv: Path
|
|
||||||
direnvrc: str
|
|
||||||
direnvrc_command: str
|
|
||||||
out1: subprocess.CompletedProcess
|
|
||||||
out2: subprocess.CompletedProcess
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls) -> None:
|
|
||||||
cls.env = os.environ.copy()
|
|
||||||
cls.dir = TemporaryDirectory()
|
|
||||||
cls.env["HOME"] = str(cls.dir.name)
|
|
||||||
cls.testenv = Path(cls.dir.name).joinpath("testenv")
|
|
||||||
shutil.copytree(TEST_ROOT.joinpath("testenv"), cls.testenv)
|
|
||||||
cls.direnvrc = str(TEST_ROOT.parent.joinpath("direnvrc"))
|
|
||||||
|
|
||||||
with open(cls.testenv.joinpath(".envrc"), "w") as f:
|
|
||||||
f.write(f"source {cls.direnvrc}\n{cls.direnvrc_command}")
|
|
||||||
|
|
||||||
run(["direnv", "allow"], cwd=str(cls.testenv), env=cls.env, check=True)
|
|
||||||
|
|
||||||
run(["nix-collect-garbage"], check=True)
|
|
||||||
|
|
||||||
cls.out1 = run(
|
|
||||||
["direnv", "exec", str(cls.testenv), "hello"],
|
|
||||||
env=cls.env,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
sys.stderr.write(cls.out1.stderr)
|
|
||||||
|
|
||||||
run(["nix-collect-garbage"], check=True)
|
|
||||||
|
|
||||||
cls.out2 = run(
|
|
||||||
["direnv", "exec", str(cls.testenv), "hello"],
|
|
||||||
env=cls.env,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
sys.stderr.write(cls.out2.stderr)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls) -> None:
|
|
||||||
cls.dir.cleanup()
|
|
||||||
|
|
||||||
def test_fresh_shell_message(self) -> None:
|
|
||||||
self.assertIn(RENEWED_MESSAGE, self.out1.stderr)
|
|
||||||
|
|
||||||
def test_fresh_shell_shellHook_gets_executed(self) -> None:
|
|
||||||
self.assertIn("Executing shellHook.", self.out1.stderr)
|
|
||||||
|
|
||||||
def test_fresh_shell_returncode(self) -> None:
|
|
||||||
self.assertEqual(self.out1.returncode, 0)
|
|
||||||
|
|
||||||
def test_cached_shell_message(self) -> None:
|
|
||||||
self.assertIn(CACHED_MESSAGE, self.out2.stderr)
|
|
||||||
|
|
||||||
def test_cached_shell_shellHook_gets_executed(self) -> None:
|
|
||||||
self.assertIn("Executing shellHook.", self.out2.stderr)
|
|
||||||
|
|
||||||
def test_cached_shell_returncode(self) -> None:
|
|
||||||
self.assertEqual(self.out2.returncode, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class NixShellTest(TestBaseNamespace.TestBase):
|
|
||||||
direnvrc_command = "use nix"
|
|
||||||
|
|
||||||
|
|
||||||
class FlakeTest(TestBaseNamespace.TestBase):
|
|
||||||
direnvrc_command = "use flake"
|
|
||||||
|
|
||||||
def test_gcroot_symlink_created_and_valid(self) -> None:
|
|
||||||
inputs = list(self.testenv.joinpath(".direnv/flake-inputs").iterdir())
|
|
||||||
# should only contain our flake-utils flake
|
|
||||||
if len(inputs) != 3:
|
|
||||||
subprocess.run(["nix", "flake", "archive", "--json"], cwd=self.testenv)
|
|
||||||
print(inputs)
|
|
||||||
self.assertEqual(len(inputs), 3)
|
|
||||||
for symlink in inputs:
|
|
||||||
self.assertTrue(symlink.is_dir())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
60
tests/test_gc.py
Normal file
60
tests/test_gc.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python2
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from procs import run
|
||||||
|
from direnv_project import DirenvProject
|
||||||
|
|
||||||
|
|
||||||
|
def common_test(direnv_project: DirenvProject) -> None:
|
||||||
|
run(["nix-collect-garbage"])
|
||||||
|
|
||||||
|
testenv = str(direnv_project.dir)
|
||||||
|
|
||||||
|
out1 = run(
|
||||||
|
["direnv", "exec", testenv, "hello"],
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
check=False,
|
||||||
|
cwd=direnv_project.dir,
|
||||||
|
)
|
||||||
|
sys.stderr.write(out1.stderr)
|
||||||
|
assert out1.returncode == 0
|
||||||
|
assert "renewed cache" in out1.stderr
|
||||||
|
assert "Executing shellHook." in out1.stderr
|
||||||
|
|
||||||
|
run(["nix-collect-garbage"])
|
||||||
|
|
||||||
|
out2 = run(
|
||||||
|
["direnv", "exec", testenv, "hello"],
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
check=False,
|
||||||
|
cwd=direnv_project.dir,
|
||||||
|
)
|
||||||
|
sys.stderr.write(out2.stderr)
|
||||||
|
assert out1.returncode == 0
|
||||||
|
assert "using cached dev shell" in out2.stderr
|
||||||
|
assert "Executing shellHook." in out2.stderr
|
||||||
|
|
||||||
|
|
||||||
|
def test_use_nix(direnv_project: DirenvProject) -> None:
|
||||||
|
direnv_project.setup_envrc("use nix")
|
||||||
|
common_test(direnv_project)
|
||||||
|
|
||||||
|
|
||||||
|
def test_use_flake(direnv_project: DirenvProject) -> None:
|
||||||
|
direnv_project.setup_envrc("use flake")
|
||||||
|
common_test(direnv_project)
|
||||||
|
inputs = list((direnv_project.dir / ".direnv/flake-inputs").iterdir())
|
||||||
|
# should only contain our flake-utils flake
|
||||||
|
if len(inputs) != 3:
|
||||||
|
run(["nix", "flake", "archive", "--json"], cwd=direnv_project.dir)
|
||||||
|
print(inputs)
|
||||||
|
assert len(inputs) == 3
|
||||||
|
for symlink in inputs:
|
||||||
|
assert symlink.is_dir()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue