Skip to content

Commit f051c9f

Browse files
authored
v0.3.0 (#3)
1 parent 99363ca commit f051c9f

File tree

8 files changed

+197
-21
lines changed

8 files changed

+197
-21
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ jobs:
5151
- name: Install dependencies
5252
run: |
5353
uv sync
54-
uv pip install pytest=="${{matrix.pytest-version}}"
54+
uv pip install pytest~="${{matrix.pytest-version}}.0"
5555
- name: Test
56-
run: uv run pytest
56+
run: uv run --no-sync pytest
5757

5858
mypy:
5959
runs-on: ubuntu-latest

README.md

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# pytest-cdist
22

3-
Like pytest-xdist, but for distributed environments.
3+
Like pytest-xdist, but for distributed environments - pytest-cdist allows to split a
4+
test suite into multiple parts and run them independently.
45

56
**This is a work in progress**
67

@@ -13,8 +14,6 @@ large. pytest-cdist can help with this by allowing to execute individual chunks
1314
test suite in a deterministic order, so you can use multiple concurrent jobs to run each
1415
individual chunk.
1516

16-
The individual invocation can *still* make use of pytest-xdist.
17-
1817

1918
## How?
2019

@@ -105,5 +104,56 @@ When running under pytest-xdist, pytest-cdist will honour tests marked with
105104

106105
### With pytest-randomly
107106

108-
At the moment, pytest-cdist does not work out of the box with pytest randomly's test
109-
reordering, unless an explicit seed is passed
107+
To use pytest-cdist with
108+
[pytest-randomly](https://github.com/pytest-dev/pytest-randomly)'s test reordering, a
109+
randomness seed that's consistent across the different pytest
110+
invocations needs to be specified, otherwise, test cases would be dropped, since
111+
pytest-cdist has to rely on a consistent collection order to split test cases among its
112+
groups.
113+
114+
To achieve this, a random source such as the current timestamp can be used:
115+
116+
```yaml
117+
jobs:
118+
setup_randomly_seed:
119+
runs-on: ubuntu-latest
120+
outputs:
121+
randomly_seed: ${{ steps.set-seed.outputs.randomly_seed }}
122+
steps:
123+
- name: Set seed
124+
id: set-seed
125+
run: echo "randomly_seed=$(date +%s)" >> $GITHUB_OUTPUT
126+
127+
test:
128+
runs-on: ubuntu-latest
129+
needs: setup_randomly_seed
130+
matrix:
131+
strategy:
132+
cdist-groups: [1, 2, 3, 4]
133+
steps:
134+
- uses: actions/checkout@v4
135+
- name: Run pytest
136+
run: >
137+
pytest
138+
--cdist-group=${{ matrix.cdist-group }}/4
139+
--randomly-seed=${{ needs.setup_randomly_seeds.outputs.randomly_seed }}
140+
```
141+
142+
or a bit simpler (but less random), using the current git hash:
143+
144+
```yaml
145+
jobs:
146+
test:
147+
runs-on: ubuntu-latest
148+
needs: setup_randomly_seed
149+
matrix:
150+
strategy:
151+
cdist-groups: [1, 2, 3, 4]
152+
steps:
153+
- uses: actions/checkout@v4
154+
- name: Run pytest
155+
run: >
156+
pytest
157+
--cdist-group=${{ matrix.cdist-group }}/4
158+
--randomly-seed=$((16#$(git rev-parse HEAD)))
159+
```

pyproject.toml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[project]
22
name = "pytest-cdist"
3-
version = "0.2.0"
4-
description = "Add your description here"
3+
version = "0.3.0"
4+
description = "A pytest plugin to split your test suite into multiple parts"
5+
authors = [
6+
{ name = "Janek Nouvertné", email = "provinzkraut@posteo.de" },
7+
]
58
readme = "README.md"
69
requires-python = ">=3.9"
710
classifiers = [
@@ -32,6 +35,7 @@ dev = [
3235
"mypy>=1.14.0",
3336
"pre-commit>=4.0.1",
3437
"typing-extensions>=4.12.2",
38+
"pytest-randomly>=3.16.0",
3539
]
3640

3741
[project.entry-points.pytest11]
@@ -52,3 +56,7 @@ packages = ["pytest_cdist", "tests"]
5256

5357
[tool.ruff]
5458
target-version = "py39"
59+
60+
61+
[tool.pytest.ini_options]
62+
addopts = "-p no:randomly"

pytest_cdist/plugin.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ def from_pytest_config(cls, config: pytest.Config) -> CdistConfig | None:
167167
) or config.getini("cdist-justify-items")
168168

169169
group_steal = _get_group_steal_opt(
170-
config.getoption("cdist_group_steal") or config.getini("cdist-group-steal")
170+
config.getoption("cdist_group_steal")
171+
or config.getini("cdist-group-steal")
172+
or None # pytest<8 returns an empty string here
171173
)
172174

173175
current_group, total_groups = map(int, cdist_option.split("/"))
@@ -213,6 +215,17 @@ def pytest_addoption(parser: pytest.Parser) -> None:
213215
def pytest_configure(config: pytest.Config) -> None:
214216
cdist_config = CdistConfig.from_pytest_config(config)
215217
config.stash[_CDIST_CONFIG_KEY] = cdist_config
218+
if (
219+
config.pluginmanager.hasplugin("randomly")
220+
and config.getoption("randomly_seed") == "default"
221+
and config.getoption("randomly_reorganize")
222+
):
223+
raise pytest.UsageError(
224+
"pytest-cdist is incompatible with the current pytest-randomly "
225+
"configuration and will produce incorrect results. To use both of them "
226+
"together, either specify a randomness seed using '--randomly-seed=...' or "
227+
"disable test reorganization by passing '--randomly-dont-reorganize'."
228+
)
216229

217230

218231
def pytest_collection_modifyitems(

test.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

tests/conftest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
1+
import pytest
2+
13
pytest_plugins = ["pytester"]
4+
5+
6+
@pytest.fixture()
7+
def ini_tpl() -> str:
8+
return '[pytest]\naddopts="-p no:randomly"'
9+
10+
11+
@pytest.fixture(autouse=True)
12+
def make_ini(pytester: pytest.Pytester, ini_tpl: str) -> None:
13+
pytester.makeini(ini_tpl)

tests/test_plugin.py

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ def test_three(self):
8787
],
8888
)
8989
def test_justify_cli_ini_cfg(
90-
pytester: pytest.Pytester, cli_opt: str, ini_opt: str | None
90+
pytester: pytest.Pytester,
91+
cli_opt: str,
92+
ini_opt: str | None,
93+
ini_tpl: str,
9194
) -> None:
9295
pytester.makepyfile("""
9396
def test_one():
@@ -100,7 +103,7 @@ def test_three():
100103
assert True
101104
""")
102105
if ini_opt is not None:
103-
pytester.makeini(f"[pytest]\n{ini_opt}")
106+
pytester.makeini(ini_tpl + f"\n{ini_opt}")
104107

105108
result = pytester.runpytest("--cdist-group=1/2", cli_opt)
106109
result.assert_outcomes(passed=3)
@@ -179,7 +182,9 @@ def test_two():
179182
("--cdist-group-steal=2:50", "cdist-group-steal=2:50"),
180183
],
181184
)
182-
def test_steal(pytester: pytest.Pytester, cli_opt: str, ini_opt: str | None) -> None:
185+
def test_steal(
186+
pytester: pytest.Pytester, cli_opt: str, ini_opt: str | None, ini_tpl: str
187+
) -> None:
183188
pytester.makepyfile("""
184189
def test_one():
185190
assert True
@@ -195,10 +200,68 @@ def test_four():
195200
""")
196201

