Skip to content

Commit 5694c85

Browse files
sylwiaszunejkodkropachev
authored andcommitted
CI: turn silent unit-test skips into failures
Tests skip themselves when their requirements are missing (a library is absent, the wrong event loop is selected, the C extensions aren't built). That is convenient locally but a footgun in CI, where a test may be silently skipped because a dependency was not installed. The libev unit tests were effectively not running in any CI configuration. Add a CASS_DRIVER_NO_SKIP-gated pytest hook in tests/conftest.py that turns skips into failures (xfail untouched), and enable it in the cibuildwheel test-commands where the C extensions are mandatory (Linux/macOS). Tests that genuinely cannot run in the default configuration are listed explicitly via -k/--ignore (reactor tests run separately per EVENT_LOOP_MANAGER; asyncore, column_encryption and a few upstream-disabled/flaky tests excluded). Add -v to every pytest invocation and install the compress-lz4 extra so the lz4 tests actually run. Pass --import-mode=append in the cibuildwheel pytest commands so the installed compiled wheel takes precedence on sys.path over the in-tree pure-Python cassandra source during wheel tests. Keep the global pytest addopts unchanged so local pytest runs keep their normal import behavior. Windows and PyPy keep no-skip off: the extensions are optional on Windows and are never built on PyPy (setup.py forces is_pypy to skip libev/cmurmur3/Cython), so their extension-dependent skips are legitimate. The PyPy override also drops the compress-lz4 extra (no prebuilt PyPy lz4 wheel), uses cross-shell quoting, and deselects the known PyPy/Windows/macOS-incompatible tests.
1 parent 721eacf commit 5694c85

3 files changed

Lines changed: 90 additions & 5 deletions

File tree

.github/workflows/integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,4 @@ jobs:
9898
if [[ "${{ matrix.python-version }}" =~ t$ ]]; then
9999
export PYTHON_GIL=0
100100
fi
101-
uv run pytest tests/integration/standard/ tests/integration/cqlengine/
101+
uv run pytest -v tests/integration/standard/ tests/integration/cqlengine/

pyproject.toml

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,22 +158,71 @@ enable = ["pypy"]
158158
[tool.cibuildwheel.linux]
159159

160160
before-build = "rm -rf ~/.pyxbld && rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux && yum install -y libffi-devel libev libev-devel openssl openssl-devel"
161+
# Install the optional lz4 compression dependency so the lz4 segment tests run
162+
# (and fail loudly under CASS_DRIVER_NO_SKIP) instead of skipping silently.
163+
test-extras = ["compress-lz4"]
164+
# Extensions are mandatory on Linux (CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST=yes),
165+
# so skipping is disabled (CASS_DRIVER_NO_SKIP=1): a missing dependency such as
166+
# libev fails loudly instead of being silently skipped. Tests that cannot run in
167+
# the default configuration are listed explicitly:
168+
# * event-loop reactor tests are run separately with the matching
169+
# EVENT_LOOP_MANAGER (gevent/eventlet/asyncio);
170+
# * asyncore is deprecated and unavailable on modern Python, so it is ignored;
171+
# * column_encryption is disabled upstream (scylladb/python-driver#365);
172+
# * test_deserialize_date_range_month is disabled upstream (PYTHON-912).
173+
# PyPy uses the pp* override below. All Linux CPython reactor commands run with
174+
# CASS_DRIVER_NO_SKIP=1 so unexpected skips fail loudly.
161175
test-command = [
162-
"pytest {package}/tests/unit",
163-
"EVENT_LOOP_MANAGER=gevent pytest {package}/tests/unit/io/test_geventreactor.py",
176+
"CASS_DRIVER_NO_SKIP=1 pytest --import-mode=append {package}/tests/unit -v --ignore={package}/tests/unit/column_encryption --ignore={package}/tests/unit/io/test_geventreactor.py --ignore={package}/tests/unit/io/test_eventletreactor.py --ignore={package}/tests/unit/io/test_asyncioreactor.py --ignore={package}/tests/unit/io/test_asyncorereactor.py -k 'not test_deserialize_date_range_month'",
177+
"EVENT_LOOP_MANAGER=gevent CASS_DRIVER_NO_SKIP=1 pytest --import-mode=append {package}/tests/unit/io/test_geventreactor.py -v",
178+
"EVENT_LOOP_MANAGER=asyncio CASS_DRIVER_NO_SKIP=1 pytest --import-mode=append {package}/tests/unit/io/test_asyncioreactor.py -v",
179+
"EVENT_LOOP_MANAGER=eventlet CASS_DRIVER_NO_SKIP=1 pytest --import-mode=append {package}/tests/unit/io/test_eventletreactor.py -v",
164180
]
165181

166182
[tool.cibuildwheel.macos]
167183
build-frontend = "build"
184+
# Install lz4 so the lz4 segment tests run instead of skipping (see Linux note).
185+
test-extras = ["compress-lz4"]
186+
# Same policy as Linux (extensions are mandatory here too, libev comes from
187+
# Homebrew). The extra -k exclusions are timing-sensitive tests that are flaky
188+
# on macOS runners. The gevent/eventlet/asyncio reactor test files only contain
189+
# those timing-sensitive timer tests, so they are not run separately here.
168190
test-command = [
169-
"pytest {project}/tests/unit -k 'not (test_multi_timer_validation or test_empty_connections or test_timer_cancellation)'",
191+
"CASS_DRIVER_NO_SKIP=1 pytest --import-mode=append {project}/tests/unit -v --ignore={project}/tests/unit/column_encryption --ignore={project}/tests/unit/io/test_geventreactor.py --ignore={project}/tests/unit/io/test_eventletreactor.py --ignore={project}/tests/unit/io/test_asyncioreactor.py --ignore={project}/tests/unit/io/test_asyncorereactor.py -k 'not (test_multi_timer_validation or test_empty_connections or test_timer_cancellation or test_deserialize_date_range_month)'",
170192
]
171193

172194
[tool.cibuildwheel.windows]
173195
build-frontend = "build"
196+
# On Windows the C extensions are optional (CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST
197+
# is overridden to "no" below), so extension-dependent tests (e.g. libev) are
198+
# legitimately skipped here. CASS_DRIVER_NO_SKIP is therefore NOT enabled on
199+
# Windows; we only add -v so skips are visible in the log.
174200
test-command = [
175-
"pytest {project}/tests/unit -k \"not (test_deserialize_date_range_year or test_datetype or test_libevreactor)\"",
201+
"pytest --import-mode=append {project}/tests/unit -v -k \"not (test_deserialize_date_range_year or test_datetype or test_libevreactor)\"",
176202
]
177203

178204
# TODO: set CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST to yes when https://github.com/scylladb/python-driver/issues/429 is fixed
179205
environment = { CASS_DRIVER_BUILD_CONCURRENCY = "2", CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST = "no" }
206+
207+
# PyPy never builds the libev/cmurmur3/Cython C extensions (setup.py forces
208+
# is_pypy to skip them even when CASS_DRIVER_BUILD_EXTENSIONS_ARE_MUST=yes), so
209+
# the tests that depend on those extensions legitimately skip. Enforcing
210+
# CASS_DRIVER_NO_SKIP would turn those expected skips into failures, so it is
211+
# NOT enabled for PyPy (same reasoning as Windows). The reactor tests are not
212+
# run separately here because eventlet is unsupported on PyPy (@notpypy) and the
213+
# extension-backed reactors are unavailable; with no-skip off they simply skip.
214+
# test-extras is cleared (no compress-lz4): PyPy has no prebuilt lz4 wheel, so
215+
# pip would try to compile it from source and fail. The lz4 tests just skip here.
216+
# test_deserialize_date_range_year and test_datetype are excluded because they
217+
# fail on Windows (the C runtime's gmtime rejects the far-future timestamps they
218+
# use); the CPython Windows command excludes them for the same reason. The
219+
# timer tests (test_multi_timer_validation, test_empty_connections,
220+
# test_timer_cancellation) are timing-sensitive and flaky on macOS, matching the
221+
# CPython macOS exclusions. The override matches PyPy on all OSes, so these are
222+
# deselected everywhere here (they are still covered by the CPython runs).
223+
[[tool.cibuildwheel.overrides]]
224+
select = "pp*"
225+
test-extras = []
226+
test-command = [
227+
"pytest --import-mode=append {package}/tests/unit -v --ignore={package}/tests/unit/column_encryption -k \"not (test_deserialize_date_range_month or test_deserialize_date_range_year or test_datetype or test_multi_timer_validation or test_empty_connections or test_timer_cancellation)\"",
228+
]

tests/conftest.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,45 @@
1616
import os
1717
import warnings
1818

19+
import pytest
20+
1921
# Directory containing the Cython-compiled driver modules.
2022
_CASSANDRA_DIR = os.path.join(os.path.dirname(__file__), os.pardir, "cassandra")
2123

24+
# When set (e.g. in CI) a skipped test is turned into a failure. Tests skip
25+
# themselves when their requirements are missing (a library is not installed,
26+
# the wrong event loop is selected, ...). That is convenient locally, but in CI
27+
# it is a footgun: a test may be silently skipped because we forgot to install
28+
# something. Enabling this forces every skip to be explicit on the command line
29+
# (via -k / --ignore / --deselect) instead of being hidden in the output.
30+
_NO_SKIP = bool(os.environ.get("CASS_DRIVER_NO_SKIP"))
31+
32+
33+
@pytest.hookimpl(hookwrapper=True)
34+
def pytest_runtest_makereport(item, call):
35+
"""Turn skips into failures when CASS_DRIVER_NO_SKIP is set.
36+
37+
xfailed tests (which are reported as skipped) are left untouched so that
38+
``xfail_strict`` keeps working as configured.
39+
"""
40+
outcome = yield
41+
if not _NO_SKIP:
42+
return
43+
report = outcome.get_result()
44+
if report.skipped and not hasattr(report, "wasxfail"):
45+
reason = ""
46+
if isinstance(report.longrepr, tuple) and len(report.longrepr) == 3:
47+
reason = report.longrepr[2]
48+
elif report.longrepr:
49+
reason = str(report.longrepr)
50+
report.outcome = "failed"
51+
report.longrepr = (
52+
"Test was skipped but skipping is disabled in this environment "
53+
"(CASS_DRIVER_NO_SKIP is set). Run it in a suitable configuration "
54+
"or deselect it explicitly on the command line. "
55+
"Original skip reason: {!r}".format(reason)
56+
)
57+
2258

2359
def pytest_configure(config):
2460
"""Warn when a compiled Cython extension is older than its .py source.

0 commit comments

Comments
 (0)