Skip to content

Commit eb3cb06

Browse files
committed
fix(pytest): Add more Xvfb tricks to try and fix tkinter integration tests in uv
1 parent 9706433 commit eb3cb06

File tree

4 files changed

+109
-3
lines changed

4 files changed

+109
-3
lines changed

.github/workflows/pytest.yml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,15 @@ jobs:
5252
run: |
5353
uv pip install --editable .[dev]
5454
55-
- name: Setup virtual display
55+
- name: Setup virtual display for GUI tests
5656
run: |
57+
sudo apt-get update
58+
sudo apt-get install -y xvfb xauth x11-utils
5759
export DISPLAY=:99.0
5860
Xvfb :99 -screen 0 1024x768x24 -ac +extension GLX +render -noreset &
59-
sleep 3 # Give Xvfb time to start
61+
sleep 5 # Give Xvfb time to start
62+
# Verify display is working
63+
xdpyinfo || echo "Warning: X11 display verification failed"
6064
6165
- name: Test with pytest
6266
id: pytest
@@ -69,8 +73,17 @@ jobs:
6973
MPLBACKEND: "Agg"
7074
PYTHONPATH: ${{ github.workspace }}
7175
CI: "true" # Mark as CI environment
76+
# Force tkinter to use Xvfb
77+
XAUTHORITY: ""
7278
run: |
73-
uv run --python=${{ matrix.python-version }} pytest --cov=ardupilot_methodic_configurator --cov-report=xml:tests/coverage.xml --md=tests/results-${{ matrix.python-version }}.md --junit-xml=tests/results-junit.xml
79+
# Add option to skip GUI tests if needed
80+
if [ "${{ matrix.python-version }}" = "pypy3.9" ] || [ "${{ matrix.python-version }}" = "pypy3.10" ]; then
81+
# PyPy might have issues with tkinter, skip GUI tests
82+
uv run --python=${{ matrix.python-version }} pytest --cov=ardupilot_methodic_configurator --cov-report=xml:tests/coverage.xml --md=tests/results-${{ matrix.python-version }}.md --junit-xml=tests/results-junit.xml -m "not gui"
83+
else
84+
# Run all tests including GUI tests
85+
uv run --python=${{ matrix.python-version }} pytest --cov=ardupilot_methodic_configurator --cov-report=xml:tests/coverage.xml --md=tests/results-${{ matrix.python-version }}.md --junit-xml=tests/results-junit.xml
86+
fi
7487
7588
- name: Fix coverage paths
7689
run: |

