From 37df3ea2a2fbc7cd401b4739c80624abae591db8 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Mon, 11 Aug 2025 12:28:31 +0200 Subject: [PATCH 01/20] [ci] add tutorials test file --- test_tutorials.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test_tutorials.py diff --git a/test_tutorials.py b/test_tutorials.py new file mode 100644 index 0000000000000..65ef89f43a5b9 --- /dev/null +++ b/test_tutorials.py @@ -0,0 +1,17 @@ +import subprocess +import sys +import pathlib +import ROOT +import os +import pytest + +# ROOT.gROOT.SetBatch(True) + +tutorial_dir = pathlib.Path(os.environ.get("ROOTSYS")) / "tutorials" +tutorials = list(tutorial_dir.rglob("*.py")) + +@pytest.mark.parametrize("tutorial", tutorials) +def test_tutorial(tutorial): + # subprocess.run([sys.executable, str(tutorial)], check=True) + print(f"Running tutorial: {tutorial}") + assert len(tutorials) > 0 From 90219580f02c544162679295fe9fb7d119bca8a0 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Mon, 11 Aug 2025 12:29:05 +0200 Subject: [PATCH 02/20] [ci] update python wheel workflow to integrate testing --- .github/workflows/python_wheel_build.yml | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/python_wheel_build.yml b/.github/workflows/python_wheel_build.yml index 9a2a34f54d7b5..6583bf3d7cb15 100644 --- a/.github/workflows/python_wheel_build.yml +++ b/.github/workflows/python_wheel_build.yml @@ -30,6 +30,39 @@ jobs: with: build-tag: ${{ matrix.target }} + test-wheels: + needs: build-wheels + runs-on: ubuntu-latest + strategy: + matrix: + target: [cp38-manylinux_x86_64, cp39-manylinux_x86_64, cp310-manylinux_x86_64, cp311-manylinux_x86_64, cp312-manylinux_x86_64, cp313-manylinux_x86_64] + name: test-${{ matrix.target }} + steps: + - uses: actions/checkout@v4 + + - name: Download produced wheels + uses: actions/download-artifact@v4 + with: + name: ${{ matrix.target }} + path: wheelhouse + # merge-multiple: true + + - name: Setup Python + uses: actions/setup-python@v5 + + - name: Test wheel + env: + RWEBEOS_KT: ${{ secrets.RWEBEOS_KT }} + KT_FILE_NAME: /tmp/decoded.keytab + KRB5CCNAME: /tmp/krb5cc + CIBW_BUILD: ${{ matrix.target }} + CIBW_TEST_REQUIRES: "pytest" + CIBW_TEST_SOURCES: "test_tutorials" + CIBW_TEST_COMMAND: "pytest -v" + run: | + python -m pip install wheelhouse/*.whl + cibuildwheel --output-dir wheelhouse + create-and-upload-wheel-registry: if: github.event_name != 'pull_request' # The secrets are not available in PR needs: build-wheels From 489ba6bce01d7e6bd6d0cc8dbbc3244c8d27efaa Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Mon, 11 Aug 2025 15:43:44 +0200 Subject: [PATCH 03/20] reset python_wheel_build --- .github/workflows/python_wheel_build.yml | 33 ------------------------ 1 file changed, 33 deletions(-) diff --git a/.github/workflows/python_wheel_build.yml b/.github/workflows/python_wheel_build.yml index 6583bf3d7cb15..9a2a34f54d7b5 100644 --- a/.github/workflows/python_wheel_build.yml +++ b/.github/workflows/python_wheel_build.yml @@ -30,39 +30,6 @@ jobs: with: build-tag: ${{ matrix.target }} - test-wheels: - needs: build-wheels - runs-on: ubuntu-latest - strategy: - matrix: - target: [cp38-manylinux_x86_64, cp39-manylinux_x86_64, cp310-manylinux_x86_64, cp311-manylinux_x86_64, cp312-manylinux_x86_64, cp313-manylinux_x86_64] - name: test-${{ matrix.target }} - steps: - - uses: actions/checkout@v4 - - - name: Download produced wheels - uses: actions/download-artifact@v4 - with: - name: ${{ matrix.target }} - path: wheelhouse - # merge-multiple: true - - - name: Setup Python - uses: actions/setup-python@v5 - - - name: Test wheel - env: - RWEBEOS_KT: ${{ secrets.RWEBEOS_KT }} - KT_FILE_NAME: /tmp/decoded.keytab - KRB5CCNAME: /tmp/krb5cc - CIBW_BUILD: ${{ matrix.target }} - CIBW_TEST_REQUIRES: "pytest" - CIBW_TEST_SOURCES: "test_tutorials" - CIBW_TEST_COMMAND: "pytest -v" - run: | - python -m pip install wheelhouse/*.whl - cibuildwheel --output-dir wheelhouse - create-and-upload-wheel-registry: if: github.event_name != 'pull_request' # The secrets are not available in PR needs: build-wheels From a18a5888c105afdf491d150495faa046026d0fa5 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Mon, 11 Aug 2025 15:44:38 +0200 Subject: [PATCH 04/20] moved impl to cibuildwheel-impl --- .github/workflows/cibuildwheel-impl/action.yml | 3 +++ test_tutorials.py | 17 ----------------- 2 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 test_tutorials.py diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index 9e8d7825f4f98..bd0c4b5aed144 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -12,6 +12,9 @@ runs: uses: pypa/cibuildwheel@v3.0.1 env: CIBW_BUILD: ${{ inputs.build-tag }} + CIBW_TEST_REQUIRES: "pytest" + CIBW_TEST_SOURCES: "test_tutorials" + CIBW_TEST_COMMAND: "pytest -v" - name: Upload wheel uses: actions/upload-artifact@v4 diff --git a/test_tutorials.py b/test_tutorials.py deleted file mode 100644 index 65ef89f43a5b9..0000000000000 --- a/test_tutorials.py +++ /dev/null @@ -1,17 +0,0 @@ -import subprocess -import sys -import pathlib -import ROOT -import os -import pytest - -# ROOT.gROOT.SetBatch(True) - -tutorial_dir = pathlib.Path(os.environ.get("ROOTSYS")) / "tutorials" -tutorials = list(tutorial_dir.rglob("*.py")) - -@pytest.mark.parametrize("tutorial", tutorials) -def test_tutorial(tutorial): - # subprocess.run([sys.executable, str(tutorial)], check=True) - print(f"Running tutorial: {tutorial}") - assert len(tutorials) > 0 From 4e2ef7db58693b8ced2d1f996f5ac2f2bed3a3a5 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Mon, 11 Aug 2025 17:32:55 +0200 Subject: [PATCH 05/20] add test_tutorials dir --- test_tutorials/test_tutorials.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test_tutorials/test_tutorials.py diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py new file mode 100644 index 0000000000000..210ab4e974eae --- /dev/null +++ b/test_tutorials/test_tutorials.py @@ -0,0 +1,18 @@ +import subprocess +import sys +import pathlib +import ROOT +import os +import pytest + +# ROOT.gROOT.SetBatch(True) + +tutorial_dir = pathlib.Path(os.environ.get("ROOTSYS")) / "tutorials" +tutorials = list(tutorial_dir.rglob("*.py")) + +@pytest.mark.parametrize("tutorial", tutorials) +def test_tutorial(tutorial): + # subprocess.run([sys.executable, str(tutorial)], check=True) + print(f"Running tutorial: {tutorial}") + assert len(tutorials) > 0 +je siiosi \ No newline at end of file From 01114054171d76d877be608716ea6212007cd289 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Mon, 11 Aug 2025 17:36:15 +0200 Subject: [PATCH 06/20] [to be removed] trigger the workflow always --- .github/workflows/python_wheel_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python_wheel_build.yml b/.github/workflows/python_wheel_build.yml index 9a2a34f54d7b5..72d3275e1e11f 100644 --- a/.github/workflows/python_wheel_build.yml +++ b/.github/workflows/python_wheel_build.yml @@ -11,7 +11,7 @@ on: schedule: - cron: '01 1 * * *' pull_request: - types: [labeled] + types: [opened, synchronize, reopened, labeled] jobs: build-wheels: From ed851ad24f22e1057436965daa7ddc2fad22223a Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Tue, 12 Aug 2025 09:33:28 +0200 Subject: [PATCH 07/20] fix typo in test script --- test_tutorials/test_tutorials.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index 210ab4e974eae..d03b2b47f6438 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -14,5 +14,4 @@ def test_tutorial(tutorial): # subprocess.run([sys.executable, str(tutorial)], check=True) print(f"Running tutorial: {tutorial}") - assert len(tutorials) > 0 -je siiosi \ No newline at end of file + assert len(tutorials) > 0 \ No newline at end of file From 854a5eab6d1ea377c0a0555c5919a28abe2ce7c7 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Tue, 12 Aug 2025 15:30:05 +0200 Subject: [PATCH 08/20] fix tutorials path --- test_tutorials/test_tutorials.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index d03b2b47f6438..0e0a9e2c53397 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -7,9 +7,10 @@ # ROOT.gROOT.SetBatch(True) -tutorial_dir = pathlib.Path(os.environ.get("ROOTSYS")) / "tutorials" +tutorial_dir = pathlib.Path(str(ROOT.gROOT.GetTutorialDir())) or pathlib.Path(ROOT.__file__).parent.parent.parent / "tutorials" tutorials = list(tutorial_dir.rglob("*.py")) + @pytest.mark.parametrize("tutorial", tutorials) def test_tutorial(tutorial): # subprocess.run([sys.executable, str(tutorial)], check=True) From ad69c8ff4d8e8a27682aa62c6874672e31ba5c0a Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Tue, 12 Aug 2025 17:35:36 +0200 Subject: [PATCH 09/20] actually run the tutorials --- test_tutorials/test_tutorials.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index 0e0a9e2c53397..6830a0022cc63 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -5,14 +5,18 @@ import os import pytest -# ROOT.gROOT.SetBatch(True) +ROOT.gROOT.SetBatch(True) -tutorial_dir = pathlib.Path(str(ROOT.gROOT.GetTutorialDir())) or pathlib.Path(ROOT.__file__).parent.parent.parent / "tutorials" +tutorial_dir = pathlib.Path(str(ROOT.gROOT.GetTutorialDir())) tutorials = list(tutorial_dir.rglob("*.py")) -@pytest.mark.parametrize("tutorial", tutorials) +def test_tutorials_are_detected(): + assert len(tutorials) > 0 + +@pytest.mark.parametrize("tutorial", tutorials, ids=lambda p: p.name) def test_tutorial(tutorial): - # subprocess.run([sys.executable, str(tutorial)], check=True) - print(f"Running tutorial: {tutorial}") - assert len(tutorials) > 0 \ No newline at end of file + env = dict(**os.environ) + # force matplotlib to use a non-GUI backend + env["MPLBACKEND"] = "Agg" + subprocess.run([sys.executable, str(tutorial)], check=True, env=env) \ No newline at end of file From 73da738f46811002f581789dc3b7dbfd53881c47 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 13 Aug 2025 11:35:41 +0200 Subject: [PATCH 10/20] set matrix fail-fast to false --- .github/workflows/cibuildwheel-impl/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index bd0c4b5aed144..f93d71774a089 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -12,7 +12,7 @@ runs: uses: pypa/cibuildwheel@v3.0.1 env: CIBW_BUILD: ${{ inputs.build-tag }} - CIBW_TEST_REQUIRES: "pytest" + CIBW_TEST_REQUIRES: "pytest numpy matplotlib mplhep" CIBW_TEST_SOURCES: "test_tutorials" CIBW_TEST_COMMAND: "pytest -v" From ce3a6079c8697684f7fd7da721e613b51f7369db Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 13 Aug 2025 11:36:09 +0200 Subject: [PATCH 11/20] set matrix fail-fast to false --- .github/workflows/python_wheel_build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python_wheel_build.yml b/.github/workflows/python_wheel_build.yml index 72d3275e1e11f..c8c703bc3d2e5 100644 --- a/.github/workflows/python_wheel_build.yml +++ b/.github/workflows/python_wheel_build.yml @@ -21,6 +21,7 @@ jobs: contains(github.event.pull_request.labels.*.name, 'build-python-wheels') runs-on: ubuntu-latest strategy: + fail-fast: false matrix: target: [cp38-manylinux_x86_64, cp39-manylinux_x86_64, cp310-manylinux_x86_64, cp311-manylinux_x86_64, cp312-manylinux_x86_64, cp313-manylinux_x86_64] name: ${{ matrix.target }} From 2c74a2cdb52acd6d0bcd5f63b556998c86eb0a03 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 13 Aug 2025 15:56:42 +0200 Subject: [PATCH 12/20] catch EOFError --- test_tutorials/test_tutorials.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index 6830a0022cc63..1952e4e3e23bb 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -19,4 +19,16 @@ def test_tutorial(tutorial): env = dict(**os.environ) # force matplotlib to use a non-GUI backend env["MPLBACKEND"] = "Agg" - subprocess.run([sys.executable, str(tutorial)], check=True, env=env) \ No newline at end of file + try: + subprocess.run( + [sys.executable, str(tutorial)], + check=True, + env=env, + capture_output=True, + text=True + ) + except subprocess.CalledProcessError as e: + # read stderr to see if EOFError occurred + if "EOFError" in e.stderr: + pytest.skip("Skipping tutorial that requires user input") + raise \ No newline at end of file From deae5d6097bd367021924f100ee253b5c02b7d98 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 13 Aug 2025 16:01:27 +0200 Subject: [PATCH 13/20] add requirements to CIBW_TEST_REQUIRES --- .github/workflows/cibuildwheel-impl/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index f93d71774a089..a3002b484d62f 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -12,7 +12,7 @@ runs: uses: pypa/cibuildwheel@v3.0.1 env: CIBW_BUILD: ${{ inputs.build-tag }} - CIBW_TEST_REQUIRES: "pytest numpy matplotlib mplhep" + CIBW_TEST_REQUIRES: "-r requirements.txt" CIBW_TEST_SOURCES: "test_tutorials" CIBW_TEST_COMMAND: "pytest -v" From da951eb470a35b50444cb8fa0286c8fa07b4fe89 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 13 Aug 2025 18:13:27 +0200 Subject: [PATCH 14/20] add a slimmed down version of the requirements (ran out of space on the runner) --- .../workflows/cibuildwheel-impl/action.yml | 2 +- test_tutorials/requirements.txt | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test_tutorials/requirements.txt diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index a3002b484d62f..9aa5975fbedd9 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -12,7 +12,7 @@ runs: uses: pypa/cibuildwheel@v3.0.1 env: CIBW_BUILD: ${{ inputs.build-tag }} - CIBW_TEST_REQUIRES: "-r requirements.txt" + CIBW_TEST_REQUIRES: "-r test_tutorials/requirements.txt" CIBW_TEST_SOURCES: "test_tutorials" CIBW_TEST_COMMAND: "pytest -v" diff --git a/test_tutorials/requirements.txt b/test_tutorials/requirements.txt new file mode 100644 index 0000000000000..5c729135d07e9 --- /dev/null +++ b/test_tutorials/requirements.txt @@ -0,0 +1,47 @@ +# ROOT requirements for third-party Python packages + +# PyROOT: Interoperability with numpy arrays +numpy +pandas + +# TMVA: SOFIE +# dm-sonnet # used for GNNs +# graph_nets +# onnx + +# TMVA: PyMVA interfaces +# scikit-learn +# tensorflow ; python_version < "3.13" # TensorFlow doesn't support Python 3.13 yet +# torch +# xgboost + +# PyROOT: ROOT.Numba.Declare decorator +numba>=0.48 +cffi>=1.9.1 + +# Notebooks: ROOT C++ kernel +# IPython +# jupyter +# metakernel>=0.20.0 +# notebook>=4.4.1 + +# Distributed RDataFrame +pyspark>=2.4 # Spark backend +dask>=2022.08.1 # Dask backend +distributed>=2022.08.1 # Dask backend + +# JsMVA: Jupyter notebook magic for TMVA +ipywidgets + +# Unified Histogram Interface (UHI) +uhi +matplotlib +mplhep + +# For testing +# nbconvert>=7.4.0 +pytest +# setuptools + +# Look for CPU-only versions of PyTorch to avoid pulling CUDA in the CI docker images. +# -f https://download.pytorch.org/whl/cpu/torch_stable.html From e9e3d077f96ef4dfad46862ccfe8b9b13b7e7592 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 13 Aug 2025 18:24:54 +0200 Subject: [PATCH 15/20] set PIP_NO_CACHE_DIR --- .github/workflows/cibuildwheel-impl/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index 9aa5975fbedd9..be9ec5b47de18 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -11,6 +11,7 @@ runs: - name: Build wheel uses: pypa/cibuildwheel@v3.0.1 env: + PIP_NO_CACHE_DIR: "1" CIBW_BUILD: ${{ inputs.build-tag }} CIBW_TEST_REQUIRES: "-r test_tutorials/requirements.txt" CIBW_TEST_SOURCES: "test_tutorials" From e87f66d249633974cd764f72353d2898a30d2f53 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Thu, 14 Aug 2025 17:11:02 +0200 Subject: [PATCH 16/20] add system dependencies --- .github/workflows/cibuildwheel-impl/action.yml | 7 +++++++ test_tutorials/requirements.txt | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index be9ec5b47de18..d8af5632fc1df 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -8,6 +8,13 @@ inputs: runs: using: "composite" steps: + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + binutils cmake dpkg-dev g++ gcc libssl-dev git libx11-dev \ + libxext-dev libxft-dev libxpm-dev python3 libtbb-dev libvdt-dev libgif-dev + - name: Build wheel uses: pypa/cibuildwheel@v3.0.1 env: diff --git a/test_tutorials/requirements.txt b/test_tutorials/requirements.txt index 5c729135d07e9..b38c47fcad9ad 100644 --- a/test_tutorials/requirements.txt +++ b/test_tutorials/requirements.txt @@ -31,7 +31,7 @@ dask>=2022.08.1 # Dask backend distributed>=2022.08.1 # Dask backend # JsMVA: Jupyter notebook magic for TMVA -ipywidgets +# ipywidgets # Unified Histogram Interface (UHI) uhi From e4acb916c1a6a38deffe51955890e33dfbcc32b3 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Thu, 14 Aug 2025 17:59:58 +0200 Subject: [PATCH 17/20] add shell --- .github/workflows/cibuildwheel-impl/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cibuildwheel-impl/action.yml b/.github/workflows/cibuildwheel-impl/action.yml index d8af5632fc1df..e0e01aad81b52 100644 --- a/.github/workflows/cibuildwheel-impl/action.yml +++ b/.github/workflows/cibuildwheel-impl/action.yml @@ -9,6 +9,7 @@ runs: using: "composite" steps: - name: Install system dependencies + shell: bash run: | sudo apt-get update sudo apt-get install -y \ From 6680cbc24dd32b34b2ca0f0f4c76ac8dd1a03d58 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Wed, 20 Aug 2025 16:38:35 +0200 Subject: [PATCH 18/20] run cpp macros --- test_tutorials/test_tutorials.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index 1952e4e3e23bb..9384bc64b702a 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -8,13 +8,16 @@ ROOT.gROOT.SetBatch(True) tutorial_dir = pathlib.Path(str(ROOT.gROOT.GetTutorialDir())) -tutorials = list(tutorial_dir.rglob("*.py")) +# ---------------------- +# Python tutorials tests +# ---------------------- +py_tutorials = list(tutorial_dir.rglob("*.py")) def test_tutorials_are_detected(): - assert len(tutorials) > 0 + assert len(py_tutorials) > 0 -@pytest.mark.parametrize("tutorial", tutorials, ids=lambda p: p.name) +@pytest.mark.parametrize("tutorial", py_tutorials, ids=lambda p: p.name) def test_tutorial(tutorial): env = dict(**os.environ) # force matplotlib to use a non-GUI backend @@ -31,4 +34,16 @@ def test_tutorial(tutorial): # read stderr to see if EOFError occurred if "EOFError" in e.stderr: pytest.skip("Skipping tutorial that requires user input") - raise \ No newline at end of file + raise + +# ---------------------- +# C++ tutorials tests +# ---------------------- +cpp_tutorials = list(tutorial_dir.rglob("*.C")) + +def test_cpp_tutorials_are_detected(): + assert len(cpp_tutorials) > 0 + +@pytest.mark.parametrize("tutorial", cpp_tutorials, ids=lambda p: p.name) +def test_cpp_tutorial(tutorial): + ROOT.gROOT.ProcessLine(f".x {tutorial}") \ No newline at end of file From 6c07035a3a3520ac42c30d01bb3795fc5a673e76 Mon Sep 17 00:00:00 2001 From: Silia Taider Date: Thu, 21 Aug 2025 09:38:08 +0200 Subject: [PATCH 19/20] skip tests that crash with SIGKILL --- test_tutorials/test_tutorials.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index 9384bc64b702a..33cb08fffc79a 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -33,7 +33,7 @@ def test_tutorial(tutorial): except subprocess.CalledProcessError as e: # read stderr to see if EOFError occurred if "EOFError" in e.stderr: - pytest.skip("Skipping tutorial that requires user input") + pytest.skip(f"Skipping {tutorial.name} (requires user input)") raise # ---------------------- @@ -46,4 +46,18 @@ def test_cpp_tutorials_are_detected(): @pytest.mark.parametrize("tutorial", cpp_tutorials, ids=lambda p: p.name) def test_cpp_tutorial(tutorial): - ROOT.gROOT.ProcessLine(f".x {tutorial}") \ No newline at end of file + # ROOT.gROOT.ProcessLine(f".x {tutorial}") + + try: + result = subprocess.run( + [sys.executable, "-c", f'import ROOT; ROOT.gROOT.ProcessLine(".x {tutorial}")'], + check=True, + capture_output=True, + text=True + ) + except subprocess.CalledProcessError as e: + if e.returncode == -signal.SIGILL or e.returncode == 132: + pytest.skip(f"Skipping {tutorial.name} (illegal instruction on this platform)") + elif "EOFError" in e.stderr: + pytest.skip(f"Skipping {tutorial.name} (requires user input)") + raise \ No newline at end of file From 94cb6260be448b505f84ab1f66823db0cad64ec6 Mon Sep 17 00:00:00 2001 From: siliataider Date: Thu, 21 Aug 2025 16:56:50 +0200 Subject: [PATCH 20/20] catch timeouts --- test_tutorials/test_tutorials.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test_tutorials/test_tutorials.py b/test_tutorials/test_tutorials.py index 33cb08fffc79a..df62a7b13b7cb 100644 --- a/test_tutorials/test_tutorials.py +++ b/test_tutorials/test_tutorials.py @@ -4,6 +4,7 @@ import ROOT import os import pytest +import signal ROOT.gROOT.SetBatch(True) @@ -27,9 +28,12 @@ def test_tutorial(tutorial): [sys.executable, str(tutorial)], check=True, env=env, + timeout=60, capture_output=True, - text=True + text=True, ) + except subprocess.TimeoutExpired: + pytest.skip(f"Tutorial {tutorial} timed out") except subprocess.CalledProcessError as e: # read stderr to see if EOFError occurred if "EOFError" in e.stderr: @@ -52,9 +56,12 @@ def test_cpp_tutorial(tutorial): result = subprocess.run( [sys.executable, "-c", f'import ROOT; ROOT.gROOT.ProcessLine(".x {tutorial}")'], check=True, + timeout=60, capture_output=True, text=True ) + except subprocess.TimeoutExpired: + pytest.skip(f"Tutorial {tutorial} timed out") except subprocess.CalledProcessError as e: if e.returncode == -signal.SIGILL or e.returncode == 132: pytest.skip(f"Skipping {tutorial.name} (illegal instruction on this platform)")