@@ -186,11 +186,21 @@ jobs:
186186 fi
187187
188188 # Test with base dependencies
189- # Exit code 5 = no tests collected (e.g. only CLI test files changed);
190- # treat that as success since CLI tests run in a separate workflow.
189+ # Exit code 5 = no tests collected (e.g. only CLI test files changed,
190+ # or no xdist_safe-marked tests in the changed set); treat that as
191+ # success since CLI tests run in a separate workflow and the xdist
192+ # step legitimately collects nothing when no marked modules changed.
191193 #
192- # TODO: xdist is disabled on Windows because several tests
193- # crash workers. Fix and re-enable. Failing tests:
194+ # xdist is opt-in: tests run serially by default. Modules opt in by
195+ # adding `pytestmark = pytest.mark.xdist_safe`. On Linux/macOS the
196+ # serial step excludes xdist_safe and a second step runs the marked
197+ # tests under -n auto. On Windows, xdist is disabled entirely and
198+ # the serial step runs every test (marked tests included).
199+ #
200+ # Note: The following tests crashed workers on Windows under xdist.
201+ # If/when any of their modules are opted into xdist_safe, re-verify
202+ # behavior on Windows (Windows will still run them serially, but the
203+ # underlying issues may resurface on Linux/macOS parallel runs):
194204 # - tests/_islands/test_island_generator.py::test_build
195205 # - tests/_islands/test_island_generator.py::test_render
196206 # - tests/_islands/test_island_generator.py::test_render_multiline_markdown
@@ -200,12 +210,29 @@ jobs:
200210 # - tests/_server/test_session_manager.py::test_create_session_absolute_url
201211 # - tests/_server/test_session_manager.py::test_create_session_with_script_config_overrides
202212 # - tests/_server/test_session_manager.py::test_recents_touch_called_on_session_create
203- - name : Test changed with base dependencies
213+ - name : Test changed with base dependencies (serial)
204214 if : ${{ matrix.dependencies == 'core' }}
205215 run : |
206216 uv run --python ${{ matrix.python-version }} --group test pytest tests/ \
207217 -v \
208- ${{ matrix.os != 'windows-latest' && '-n auto' || '-p no:xdist' }} \
218+ -p no:xdist \
219+ ${{ matrix.os != 'windows-latest' && '-m "not xdist_safe"' || '' }} \
220+ -k "not test_cli" \
221+ --durations=10 \
222+ -p packages.pytest_changed \
223+ --changed-from=${{ steps.setup-flags.outputs.changed_from }} \
224+ --include-unchanged=${{ steps.setup-flags.outputs.include_unchanged }} \
225+ --picked=first \
226+ --inline-snapshot=disable \
227+ || { ec=$?; [ $ec -eq 5 ] && exit 0 || exit $ec; }
228+
229+ - name : Test changed with base dependencies (xdist)
230+ if : ${{ matrix.dependencies == 'core' && matrix.os != 'windows-latest' }}
231+ run : |
232+ uv run --python ${{ matrix.python-version }} --group test pytest tests/ \
233+ -v \
234+ -n auto \
235+ -m xdist_safe \
209236 -k "not test_cli" \
210237 --durations=10 \
211238 -p packages.pytest_changed \
@@ -216,12 +243,29 @@ jobs:
216243 || { ec=$?; [ $ec -eq 5 ] && exit 0 || exit $ec; }
217244
218245 # Test with optional dependencies
219- - name : Test changed with optional dependencies
246+ - name : Test changed with optional dependencies (serial)
220247 if : ${{ matrix.dependencies == 'core,optional' }}
221248 run : |
222249 uv run --python ${{ matrix.python-version }} --group test-optional pytest tests/ \
223250 -v \
224- ${{ matrix.os != 'windows-latest' && '-n auto' || '-p no:xdist' }} \
251+ -p no:xdist \
252+ ${{ matrix.os != 'windows-latest' && '-m "not xdist_safe"' || '' }} \
253+ -k "not test_cli" \
254+ --durations=10 \
255+ -p packages.pytest_changed \
256+ --changed-from=${{ steps.setup-flags.outputs.changed_from }} \
257+ --include-unchanged=${{ steps.setup-flags.outputs.include_unchanged }} \
258+ --picked=first \
259+ --inline-snapshot=disable \
260+ || { ec=$?; [ $ec -eq 5 ] && exit 0 || exit $ec; }
261+
262+ - name : Test changed with optional dependencies (xdist)
263+ if : ${{ matrix.dependencies == 'core,optional' && matrix.os != 'windows-latest' }}
264+ run : |
265+ uv run --python ${{ matrix.python-version }} --group test-optional pytest tests/ \
266+ -v \
267+ -n auto \
268+ -m xdist_safe \
225269 -k "not test_cli" \
226270 --durations=10 \
227271 -p packages.pytest_changed \
@@ -234,12 +278,31 @@ jobs:
234278 # Test with minimal dependencies using lowest resolution
235279 # https://docs.astral.sh/uv/concepts/resolution/#lowest-resolution
236280 # https://docs.astral.sh/uv/reference/environment/#uv_resolution
237- - name : Test with minimal dependencies (lowest resolution)
281+ - name : Test with minimal dependencies (lowest resolution, serial )
238282 if : ${{ matrix.dependencies == 'minimal' }}
239283 run : |
240284 uv run --python ${{ matrix.python-version }} --group test pytest tests/ \
241285 -v \
242- ${{ matrix.os != 'windows-latest' && '-n auto' || '-p no:xdist' }} \
286+ -p no:xdist \
287+ ${{ matrix.os != 'windows-latest' && '-m "not xdist_safe"' || '' }} \
288+ -k "not test_cli" \
289+ --durations=10 \
290+ -p packages.pytest_changed \
291+ --changed-from=${{ steps.setup-flags.outputs.changed_from }} \
292+ --include-unchanged=${{ steps.setup-flags.outputs.include_unchanged }} \
293+ --picked=first \
294+ --inline-snapshot=disable \
295+ || { ec=$?; [ $ec -eq 5 ] && exit 0 || exit $ec; }
296+ env :
297+ UV_RESOLUTION : lowest-direct
298+
299+ - name : Test with minimal dependencies (lowest resolution, xdist)
300+ if : ${{ matrix.dependencies == 'minimal' && matrix.os != 'windows-latest' }}
301+ run : |
302+ uv run --python ${{ matrix.python-version }} --group test pytest tests/ \
303+ -v \
304+ -n auto \
305+ -m xdist_safe \
243306 -k "not test_cli" \
244307 --durations=10 \
245308 -p packages.pytest_changed \
0 commit comments