From a1b1b4dbf21e76e241e906b6f439155278368fa9 Mon Sep 17 00:00:00 2001 From: Matthew Willcockson Date: Tue, 28 Sep 2021 21:01:00 -0500 Subject: [PATCH] test: add tests using git (#347) git is isolated using the following environment, which are set globally for the duration of that the new pytest fixture "tmp_git" is used: GIT_CONFIG_GLOBAL GIT_CONFIG_NOSYSTEM HOME GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME GIT_AUTHOR_DATE GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_DATE GIT_DIR and GIT_WORK_TREE could be set so that git can be called using any method, but instead a git() function is added that uses git's -C command-line option. These were taken from one of git's test scripts: https://github.com/git/git/blob/cefe983a320c03d7843ac78e73bd513a27806845/t/test-lib.sh#L454-L461 There are probably other ways git can be isolated. The repository is initialized with an empty commit, but this isn't strictly necessary, it just makes some of the possible tests require less setup. The new tmp_project fixture copies the sample module from ./test/samples/module1_toml to the project and commits the files. A test is added for #345 as an example of how this can be used. A pytest marker is added so that tests with either "needgit" or "needsgit" in the name are skipped if python can't find an executable named "git". The tox configuration is changed and another pytest marker is added so that those tests can be run by themselves: tox -- -m needgit or skipped: tox -- -m "not needgit" Type hints were added to help with development, but aren't necessary to keep. --- pyproject.toml | 6 ++++ tests/conftest.py | 86 +++++++++++++++++++++++++++++++++++++++++++-- tests/test_build.py | 9 +++++ tox.ini | 2 +- 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7a613581..6ac8dbb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,3 +42,9 @@ Source = "https://github.com/takluyver/flit" [project.scripts] flit = "flit:main" + +[tool.pytest.ini_options] +markers = [ + "needgit: needs git to be installed in order to run", + "needsgit: needs git to be installed in order to run", +] diff --git a/tests/conftest.py b/tests/conftest.py index 4c5ecef1..0a7e7de2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,15 +1,97 @@ from pathlib import Path +from shutil import copy, copytree, which +from subprocess import check_output +from typing import TYPE_CHECKING +from unittest.mock import patch + import pytest -from shutil import copytree -samples_dir = Path(__file__).parent / 'samples' +if TYPE_CHECKING: + from typing import Iterator, List, Union + +samples_dir = Path(__file__).parent / "samples" + +skip_if_no_git = pytest.mark.skipif( + (not which("git")), + reason="needs git to be installed and findable through the PATH environment variable", +) + + +def pytest_collection_modifyitems(items): + for item in items: + if "needgit" in item.nodeid or "needsgit" in item.nodeid: + item.add_marker(skip_if_no_git) + item.add_marker(pytest.mark.needgit) + item.add_marker(pytest.mark.needsgit) + @pytest.fixture def copy_sample(tmp_path): """Copy a subdirectory from the samples dir to a temp dir""" + def copy(dirname): dst = tmp_path / dirname copytree(str(samples_dir / dirname), str(dst)) return dst return copy + + +def git(repo: Path, command: "Union[List[str], str]") -> bytes: + if isinstance(command, str): + args = command.split() + else: + args = command + + return check_output( + ["git", "-C", str(repo), *args], + ) + + +@pytest.fixture +def tmp_git(tmp_path: Path) -> "Iterator[Path]": + """ + Make a git repository in a temporary folder + + The path returned is what should be passed to git's -C command, or what cwd + should be set to in subprocess calls + """ + git_global_config = tmp_path / "git_global_config" + git_global_config.touch(exist_ok=False) + repository = tmp_path / "repository" + repository.mkdir(exist_ok=False) + with patch.dict( + "os.environ", + { + # https://git-scm.com/docs/git#Documentation/git.txt-codeGITCONFIGGLOBALcode + "GIT_CONFIG_GLOBAL": str(git_global_config), + # https://git-scm.com/docs/git#Documentation/git.txt-codeGITCONFIGNOSYSTEMcode + "GIT_CONFIG_NOSYSTEM": "true", + "HOME": str(tmp_path), + # tox by default only passes the PATH environment variable, so + # XDG_CONFIG_HOME is already unset + # https://github.com/git/git/blob/cefe983a320c03d7843ac78e73bd513a27806845/t/test-lib.sh#L454-L461 + "GIT_AUTHOR_EMAIL": "author@example.com", + "GIT_AUTHOR_NAME": "A U Thor", + "GIT_AUTHOR_DATE": "1112354055 +0200", + "GIT_COMMITTER_EMAIL": "committer@example.com", + "GIT_COMMITTER_NAME": "committer", + "GIT_COMMITTER_DATE": "1112354055 +0200", + }, + ): + git(repository, "config --global init.defaultBranch main") + git(repository, ["init"]) + git(repository, "commit --allow-empty --allow-empty-message --no-edit") + + yield repository + + +@pytest.fixture +def tmp_project(tmp_git: Path) -> "Iterator[Path]": + "return a path to the root of a git repository containing a sample package" + for file in (samples_dir / "module1_toml").glob("*"): + copy(str(file), str(tmp_git / file.name)) + git(tmp_git, "add -A :/") + git(tmp_git, "commit --allow-empty --allow-empty-message --no-edit") + + yield tmp_git diff --git a/tests/test_build.py b/tests/test_build.py index c17ba649..2ee485eb 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -5,6 +5,7 @@ from tempfile import TemporaryDirectory from testpath import assert_isdir, MockCommand +from .conftest import git from flit_core import common from flit import build @@ -69,3 +70,11 @@ def test_build_module_no_docstring(): with pytest.raises(common.NoDocstringError) as exc_info: build.main(pyproject) assert 'no_docstring.py' in str(exc_info.value) + +def test_build_needgit_unicode_filenames(tmp_project: Path) -> None: + "does a package build if it includes a unicode filename?" + noel_file = tmp_project / "No\N{LATIN SMALL LETTER E WITH DIAERESIS}l" + noel_file.touch() + git(tmp_project, "add -A :/") + git(tmp_project, "commit --allow-empty --allow-empty-message --no-edit") + build.main(tmp_project / "pyproject.toml") diff --git a/tox.ini b/tox.ini index 59ddc05d..7f807b2b 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ setenv = PYTHONPATH = flit_core commands = - python -m pytest --cov=flit --cov=flit_core/flit_core + python -m pytest --cov=flit --cov=flit_core/flit_core {posargs} [testenv:bootstrap] skip_install = true