Skip to content

Commit dedb392

Browse files
larsonerdrammock
andauthored
MAINT: Ensure limited set of tests are skipped (#13053)
Co-authored-by: Daniel McCloy <dan@mccloy.info>
1 parent 47ea360 commit dedb392

18 files changed

+129
-77
lines changed

.github/workflows/tests.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
with:
9494
qt: true
9595
pyvista: false
96+
wm: false
9697
# Python (if pip)
9798
- uses: actions/setup-python@v5
9899
with:
@@ -115,13 +116,13 @@ jobs:
115116
create-args: >-
116117
python=${{ env.PYTHON_VERSION }}
117118
if: ${{ !startswith(matrix.kind, 'pip') }}
118-
- run: ./tools/github_actions_dependencies.sh
119+
- run: bash ./tools/github_actions_dependencies.sh
119120
# Minimal commands on Linux (macOS stalls)
120-
- run: ./tools/get_minimal_commands.sh
121+
- run: bash ./tools/get_minimal_commands.sh
121122
if: startswith(matrix.os, 'ubuntu') && matrix.kind != 'minimal' && matrix.kind != 'old'
122-
- run: ./tools/github_actions_infos.sh
123+
- run: bash ./tools/github_actions_infos.sh
123124
# Check Qt
124-
- run: ./tools/check_qt_import.sh $MNE_QT_BACKEND
125+
- run: bash ./tools/check_qt_import.sh $MNE_QT_BACKEND
125126
if: env.MNE_QT_BACKEND != ''
126127
- name: Run tests with no testing data
127128
run: MNE_SKIP_TESTING_DATASET_TESTS=true pytest -m "not (ultraslowtest or pgtest)" --tb=short --cov=mne --cov-report xml -vv -rfE mne/
@@ -131,8 +132,8 @@ jobs:
131132
with:
132133
key: ${{ env.TESTING_VERSION }}
133134
path: ~/mne_data
134-
- run: ./tools/github_actions_download.sh
135-
- run: ./tools/github_actions_test.sh
135+
- run: bash ./tools/github_actions_download.sh
136+
- run: bash ./tools/github_actions_test.sh # for some reason on macOS we need to run "bash X" in order for a failed test run to show up
136137
- uses: codecov/codecov-action@v5
137138
with:
138139
token: ${{ secrets.CODECOV_TOKEN }}

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ repos:
7070
name: Copy dependency changes from pyproject.toml to environment.yml
7171
language: python
7272
entry: ./tools/hooks/update_environment_file.py
73-
files: pyproject.toml
73+
files: '^(pyproject.toml|tools/hooks/update_environment_file.py)$'
7474
- repo: local
7575
hooks:
7676
- id: dependency-sync
7777
name: Copy core dependencies from pyproject.toml to README.rst
7878
language: python
7979
entry: ./tools/hooks/sync_dependencies.py
80-
files: pyproject.toml
80+
files: '^(pyproject.toml|tools/hooks/sync_dependencies.py)$'
8181
additional_dependencies: ["mne==1.9.0"]
8282

8383
# zizmor

azure-pipelines.yml

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ stages:
8888
variables:
8989
DISPLAY: ':99'
9090
OPENBLAS_NUM_THREADS: '1'
91+
MNE_TEST_ALLOW_SKIP: '^.*(PySide6 causes segfaults).*$'
9192
steps:
9293
- bash: |
9394
set -e
@@ -111,7 +112,7 @@ stages:
111112
- bash: |
112113
set -e
113114
python -m pip install --progress-bar off --upgrade pip
114-
python -m pip install --progress-bar off "mne-qt-browser[opengl] @ git+https://github.com/mne-tools/mne-qt-browser.git@main" pyvista scikit-learn pytest-error-for-skips python-picard qtpy nibabel sphinx-gallery "PySide6!=6.8.0,!=6.8.0.1"
115+
python -m pip install --progress-bar off "mne-qt-browser[opengl] @ git+https://github.com/mne-tools/mne-qt-browser.git@main" pyvista scikit-learn python-picard qtpy nibabel sphinx-gallery "PySide6!=6.8.0,!=6.8.0.1" pandas neo pymatreader antio defusedxml
115116
python -m pip uninstall -yq mne
116117
python -m pip install --progress-bar off --upgrade -e .[test]
117118
displayName: 'Install dependencies with pip'
@@ -132,7 +133,7 @@ stages:
132133
displayName: 'Cache testing data'
133134
- script: python -c "import mne; mne.datasets.testing.data_path(verbose=True)"
134135
displayName: 'Get test data'
135-
- script: pytest --error-for-skips -m "ultraslowtest or pgtest" --tb=short --cov=mne --cov-report=xml --cov-report=html -vv mne
136+
- script: pytest -m "ultraslowtest or pgtest" --tb=short --cov=mne --cov-report=xml -vv mne
136137
displayName: 'slow and mne-qt-browser tests'
137138
# Coverage
138139
- bash: bash <(curl -s https://codecov.io/bash)
@@ -144,19 +145,18 @@ stages:
144145
testRunTitle: 'Publish test results for $(Agent.JobName)'
145146
failTaskOnFailedTests: true
146147
condition: succeededOrFailed()
147-
- task: PublishCodeCoverageResults@1
148+
- task: PublishCodeCoverageResults@2
148149
inputs:
149-
codeCoverageTool: Cobertura
150150
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
151-
reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov'
152151

153152
- job: Qt
154153
pool:
155154
vmImage: 'ubuntu-22.04'
156155
variables:
157156
DISPLAY: ':99'
158157
OPENBLAS_NUM_THREADS: '1'
159-
TEST_OPTIONS: "--tb=short --cov=mne --cov-report=xml --cov-report=html --cov-append -vv mne/viz/_brain mne/viz/backends mne/viz/tests/test_evoked.py mne/gui mne/report"
158+
TEST_OPTIONS: "--tb=short --cov=mne --cov-report=xml --cov-append -vv mne/viz/_brain mne/viz/backends mne/viz/tests/test_evoked.py mne/gui mne/report"
159+
MNE_TEST_ALLOW_SKIP: '^.*(PySide6 causes segfaults).*$'
160160
steps:
161161
- bash: ./tools/setup_xvfb.sh
162162
displayName: 'Install Ubuntu dependencies'
@@ -192,6 +192,7 @@ stages:
192192
set -eo pipefail
193193
python -m pip install PyQt6
194194
LD_DEBUG=libs python -c "from PyQt6.QtWidgets import QApplication, QWidget; app = QApplication([]); import matplotlib; matplotlib.use('QtAgg'); import matplotlib.pyplot as plt; plt.figure()"
195+
displayName: 'Check Qt import'
195196
- bash: |
196197
set -eo pipefail
197198
mne sys_info -pd
@@ -226,11 +227,9 @@ stages:
226227
testRunTitle: 'Publish test results for $(Agent.JobName)'
227228
failTaskOnFailedTests: true
228229
condition: succeededOrFailed()
229-
- task: PublishCodeCoverageResults@1
230+
- task: PublishCodeCoverageResults@2
230231
inputs:
231-
codeCoverageTool: Cobertura
232232
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
233-
reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov'
234233

235234
- job: Windows
236235
pool:
@@ -285,7 +284,7 @@ stages:
285284
displayName: 'Cache testing data'
286285
- script: python -c "import mne; mne.datasets.testing.data_path(verbose=True)"
287286
displayName: 'Get test data'
288-
- script: pytest -m "not (slowtest or pgtest)" --tb=short --cov=mne --cov-report=xml --cov-report=html -vv mne
287+
- script: pytest -m "not (slowtest or pgtest)" --tb=short --cov=mne --cov-report=xml -vv mne
289288
displayName: 'Run tests'
290289
- bash: bash <(curl -s https://codecov.io/bash)
291290
displayName: 'Codecov'
@@ -296,8 +295,6 @@ stages:
296295
testRunTitle: 'Publish test results for $(Agent.JobName) $(TEST_MODE) $(PYTHON_VERSION)'
297296
failTaskOnFailedTests: true
298297
condition: succeededOrFailed()
299-
- task: PublishCodeCoverageResults@1
298+
- task: PublishCodeCoverageResults@2
300299
inputs:
301-
codeCoverageTool: Cobertura
302300
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
303-
reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov'

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ dependencies:
5959
- trame
6060
- trame-vtk
6161
- trame-vuetify
62-
- vtk >=9.2
62+
- vtk =9.3.1=qt_*
6363
- xlrd

mne/conftest.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import inspect
77
import os
88
import os.path as op
9+
import re
910
import shutil
1011
import sys
1112
import warnings
@@ -79,7 +80,7 @@
7980
collect_ignore = ["export/_brainvision.py", "export/_eeglab.py", "export/_edf.py"]
8081

8182

82-
def pytest_configure(config):
83+
def pytest_configure(config: pytest.Config):
8384
"""Configure pytest options."""
8485
# Markers
8586
for marker in (
@@ -650,6 +651,11 @@ def _check_skip_backend(name):
650651
pytest.skip("Test skipped, requires Qt.")
651652
else:
652653
assert name == "notebook", name
654+
pytest.importorskip("jupyter")
655+
pytest.importorskip("ipympl")
656+
pytest.importorskip("trame")
657+
pytest.importorskip("trame_vtk")
658+
pytest.importorskip("trame_vuetify")
653659
if not _notebook_vtk_works():
654660
pytest.skip("Test skipped, requires working notebook vtk")
655661

@@ -1178,10 +1184,55 @@ def qt_windows_closed(request):
11781184

11791185
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
11801186
def pytest_runtest_makereport(item, call):
1181-
"""Stash the status of each item."""
1187+
"""Stash the status of each item and turn unexpected skips into errors."""
11821188
outcome = yield
1183-
rep = outcome.get_result()
1189+
rep: pytest.TestReport = outcome.get_result()
11841190
item.stash.setdefault(_phase_report_key, {})[rep.when] = rep
1191+
_modify_report_skips(rep)
1192+
return rep
1193+
1194+
1195+
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
1196+
def pytest_make_collect_report(collector: pytest.Collector):
1197+
"""Turn unexpected skips during collection (e.g., module-level) into errors."""
1198+
outcome = yield
1199+
rep: pytest.CollectReport = outcome.get_result()
1200+
_modify_report_skips(rep)
1201+
return rep
1202+
1203+
1204+
# Default means "allow all skips". Can use something like "$." to mean
1205+
# "never match", i.e., "treat all skips as errors"
1206+
_valid_skips_re = re.compile(os.getenv("MNE_TEST_ALLOW_SKIP", ".*"))
1207+
1208+
1209+
# To turn unexpected skips into errors, we need to look both at the collection phase
1210+
# (for decorated tests) and the call phase (for things like `importorskip`
1211+
# within the test body). code adapted from pytest-error-for-skips
1212+
def _modify_report_skips(report: pytest.TestReport | pytest.CollectReport):
1213+
if not report.skipped:
1214+
return
1215+
if isinstance(report.longrepr, tuple):
1216+
file, lineno, reason = report.longrepr
1217+
else:
1218+
file, lineno, reason = "<unknown>", 1, str(report.longrepr)
1219+
if _valid_skips_re.match(reason):
1220+
return
1221+
assert isinstance(report, pytest.TestReport | pytest.CollectReport), type(report)
1222+
if file.endswith("doctest.py"): # _python/doctest.py
1223+
return
1224+
# xfail tests aren't true "skips" but show up as skipped in reports
1225+
if getattr(report, "keywords", {}).get("xfail", False):
1226+
return
1227+
# the above only catches marks, so we need to actually parse the report to catch
1228+
# an xfail based on the traceback
1229+
if " pytest.xfail( " in reason:
1230+
return
1231+
if reason.startswith("Skipped: "):
1232+
reason = reason[9:]
1233+
report.longrepr = f"{file}:{lineno}: UNEXPECTED SKIP: {reason}"
1234+
# Make it show up as an error in the report
1235+
report.outcome = "error" if isinstance(report, pytest.TestReport) else "failed"
11851236

11861237

11871238
@pytest.fixture(scope="function")

mne/decoding/tests/test_ssd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,5 +474,5 @@ def test_non_full_rank_data():
474474

475475
ssd = SSD(info, filt_params_signal, filt_params_noise)
476476
if sys.platform == "darwin":
477-
pytest.skip("Unknown linalg bug (Accelerate?)")
477+
pytest.xfail("Unknown linalg bug (Accelerate?)")
478478
ssd.fit(X)

mne/tests/test_parallel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_parallel_func(n_jobs):
2626
"""Test Parallel wrapping."""
2727
joblib = pytest.importorskip("joblib")
2828
if os.getenv("MNE_FORCE_SERIAL", "").lower() in ("true", "1"):
29-
pytest.skip("MNE_FORCE_SERIAL cannot be set")
29+
pytest.skip("MNE_FORCE_SERIAL is set")
3030

3131
def fun(x):
3232
return x * 2

mne/viz/_brain/tests/test_brain.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -850,14 +850,6 @@ def tiny(tmp_path):
850850
def test_brain_screenshot(renderer_interactive_pyvistaqt, tmp_path, brain_gc):
851851
"""Test time viewer screenshot."""
852852
# This is broken on Conda + GHA for some reason
853-
from qtpy import API_NAME
854-
855-
if (
856-
os.getenv("CONDA_PREFIX", "") != ""
857-
and os.getenv("GITHUB_ACTIONS", "") == "true"
858-
or API_NAME.lower() == "pyside6"
859-
):
860-
pytest.skip("Test is unreliable on GitHub Actions conda runs and pyside6")
861853
tiny_brain, ratio = tiny(tmp_path)
862854
img_nv = tiny_brain.screenshot(time_viewer=False)
863855
want = (_TINY_SIZE[1] * ratio, _TINY_SIZE[0] * ratio, 3)

mne/viz/tests/test_evoked.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,10 @@ def test_plot_evoked_image():
339339

340340
ch_names = evoked.ch_names[3:5]
341341
picks = [evoked.ch_names.index(ch) for ch in ch_names]
342-
evoked.plot_image(show_names="all", time_unit="s", picks=picks)
343-
yticklabels = plt.gca().get_yticklabels()
342+
fig = evoked.plot_image(show_names="all", time_unit="s", picks=picks)
343+
fig.canvas.draw_idle()
344+
yticklabels = fig.axes[0].get_yticklabels()
345+
assert len(yticklabels) == len(ch_names)
344346
for tick_target, tick_observed in zip(ch_names, yticklabels):
345347
assert tick_target in str(tick_observed)
346348
evoked.plot_image(show_names=True, time_unit="s")

mne/viz/tests/test_raw.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -862,16 +862,14 @@ def test_remove_annotations(raw, hide_which, browser_backend):
862862
assert len(raw.annotations) == len(hide_which)
863863

864864

865-
def test_merge_annotations(raw, browser_backend):
865+
def test_merge_annotations(raw, pg_backend):
866866
"""Test merging of annotations in the Qt backend.
867867
868868
Let's not bother in figuring out on which sample the _fake_click actually
869869
dropped the annotation, especially with the 600.614 Hz weird sampling rate.
870870
-> atol = 10 / raw.info["sfreq"]
871871
"""
872-
if browser_backend.name == "matplotlib":
873-
pytest.skip("The MPL backend does not support draggable annotations.")
874-
elif not check_version("mne_qt_browser", "0.5.3"):
872+
if not check_version("mne_qt_browser", "0.5.3"):
875873
pytest.xfail("mne_qt_browser < 0.5.3 does not merge annotations properly")
876874
annot = Annotations(
877875
onset=[1, 3, 4, 5, 7, 8],

0 commit comments

Comments
 (0)