feat: add coverage reports for library functions #5839

Merged
tobiasd merged 6 commits from feat/coverage into master 2026-05-08 07:17:49 +00:00
7 changed files with 264 additions and 5 deletions
+3 -1
View File
@@ -10,6 +10,8 @@ output
# Python stuff
.venv
__pycache__
# pytest coverage outputs
.coverage
#phpcsfixer cache
.php-cs-fixer.cache
# Secrets
@@ -19,4 +21,4 @@ __pycache__
secrets.txt
drone_token.txt
# editors
.vscode
.vscode
@@ -0,0 +1,76 @@
# SPDX-FileCopyrightText: Free Software Foundation Europe e.V. <https://fsfe.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later
from pathlib import Path
from typing import Any
import pytest
from fsfe_website_build.lib.build_config import GlobalBuildConfig, SiteBuildConfig
class TestGlobalBuildConfig:
def _base_kwargs(self) -> dict[str, Any]:
return {
"all_languages": ["en", "de", "fr", "it"],
"clean_cache": False,
"full": False,
"languages": [],
"log_level": "INFO",
"processes": 4,
"serve": False,
"sites": [Path("fsfe.org")],
"source": Path("src-sites"),
"stage": False,
"targets": ["output"],
"working_target": Path("output"),
"completion_notification": False,
}
def valid_empty_languages_test(self) -> None:
kwargs = self._base_kwargs()
config = GlobalBuildConfig(**kwargs)
assert config.languages == []
def valid_languages_test(self) -> None:
kwargs = self._base_kwargs()
kwargs["languages"] = ["en", "de"]
config = GlobalBuildConfig(**kwargs)
assert config.languages == ["en", "de"]
def invalid_language_too_short_test(self) -> None:
kwargs = self._base_kwargs()
kwargs["languages"] = ["e"]
with pytest.raises(ValueError, match="two-letter"):
GlobalBuildConfig(**kwargs)
def invalid_language_too_long_test(self) -> None:
kwargs = self._base_kwargs()
kwargs["languages"] = ["eng"]
with pytest.raises(ValueError, match="two-letter"):
GlobalBuildConfig(**kwargs)
def invalid_language_non_alpha_test(self) -> None:
kwargs = self._base_kwargs()
kwargs["languages"] = ["e1"]
with pytest.raises(ValueError, match="two-letter"):
GlobalBuildConfig(**kwargs)
def invalid_language_not_in_all_languages_test(self) -> None:
kwargs = self._base_kwargs()
# check https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
# to ensure that it is not an actual lang code
kwargs["languages"] = ["zz"]
with pytest.raises(ValueError, match="all_languages"):
GlobalBuildConfig(**kwargs)
class TestSiteBuildConfig:
def creation_test(self) -> None:
config = SiteBuildConfig(
languages=["en", "de"],
site=Path("/sites/example"),
)
assert config.languages == ["en", "de"]
assert config.site == Path("/sites/example")
@@ -12,6 +12,8 @@ from lxml import etree
if TYPE_CHECKING:
from collections.abc import Generator
from pytest_mock import MockFixture
class TestCompareFiles:
"""Smoke tests for the high-level entry point."""
@@ -26,24 +28,45 @@ class TestCompareFiles:
b.write_text("<root><y/></root>")
yield a, b
def test_compare_files_returns_list(self, two_files: tuple[Path, Path]) -> None:
def compare_files_returns_list_test(self, two_files: tuple[Path, Path]) -> None:
a, b = two_files
assert isinstance(compare_files(a, b), list)
def test_compare_files_finds_difference(self, two_files: tuple[Path, Path]) -> None:
def compare_files_finds_difference_test(self, two_files: tuple[Path, Path]) -> None:
a, b = two_files
diff = compare_files(a, b)
assert len(diff) == 1
def compare_files_xml_syntax_error_test(self, mocker: MockFixture) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
bad = Path(tmpdir) / "bad.xml"
good = Path(tmpdir) / "good.xml"
bad.write_text("<not xml")
good.write_text("<root/>")
mocker.patch("sys.exit", side_effect=SystemExit)
with pytest.raises(SystemExit):
compare_files(bad, good)
class TestCompareElements:
"""Unit tests for the xml comparator function"""
"""Unit tests for the xml comparison function"""
def identical_elements_test(self) -> None:
e1 = etree.Element("root")
e2 = etree.Element("root")
assert compare_elements(e1, e2) == []
def wildcard_attributes_deletion_test(self) -> None:
e1 = etree.Element("root", a="1")
e2 = etree.Element("root", a="2")
assert compare_elements(e1, e2, ["//root/@*"]) == []
def named_attributes_deletion_test(self) -> None:
e1 = etree.Element("root", a="1")
e2 = etree.Element("root")
assert compare_elements(e1, e2, ["//root/@a"]) == []
def tag_mismatch_test(self) -> None:
e1 = etree.Element("root")
e2 = etree.Element("other")
@@ -4,11 +4,16 @@
import textwrap
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import TYPE_CHECKING
import pytest
from fsfe_website_build.lib.process_file import process_file
from lxml import etree
if TYPE_CHECKING:
from pytest_mock import MockFixture
@pytest.fixture
def sample_xsl_transformer(tmp_path: Path) -> etree.XSLT:
@@ -75,3 +80,89 @@ def process_file_link_rewrites_test(
# we only need to care about the first one
link_node = etree.fromstring(result_doc).xpath("//a[@href and @test_url]")[0]
assert link_node.get("href") == link_out
def xmllist_processing_test(sample_xsl_transformer: etree.XSLT) -> None:
"""Process something that has an XMLLIST."""
with TemporaryDirectory() as tmp:
source = Path(tmp) / "source"
source.mkdir()
lang_folder = source / "global" / "languages"
lang_folder.mkdir(parents=True, exist_ok=True)
en_lang_file = lang_folder / "en"
en_lang_file.write_text("English\n")
action_dir = source / "news"
action_dir.mkdir()
action_file = action_dir / "news.en.xhtml"
action_file.write_text("<html><body>News</body></html>")
list_file = action_dir / ".news.xmllist"
list_file.write_text("global/data/sidebar\n")
sidebar_dir = source / "global" / "data" / "sidebar"
sidebar_dir.mkdir(parents=True)
sidebar_en = sidebar_dir / ".sidebar.en.xml"
sidebar_en.write_text("<sidebar><item>Link</item></sidebar>")
infile = action_dir / "news.de.xhtml"
result = process_file(source, infile, sample_xsl_transformer)
assert result is not None
def missing_translation_fallback_test(sample_xsl_transformer: etree.XSLT) -> None:
"""Process a file where it does not exist in the correct language."""
with TemporaryDirectory() as tmp:
source = Path(tmp) / "source"
source.mkdir()
lang_folder = source / "global" / "languages"
lang_folder.mkdir(parents=True, exist_ok=True)
en_lang_file = lang_folder / "en"
en_lang_file.write_text("English\n")
de_lang_file = lang_folder / "de"
de_lang_file.write_text("Deutsch\n")
action_dir = source / "news"
action_dir.mkdir()
en_file = action_dir / "news.en.xhtml"
en_file.write_text("<html><body>News</body></html>")
infile = action_dir / "news.de.xhtml"
result = process_file(source, infile, sample_xsl_transformer)
assert result is not None
def detect_invalid_xml_from_transformation_test(mocker: MockFixture) -> None:
"""Check that it detects invalid XML being returned"""
with TemporaryDirectory() as tmp:
source = Path(tmp) / "source"
source.mkdir()
lang_folder = source / "global" / "languages"
lang_folder.mkdir(parents=True, exist_ok=True)
en_lang_file = lang_folder / "en"
en_lang_file.write_text("English\n")
action_dir = source / "news"
action_dir.mkdir()
infile = action_dir / "news.en.xhtml"
infile.write_text("<html><body>News</body></html>")
mock_result = mocker.MagicMock()
mock_result.xpath.side_effect = AssertionError("bad xml")
mock_transform = mocker.MagicMock()
mock_transform.return_value = mock_result
result = process_file(source, infile, mock_transform)
assert result == str(mock_result)
+5 -1
View File
@@ -59,7 +59,11 @@ pre-commit:
pytest:
glob:
- "*.py"
run: pytest ./build/fsfe_website_build_tests_pre_commit
run: coverage run -m pytest ./build/fsfe_website_build_tests_pre_commit
pytest-coverage:
glob:
- "*.py"
run: coverage report --show-missing --fail-under=85
ty:
glob: "*.py"
run: ty check {staged_files}
+8
View File
@@ -23,6 +23,7 @@ dev = [
"pillow", # image processing
"pyright", # python typechecker
"pytest", # python test runner
"pytest-cov", # coverage reports for python coverage
"pytest-mock", # helper for mocking in pytest
"reuse", # for enforcing licensing
"ruff", # python formatter and linter
@@ -38,6 +39,13 @@ notifications = [
requires = ["uv_build"]
build-backend = "uv_build"
[tool.coverage.run]
source = ["fsfe_website_build.lib"]
[tool.coverage.report]
show_missing = true
fail_under = 100
[tool.pytest]
addopts = ["--import-mode=importlib"]
python_files = ["*_test.py"]
Generated
+55
View File
@@ -146,6 +146,45 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "coverage"
version = "7.13.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" },
{ url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" },
{ url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" },
{ url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" },
{ url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" },
{ url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" },
{ url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" },
{ url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" },
{ url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" },
{ url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" },
{ url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" },
{ url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" },
{ url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" },
{ url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" },
{ url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" },
{ url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" },
{ url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" },
{ url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" },
{ url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" },
{ url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" },
{ url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" },
{ url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" },
{ url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" },
{ url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" },
{ url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" },
{ url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" },
{ url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" },
{ url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" },
{ url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" },
{ url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" },
{ url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" },
]
[[package]]
name = "cssselect"
version = "1.4.0"
@@ -212,6 +251,7 @@ dependencies = [
{ name = "lxml" },
{ name = "nltk" },
{ name = "platformdirs" },
{ name = "pytest-cov" },
{ name = "python-iso639" },
{ name = "requests" },
{ name = "tdewolff-minify" },
@@ -240,6 +280,7 @@ requires-dist = [
{ name = "lxml" },
{ name = "nltk" },
{ name = "platformdirs" },
{ name = "pytest-cov" },
{ name = "python-iso639" },
{ name = "requests" },
{ name = "tdewolff-minify" },
@@ -529,6 +570,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
]
[[package]]
name = "pytest-cov"
version = "7.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage" },
{ name = "pluggy" },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" },
]
[[package]]
name = "pytest-mock"
version = "3.15.1"