From af31c623fb5b2e4b49b8dd5e4547aeb6c8e8f40b Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Thu, 3 Sep 2026 15:34:27 +0200 Subject: [PATCH 1/2] Mock webbrowser.open() in test_dotfile to stop popping up a PDF showgraph() calls webbrowser.open() on the rendered PDF, which was launching a real PDF viewer/browser window every test run -- purely incidental, since the test made no assertion on the result at all. Mocks webbrowser.open() so the test runs headless, and replaces the "doesn't crash" smoke test with a real assertion: the file webbrowser.open() was called with actually exists and starts with the PDF magic bytes (%PDF-), confirming dot's rendering pipeline genuinely produced a valid PDF -- a stronger check than before, not a weaker one. Co-Authored-By: Claude Sonnet 5 --- tests/test_graph.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/test_graph.py b/tests/test_graph.py index a183380a..94502b15 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -781,6 +781,7 @@ def test_plot(self): def test_dotfile(self): import pathlib + import unittest.mock g = UGraph() v1 = g.add_vertex(coord=[0,0], name='v1') @@ -795,7 +796,17 @@ def test_dotfile(self): g.dotfile(str(path)) self.assertTrue(path.is_file()) - g.showgraph() + # showgraph() pops a PDF up in a browser via webbrowser.open() -- + # mock that so the test runs headless, but still exercise the real + # dot->PDF rendering and check the result is an actual PDF file. + with unittest.mock.patch('pgraph.PGraph.webbrowser.open') as mock_open: + g.showgraph() + + mock_open.assert_called_once() + pdf_url = mock_open.call_args[0][0] + pdf_path = pathlib.Path(pdf_url.removeprefix('file://')) + self.assertTrue(pdf_path.is_file()) + self.assertEqual(pdf_path.read_bytes()[:5], b'%PDF-') class TestDGraph(unittest.TestCase): From ea2336a457636fb2434240c62ca32edcd61385bd Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Thu, 3 Sep 2026 20:08:47 +0200 Subject: [PATCH 2/2] ci: install graphviz in unittest/codecov jobs test_dotfile() exercises the real dot->PDF rendering path via showgraph(), but only the docs job installed graphviz -- unittest and codecov didn't, so `dot` was silently missing there. The previous version of this test had no assertion on the result, so this went unnoticed; the new assertion (mock_open.assert_called_once(), added in this same PR) correctly caught it: CI failed with "dot: not found", showgraph()'s subprocess call returned nonzero, and webbrowser.open() was consequently never reached. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d947ce2d..b1bb88a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,8 @@ jobs: run: | python -m pip install --upgrade pip pip install .[dev] + sudo apt-get update + sudo apt-get install -y graphviz - name: Test with pytest run: pytest @@ -59,6 +61,8 @@ jobs: run: | python -m pip install --upgrade pip pip install -e .[dev] + sudo apt-get update + sudo apt-get install -y graphviz - name: Run coverage run: | pytest --cov=pgraph --cov-report=xml:coverage.xml