From 1828ebdde4f371bf47e77cd97f095ec8d8c2edd7 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Sun, 5 Apr 2026 18:55:41 -0400 Subject: [PATCH 01/10] Speed up test suite with pytest-xdist parallelization --- pyproject.toml | 6 +++--- tests/test_api/test_synchronous.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 96932a9611..d1b8b4cc37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -172,14 +172,14 @@ matrix.deps.dependency-groups = [ [tool.hatch.envs.test.scripts] run-coverage = [ - "coverage run --source=src -m pytest --junitxml=junit.xml -o junit_family=legacy {args:}", + "coverage run --source=src -m pytest -n auto --junitxml=junit.xml -o junit_family=legacy {args:}", "coverage xml", ] run-coverage-html = [ - "coverage run --source=src -m pytest {args:}", + "coverage run --source=src -m pytest -n auto {args:}", "coverage html", ] -run = "pytest --ignore tests/benchmarks" +run = "pytest -n auto --ignore tests/benchmarks {args:}" run-verbose = "run-coverage --verbose" run-mypy = "mypy src" run-hypothesis = [ diff --git a/tests/test_api/test_synchronous.py b/tests/test_api/test_synchronous.py index d6ae61f1ca..1d5374aab3 100644 --- a/tests/test_api/test_synchronous.py +++ b/tests/test_api/test_synchronous.py @@ -93,7 +93,7 @@ def test_docstrings_match(callable_name: str) -> None: ), ), ], - ids=str, + ids=["store_path-create_array_group", "store_path-create", "array_params-create_array_dataset"], ) def test_docstring_consistent_parameters( parameter_name: str, array_creation_routines: tuple[Callable[[Any], Any], ...] From d29794ff2d18f853c1267b128d137f387c4418b6 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:14:35 -0400 Subject: [PATCH 02/10] Group Fsspec S3 tests --- tests/test_store/test_fsspec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 5e9e33f0e4..690aa63be0 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -133,7 +133,8 @@ async def test_basic() -> None: assert out[0].to_bytes() == data[1:] -class TestFsspecStoreS3(StoreTests[FsspecStore, cpu.Buffer]): +@pytest.mark.xdist_group(name="s3") +class TestFsspecStoreS3(StoreTests[FsspecStore, cpu.Buffer]): # type: ignore[misc] store_cls = FsspecStore buffer_cls = cpu.Buffer From 51ff38768b100a25d39c3eef85e2771d87eed25c Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:24:46 -0400 Subject: [PATCH 03/10] Lint --- tests/test_store/test_fsspec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 690aa63be0..9f7ab50f63 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -134,7 +134,7 @@ async def test_basic() -> None: @pytest.mark.xdist_group(name="s3") -class TestFsspecStoreS3(StoreTests[FsspecStore, cpu.Buffer]): # type: ignore[misc] +class TestFsspecStoreS3(StoreTests[FsspecStore, cpu.Buffer]): store_cls = FsspecStore buffer_cls = cpu.Buffer From dcb4c56b0492de9560235bddfc6d7a7dfe517ff6 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Sun, 5 Apr 2026 20:00:54 -0400 Subject: [PATCH 04/10] Group at module level --- tests/test_store/test_fsspec.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_store/test_fsspec.py b/tests/test_store/test_fsspec.py index 9f7ab50f63..51276045f7 100644 --- a/tests/test_store/test_fsspec.py +++ b/tests/test_store/test_fsspec.py @@ -44,6 +44,9 @@ pytest.mark.filterwarnings( "ignore:Exception ignored in.*finalize object.*:pytest.PytestUnraisableExceptionWarning" ), + # All S3 tests share a module-scoped moto server on a fixed port, + # so they must run on a single xdist worker to avoid port conflicts. + pytest.mark.xdist_group(name="s3"), ] fsspec = pytest.importorskip("fsspec") @@ -133,7 +136,6 @@ async def test_basic() -> None: assert out[0].to_bytes() == data[1:] -@pytest.mark.xdist_group(name="s3") class TestFsspecStoreS3(StoreTests[FsspecStore, cpu.Buffer]): store_cls = FsspecStore buffer_cls = cpu.Buffer From 5b001512525fba193acd5dcddb3f4c8e72fc9925 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:47:54 -0400 Subject: [PATCH 05/10] Set dist loadgroup --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d1b8b4cc37..b9890dd358 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -413,6 +413,7 @@ addopts = [ "--benchmark-disable", # benchmark routines run as tests without benchmarking instrumentation "--durations", "10", "-ra", "--strict-config", "--strict-markers", + "--dist", "loadgroup", ] filterwarnings = [ "error", From 1f0e2e794309fe3c27eddb203ef0dc38ff99cd54 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:57:30 -0400 Subject: [PATCH 06/10] Parallel coverage --- pyproject.toml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6cdcb3cce8..4dc641b133 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -141,6 +141,8 @@ exclude_also = [ ] [tool.coverage.run] +parallel = true +source = ["src"] omit = [ "bench/compress_normal.py", "src/zarr/testing/conftest.py", # only for downstream projects @@ -173,18 +175,21 @@ matrix.deps.dependency-groups = [ [tool.hatch.envs.test.scripts] run-coverage = [ - "coverage run --source=src -m pytest -n auto --ignore tests/benchmarks --junitxml=junit.xml -o junit_family=legacy {args:}", + "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --ignore tests/benchmarks --junitxml=junit.xml -o junit_family=legacy {args:}", + "coverage combine", "coverage xml", ] run-coverage-html = [ - "coverage run --source=src -m pytest -n auto --ignore tests/benchmarks {args:}", + "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --ignore tests/benchmarks {args:}", + "coverage combine", "coverage html", ] run = "pytest -n auto --ignore tests/benchmarks {args:}" run-verbose = "run-coverage --verbose" run-mypy = "mypy src" run-hypothesis = [ - "coverage run --source=src -m pytest -nauto --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful* {args:}", + "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -nauto --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful* {args:}", + "coverage combine", "coverage xml", ] run-benchmark = "pytest --benchmark-enable tests/benchmarks" From 73ce566665923891b82cf88631e2ef21992a5155 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:01:35 -0400 Subject: [PATCH 07/10] Don't use xdist in benchmark smoketests --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4dc641b133..d8d13acb63 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -192,7 +192,7 @@ run-hypothesis = [ "coverage combine", "coverage xml", ] -run-benchmark = "pytest --benchmark-enable tests/benchmarks" +run-benchmark = "pytest -p no:xdist --benchmark-enable tests/benchmarks" serve-coverage-html = "python -m http.server -d htmlcov 8000" list-env = "pip list" From d2a326c853d572ca37514454e7af5acf36b6d262 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:13:04 -0400 Subject: [PATCH 08/10] Fix gpu workflow --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d8d13acb63..cf2eea3cc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -208,7 +208,8 @@ python = ["3.12", "3.13"] [tool.hatch.envs.gputest.scripts] run-coverage = [ - "coverage run --source=src -m pytest -m gpu --junitxml=junit.xml -o junit_family=legacy --ignore tests/benchmarks {args:}", + "coverage run -m pytest -m gpu --junitxml=junit.xml -o junit_family=legacy --ignore tests/benchmarks {args:}", + "coverage combine", "coverage xml", ] run = "pytest -m gpu --ignore tests/benchmarks" From 68156a061192f75de1b28b9e98bb3122c5509d5a Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:28:11 -0400 Subject: [PATCH 09/10] fixup --- pyproject.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cf2eea3cc5..33b4324a9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -175,24 +175,24 @@ matrix.deps.dependency-groups = [ [tool.hatch.envs.test.scripts] run-coverage = [ - "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --ignore tests/benchmarks --junitxml=junit.xml -o junit_family=legacy {args:}", + "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --dist loadgroup --ignore tests/benchmarks --junitxml=junit.xml -o junit_family=legacy {args:}", "coverage combine", "coverage xml", ] run-coverage-html = [ - "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --ignore tests/benchmarks {args:}", + "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --dist loadgroup --ignore tests/benchmarks {args:}", "coverage combine", "coverage html", ] -run = "pytest -n auto --ignore tests/benchmarks {args:}" +run = "pytest -n auto --dist loadgroup --ignore tests/benchmarks {args:}" run-verbose = "run-coverage --verbose" run-mypy = "mypy src" run-hypothesis = [ - "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -nauto --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful* {args:}", + "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -nauto --dist loadgroup --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful* {args:}", "coverage combine", "coverage xml", ] -run-benchmark = "pytest -p no:xdist --benchmark-enable tests/benchmarks" +run-benchmark = "pytest --benchmark-enable tests/benchmarks" serve-coverage-html = "python -m http.server -d htmlcov 8000" list-env = "pip list" @@ -420,7 +420,6 @@ addopts = [ "--benchmark-disable", # benchmark routines run as tests without benchmarking instrumentation "--durations", "10", "-ra", "--strict-config", "--strict-markers", - "--dist", "loadgroup", ] filterwarnings = [ "error", From 7dc265b83e48da4fa8751d75eb731995f2873090 Mon Sep 17 00:00:00 2001 From: Max Jones <14077947+maxrjones@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:39:17 -0400 Subject: [PATCH 10/10] Fix env var --- pyproject.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 33b4324a9a..08da4c2f1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,6 +158,7 @@ hooks.vcs.version-file = "src/zarr/_version.py" dependency-groups = ["test"] [tool.hatch.envs.test.env-vars] +COVERAGE_PROCESS_START = "pyproject.toml" [[tool.hatch.envs.test.matrix]] python = ["3.12", "3.13", "3.14"] @@ -175,12 +176,12 @@ matrix.deps.dependency-groups = [ [tool.hatch.envs.test.scripts] run-coverage = [ - "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --dist loadgroup --ignore tests/benchmarks --junitxml=junit.xml -o junit_family=legacy {args:}", + "coverage run -m pytest -n auto --dist loadgroup --ignore tests/benchmarks --junitxml=junit.xml -o junit_family=legacy {args:}", "coverage combine", "coverage xml", ] run-coverage-html = [ - "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -n auto --dist loadgroup --ignore tests/benchmarks {args:}", + "coverage run -m pytest -n auto --dist loadgroup --ignore tests/benchmarks {args:}", "coverage combine", "coverage html", ] @@ -188,7 +189,7 @@ run = "pytest -n auto --dist loadgroup --ignore tests/benchmarks {args:}" run-verbose = "run-coverage --verbose" run-mypy = "mypy src" run-hypothesis = [ - "COVERAGE_PROCESS_START=pyproject.toml coverage run -m pytest -nauto --dist loadgroup --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful* {args:}", + "coverage run -m pytest -nauto --dist loadgroup --run-slow-hypothesis tests/test_properties.py tests/test_store/test_stateful* {args:}", "coverage combine", "coverage xml", ]