.vscode/tasks.json

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,84 @@
222222
"-Command",
223223
"coverage run -m pytest; coverage html; Start-Process firefox -ArgumentList 'htmlcov/index.html'"
224224
]
225+
},
226+
{
227+
"label": "pytest skip GUI tests",
228+
"type": "shell",
229+
"command": "pytest",
230+
"args": [
231+
"--cov=ardupilot_methodic_configurator",
232+
"--cov-report=xml:tests/coverage.xml",
233+
"--md=tests/results.md",
234+
"-m",
235+
"not gui"
236+
],
237+
"group": {
238+
"kind": "test",
239+
"isDefault": false
240+
},
241+
"problemMatcher": {
242+
"owner": "python",
243+
"fileLocation": ["relative", "${workspaceFolder}"],
244+
"pattern": [
245+
{
246+
"regexp": "^(.+):(\\d+): (\\w+): (.+)$",
247+
"file": 1,
248+
"line": 2,
249+
"severity": 3,
250+
"message": 4
251+
},
252+
{
253+
"regexp": "^(.+):(\\d+): in (.+)$",
254+
"file": 1,
255+
"line": 2,
256+
"code": 3
257+
},
258+
{
259+
"regexp": "^E\\s+(.+)$",
260+
"message": 1
261+
}
262+
]
263+
}
264+
},
265+
{
266+
"label": "pytest only GUI tests",
267+
"type": "shell",
268+
"command": "DISPLAY=:99.0 LIBGL_ALWAYS_SOFTWARE=1 QT_QPA_PLATFORM=offscreen MPLBACKEND=Agg pytest",
269+
"args": [
270+
"--cov=ardupilot_methodic_configurator",
271+
"--cov-report=xml:tests/coverage.xml",
272+
"--md=tests/results.md",
273+
"-m",
274+
"gui"
275+
],
276+
"group": {
277+
"kind": "test",
278+
"isDefault": false
279+
},
280+
"problemMatcher": {
281+
"owner": "python",
282+
"fileLocation": ["relative", "${workspaceFolder}"],
283+
"pattern": [
284+
{
285+
"regexp": "^(.+):(\\d+): (\\w+): (.+)$",
286+
"file": 1,
287+
"line": 2,
288+
"severity": 3,
289+
"message": 4
290+
},
291+
{
292+
"regexp": "^(.+):(\\d+): in (.+)$",
293+
"file": 1,
294+
"line": 2,
295+
"code": 3
296+
},
297+
{
298+
"regexp": "^E\\s+(.+)$",
299+
"message": 1
300+
}
301+
]
302+
}
225303
}
226304
],
227305
"inputs": [

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ addopts = -v --strict-config --continue-on-collection-errors
66
markers =
77
integration: mark a test as an integration test
88
slow: mark a test as slow running
9+
gui: mark a test as requiring GUI/X11 environment (tkinter tests)

tests/test_frontend_tkinter_autoresize_combobox.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from tkinter import ttk
1616
from unittest.mock import patch
1717

18+
import pytest
19+
1820
from ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox import (
1921
AutoResizeCombobox,
2022
update_combobox_width,
@@ -24,17 +26,20 @@
2426
class TestUpdateComboboxWidth(unittest.TestCase):
2527
"""Test cases for the update_combobox_width function."""
2628

29+
@pytest.mark.gui
2730
def test_update_combobox_width(self) -> None:
2831
combobox = ttk.Combobox(values=["short", "longer", "longest"])
2932
update_combobox_width(combobox)
3033
assert combobox.cget("width") == 7
3134

35+
@pytest.mark.gui
3236
def test_update_combobox_width_empty_values(self) -> None:
3337
combobox = ttk.Combobox(values=[])
3438
update_combobox_width(combobox)
3539
# Should use the minimum width (4) when no values
3640
assert combobox.cget("width") == 4
3741

42+
@pytest.mark.gui
3843
def test_update_combobox_width_very_short_values(self) -> None:
3944
combobox = ttk.Combobox(values=["a", "b", "c"])
4045
update_combobox_width(combobox)
@@ -45,6 +50,7 @@ def test_update_combobox_width_very_short_values(self) -> None:
4550
class TestAutoResizeCombobox(unittest.TestCase):
4651
"""Test cases for the AutoResizeCombobox class."""
4752

53+
@pytest.mark.gui
4854
def setUp(self) -> None:
4955
self.root = tk.Tk()
5056
self.root.withdraw() # Hide the main window during tests
@@ -55,21 +61,25 @@ def setUp(self) -> None:
5561
def tearDown(self) -> None:
5662
self.root.destroy()
5763

64+
@pytest.mark.gui
5865
def test_initial_selection(self) -> None:
5966
assert self.combobox.get() == "two"
6067

68+
@pytest.mark.gui
6169
def test_update_values(self) -> None:
6270
self.combobox.set_entries_tuple(["four", "five", "six"], "five")
6371
assert self.combobox.get() == "five"
6472
assert self.combobox["values"] == ("four", "five", "six")
6573

74+
@pytest.mark.gui
6675
def test_set_entries_with_spaces(self) -> None:
6776
"""Test values with spaces."""
6877
values = ["option one", "option two", "option three"]
6978
self.combobox.set_entries_tuple(values, "option two")
7079
assert self.combobox["values"] == tuple(values)
7180
assert self.combobox.get() == "option two"
7281

82+
@pytest.mark.gui
7383
@patch("ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox.logging_error")
7484
def test_set_entries_invalid_selection(self, mock_logging_error) -> None:
7585
"""Test when selected element is not in values list."""
@@ -81,6 +91,7 @@ def test_set_entries_invalid_selection(self, mock_logging_error) -> None:
8191
# Selected value should not be set
8292
assert self.combobox.get() == "two" # Maintains previous value
8393

94+
@pytest.mark.gui
8495
@patch("ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox.logging_warning")
8596
def test_set_entries_no_selection(self, mock_logging_warning) -> None:
8697
"""Test when no selection is provided."""
@@ -90,6 +101,7 @@ def test_set_entries_no_selection(self, mock_logging_warning) -> None:
90101
# Should log a warning
91102
mock_logging_warning.assert_called_once()
92103

104+
@pytest.mark.gui
93105
@patch("ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox.update_combobox_width")
94106
def test_set_entries_empty_values(self, mock_update_width) -> None:
95107
"""Test behavior with empty values list."""
@@ -98,6 +110,7 @@ def test_set_entries_empty_values(self, mock_update_width) -> None:
98110
# Width update should not be called with empty values
99111
mock_update_width.assert_not_called()
100112

113+
@pytest.mark.gui
101114
@patch("ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox.show_tooltip")
102115
def test_tooltip_display(self, mock_show_tooltip) -> None:
103116
"""Test tooltip is shown when provided."""
@@ -106,6 +119,7 @@ def test_tooltip_display(self, mock_show_tooltip) -> None:
106119
# Tooltip should be shown
107120
mock_show_tooltip.assert_called_once_with(self.combobox, "Help text")
108121

122+
@pytest.mark.gui
109123
@patch("ardupilot_methodic_configurator.frontend_tkinter_autoresize_combobox.show_tooltip")
110124
def test_no_tooltip_when_none(self, mock_show_tooltip) -> None:
111125
"""Test tooltip is not shown when None."""

0 commit comments

Comments
 (0)