From 1374ba6237e268fbb23af34b9bb128bc21ed33f0 Mon Sep 17 00:00:00 2001 From: Jeremy Manning Date: Sun, 25 Oct 2026 22:04:26 -0400 Subject: [PATCH 1/2] CI: temporary GL diagnostics on Linux (do not merge) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ue9wuWfgA8yRXEuEiydTVh --- .github/workflows/tests.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ff88879..366aa9f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,6 +29,24 @@ jobs: - run: pip install -e ".[dev]" - run: ruff check . - run: ruff format --check . + - name: GL diagnostics (temporary) + if: runner.os == 'Linux' + run: | + ldconfig -p | grep -i -E "libEGL|OSMesa|libGLX|libgallium|egl_mesa|libGL\.so" || true + ls /usr/share/glvnd/egl_vendor.d/ 2>/dev/null || echo "no EGL vendor files" + dpkg -l | grep -i -E "libegl|osmesa|mesa" | awk '{print $2, $3}' || true + python -c "import vtk; print('vtk', vtk.vtkVersion.GetVTKVersion())" + for d in ":99.0" ""; do + for w in vtkEGLRenderWindow vtkOSOpenGLRenderWindow; do + echo "=== DISPLAY='$d' window=$w" + env DISPLAY="$d" VTK_DEFAULT_OPENGL_WINDOW=$w python -c " + import vtkmodules.vtkRenderingOpenGL2 + from vtkmodules.vtkRenderingCore import vtkRenderer, vtkRenderWindow + w = vtkRenderWindow(); w.SetOffScreenRendering(1); w.SetSize(8, 8) + w.AddRenderer(vtkRenderer()); w.Render(); print('class', w.GetClassName()) + " 2>&1 | tail -5; echo "exit ${PIPESTATUS[0]}" + done + done - run: pytest -q env: HTFA_DATA_DIR: ${{ runner.temp }}/htfa-data From b2872e5a07c12e2ace7bfe2b0cf66edb5b1ccac5 Mon Sep 17 00:00:00 2001 From: Jeremy Manning Date: Sun, 25 Oct 2026 22:10:50 -0400 Subject: [PATCH 2/2] CI: install Mesa EGL on Linux; headless tests probe backends without DISPLAY Root cause of the ubuntu failures: the runner has neither libEGL nor libOSMesa (setup-headless-display-action installs only GLX and Xvfb), so both VTK offscreen probes fail with DISPLAY unset. The render test probed with the runner's Xvfb DISPLAY set and so expected a render. CI now installs libegl1 libegl-mesa0 (what Colab has), and the tests probe with DISPLAY removed, as _headless_window does. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Ue9wuWfgA8yRXEuEiydTVh --- .github/workflows/tests.yml | 23 +++++------------------ tests/test_headless.py | 26 ++++++++++++++++---------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 366aa9f..b10b83b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,6 +20,11 @@ jobs: python-version: ${{ matrix.python-version }} # off-screen pyvista rendering (htfa.brainnet): Xvfb on Linux, Mesa on Windows - uses: pyvista/setup-headless-display-action@v5 + # Mesa's EGL, as on Colab: htfa renders without a display through VTK's + # EGL window (tests/test_headless.py). The action above installs only + # GLX and Xvfb. + - if: runner.os == 'Linux' + run: sudo apt-get install -y --no-install-recommends libegl1 libegl-mesa0 # BrainNet Viewer meshes are downloaded from NITRC on first use; keep them - uses: actions/cache@v4 with: @@ -29,24 +34,6 @@ jobs: - run: pip install -e ".[dev]" - run: ruff check . - run: ruff format --check . - - name: GL diagnostics (temporary) - if: runner.os == 'Linux' - run: | - ldconfig -p | grep -i -E "libEGL|OSMesa|libGLX|libgallium|egl_mesa|libGL\.so" || true - ls /usr/share/glvnd/egl_vendor.d/ 2>/dev/null || echo "no EGL vendor files" - dpkg -l | grep -i -E "libegl|osmesa|mesa" | awk '{print $2, $3}' || true - python -c "import vtk; print('vtk', vtk.vtkVersion.GetVTKVersion())" - for d in ":99.0" ""; do - for w in vtkEGLRenderWindow vtkOSOpenGLRenderWindow; do - echo "=== DISPLAY='$d' window=$w" - env DISPLAY="$d" VTK_DEFAULT_OPENGL_WINDOW=$w python -c " - import vtkmodules.vtkRenderingOpenGL2 - from vtkmodules.vtkRenderingCore import vtkRenderer, vtkRenderWindow - w = vtkRenderWindow(); w.SetOffScreenRendering(1); w.SetSize(8, 8) - w.AddRenderer(vtkRenderer()); w.Render(); print('class', w.GetClassName()) - " 2>&1 | tail -5; echo "exit ${PIPESTATUS[0]}" - done - done - run: pytest -q env: HTFA_DATA_DIR: ${{ runner.temp }}/htfa-data diff --git a/tests/test_headless.py b/tests/test_headless.py index 313ff00..896f97f 100644 --- a/tests/test_headless.py +++ b/tests/test_headless.py @@ -23,15 +23,25 @@ def no_display(monkeypatch): monkeypatch.setattr(brainnet, "_no_window", False) +def _offscreen_windows() -> list[str]: + """The headless windows that render here with DISPLAY unset. + + Probed without DISPLAY, as `_headless_window` does: a runner with Xvfb + (DISPLAY set) can render through it while EGL/OSMesa cannot. + """ + saved = os.environ.pop("DISPLAY", None) + try: + return [w for w in brainnet.HEADLESS_WINDOWS if brainnet._gl_window_works(w)] + finally: + if saved is not None: + os.environ["DISPLAY"] = saved + + def _working_window() -> str | None: """A window class the probe should accept here, if any.""" if not LINUX: return NATIVE[sys.platform] - if os.environ.get("DISPLAY"): - return None - return next( - (w for w in brainnet.HEADLESS_WINDOWS if brainnet._gl_window_works(w)), None - ) + return next(iter(_offscreen_windows()), None) def test_gl_window_works_detects_native_window(): @@ -164,11 +174,7 @@ def test_plot_network_renders_without_display(tmp_path): timeout=600, ) print(run.stdout, run.stderr) - if ( - LINUX - and not os.environ.get("DISPLAY") - and not any(brainnet._gl_window_works(w) for w in brainnet.HEADLESS_WINDOWS) - ): + if LINUX and not _offscreen_windows(): # No offscreen library here: the call must fail cleanly (no segfault) # with the error that names the packages to install. assert run.returncode == 1, run.stderr