197202
if ini_opt is not None:
198-
pytester.makeini(f"[pytest]\n{ini_opt}")
203+
pytester.makeini(ini_tpl + f"\n{ini_opt}")
199204

200205
result = pytester.runpytest("--cdist-group=1/2", cli_opt)
201206
result.assert_outcomes(passed=1, deselected=3)
202207

203208
result = pytester.runpytest("--cdist-group=2/2", cli_opt)
204209
result.assert_outcomes(passed=3, deselected=1)
210+
211+
212+
def test_raises_for_invalid_randomly_cfg(pytester: pytest.Pytester) -> None:
213+
pytester.makeini("")
214+
215+
result = pytester.runpytest("--cdist-group=1/2")
216+
assert (
217+
"pytest-cdist is incompatible with the current pytest-randomly configuration"
218+
in result.stderr.str()
219+
)
220+
221+
222+
@pytest.mark.parametrize("i", range(10))
223+
def test_randomly_with_seed(pytester: pytest.Pytester, i: int) -> None:
224+
pytester.makeini('[pytest]\naddopts="-p randomly"')
225+
226+
pytester.makepyfile("""
227+
def test_one():
228+
assert True
229+
230+
def test_two():
231+
assert True
232+
233+
def test_three():
234+
assert False
235+
""")
236+
237+
result = pytester.runpytest_subprocess("--cdist-group=1/2", "--randomly-seed=123")
238+
result.assert_outcomes(passed=1, failed=1, deselected=1)
239+
240+
result = pytester.runpytest_subprocess("--cdist-group=2/2", "--randomly-seed=123")
241+
result.assert_outcomes(passed=1, failed=0, deselected=2)
242+
243+
244+
@pytest.mark.parametrize("i", range(10))
245+
def test_randomly_with_dont_reorganize(pytester: pytest.Pytester, i: int) -> None:
246+
pytester.makeini('[pytest]\naddopts="-p randomly"')
247+
248+
pytester.makepyfile("""
249+
def test_one():
250+
assert True
251+
252+
def test_two():
253+
assert True
254+
255+
def test_three():
256+
assert False
257+
""")
258+
259+
result = pytester.runpytest_subprocess(
260+
"--cdist-group=1/2", "--randomly-dont-reorganize"
261+
)
262+
result.assert_outcomes(passed=2, failed=0, deselected=1)
263+
264+
result = pytester.runpytest_subprocess(
265+
"--cdist-group=2/2", "--randomly-dont-reorganize"
266+
)
267+
result.assert_outcomes(passed=0, failed=1, deselected=2)

uv.lock

Lines changed: 37 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)