11name : Pytest
22
3- # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#packaging-workflow-data-as-artifacts
4-
53on :
64 pull_request :
75 paths :
8- - ' **/*.py' # Watch for changes in any Python files
9- - ' pyproject.toml' # Watch for changes in the pyproject.toml file
6+ - ' **/*.py'
7+ - ' pyproject.toml'
108 - ' .github/workflows/pytest.yml'
119 push :
1210 branches :
13- - master # Only run on push to master branch
11+ - master
1412 paths :
15- - ' **/*.py' # Watch for changes in any Python files
16- - ' pyproject.toml' # Watch for changes in the pyproject.toml file
13+ - ' **/*.py'
14+ - ' pyproject.toml'
1715 - ' .github/workflows/pytest.yml'
1816 workflow_dispatch :
1917 release :
@@ -29,28 +27,32 @@ jobs:
2927 strategy :
3028 fail-fast : false
3129 matrix :
32- # os: [ubuntu-latest, macos-latest, windows-latest]
33- # python-version: ["3.9", "3.10", "3.11", "3.12", "pypy3.9", "pypy3.10"]
34- os : [ubuntu-latest]
35- python-version : ["3.9", "3.14", "3.14t"]
30+ include :
31+ - os : ubuntu-latest
32+ python-version : " 3.9" # to make sure the minimum AMC supported python version does not break
33+ - os : ubuntu-latest
34+ python-version : " 3.14" # to make sure the latest AMC supported python version does not break
35+ - os : macos-latest
36+ python-version : " 3.14" # The macOS .dmg file uses the latest python version
3637
3738 steps :
3839 - name : Harden the runner (Audit all outbound calls)
40+ if : matrix.os != 'windows-latest'
3941 uses : step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2
4042 with :
4143 egress-policy : audit
4244
4345 - name : Checkout
4446 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4547
46- # https://docs.astral.sh/uv/guides/integration/github/
4748 - name : Install uv and set the Python version
4849 uses : astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
4950 with :
5051 python-version : ${{ matrix.python-version }}
5152 activate-environment : true
5253
53- - name : Install system dependencies for GUI testing
54+ - name : Install system dependencies for GUI testing on linux
55+ if : matrix.os == 'ubuntu-latest'
5456 run : |
5557 sudo apt-get update
5658 # Only install system Tcl/Tk for Python < 3.13 (newer versions bundle their own)
@@ -62,35 +64,32 @@ jobs:
6264 # Only install tools, not Tcl/Tk libraries
6365 sudo apt-get install -y scrot xdotool x11-utils gnome-screenshot
6466 fi
67+ sudo apt-get install -y xvfb libx11-6 libxrender1 libxext6 libsm6
6568 python3 --version && python3 -c "import tkinter; print(tkinter.TclVersion, tkinter.TkVersion)"
6669
67- - name : Ensure Tcl/Tk search paths
70+ - name : Install system dependencies for GUI testing on macOS
71+ if : matrix.os == 'macos-latest'
6872 run : |
69- # Only set Tcl/Tk paths for Python < 3.13
70- if [[ "${{ matrix.python-version }}" < "3.13" ]]; then
71- echo "Setting Tcl/Tk paths for Python ${{ matrix.python-version }}"
72- echo "TCL_LIBRARY=/usr/share/tcltk/tcl8.6" >> $GITHUB_ENV
73- echo "TK_LIBRARY=/usr/share/tcltk/tk8.6" >> $GITHUB_ENV
74- else
75- echo "Python ${{ matrix.python-version }} uses bundled Tcl/Tk, no custom paths needed"
76- fi
73+ brew install tcl-tk
74+ echo "LDFLAGS=-L$(brew --prefix tcl-tk)/lib" >> $GITHUB_ENV
75+ echo "CPPFLAGS=-I$(brew --prefix tcl-tk)/include" >> $GITHUB_ENV
76+ echo "PKG_CONFIG_PATH=$(brew --prefix tcl-tk)/lib/pkgconfig" >> $GITHUB_ENV
77+ python3 -c "import tkinter; print('Tk OK:', tkinter.TclVersion, tkinter.TkVersion)"
7778
7879 - name : Install dependencies and application
79- # without --editable, the coverage report is not generated correctly
8080 run : |
8181 uv pip install --editable .[dev,ci_headless_tests]
8282
8383 - name : Download ArduCopter SITL (if available)
84+ if : matrix.os == 'ubuntu-latest'
8485 run : |
85- # Create cache key based on current quarter (YYYY-Q)
8686 CURRENT_YEAR=$(date +%Y)
8787 CURRENT_MONTH=$(date +%m)
8888 QUARTER=$(( (CURRENT_MONTH-1)/3 + 1 ))
8989 CACHE_KEY="${CURRENT_YEAR}-Q${QUARTER}"
9090
9191 echo "Cache key: ${CACHE_KEY}"
9292
93- # Check if we have cached SITL files for this quarter
9493 if [ -d "sitl-cache/${CACHE_KEY}" ] && [ -f "sitl-cache/${CACHE_KEY}/arducopter" ]; then
9594 echo "Using cached SITL files from ${CACHE_KEY}"
9695 mkdir -p sitl/
@@ -99,20 +98,16 @@ jobs:
9998 echo "Downloading fresh SITL files for ${CACHE_KEY}"
10099 mkdir -p sitl/ sitl-cache/${CACHE_KEY}/
101100
102- # Download latest ArduCopter SITL from official firmware server
103101 curl -L -o sitl/arducopter https://firmware.ardupilot.org/Copter/latest/SITL_x86_64_linux_gnu/arducopter
104102 curl -L -o sitl/firmware-version.txt https://firmware.ardupilot.org/Copter/latest/SITL_x86_64_linux_gnu/firmware-version.txt
105103 curl -L -o sitl/git-version.txt https://firmware.ardupilot.org/Copter/latest/SITL_x86_64_linux_gnu/git-version.txt
106104
107- # Cache the downloaded files
108105 cp sitl/* sitl-cache/${CACHE_KEY}/
109106 fi
110107
111- # Make executable and verify
112108 chmod +x sitl/arducopter
113109 ls -la sitl/
114110
115- # Set environment variables
116111 echo "SITL_BINARY=$(pwd)/sitl/arducopter" >> $GITHUB_ENV
117112 echo "SITL_AVAILABLE=true" >> $GITHUB_ENV
118113 echo "SITL version: $(cat sitl/git-version.txt)"
@@ -132,19 +127,25 @@ jobs:
132127 continue-on-error : false
133128 run : |
134129 export LIBGL_ALWAYS_SOFTWARE=1
135- export DISPLAY=:99
136- # disable X authentication
137- export XAUTHORITY=/dev/null
138- # disable access control restrictions
139- Xvfb :99 -screen 0 1024x768x16 -ac &
140- # ensure Xvfb is fully started before running tests
141- sleep 2
142- if [ "$SITL_AVAILABLE" = "true" ]; then
143- echo "Running tests with SITL support"
144- uv run 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 "sitl or not sitl"
130+
131+ if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
132+ export DISPLAY=:99
133+ export XAUTHORITY=/dev/null
134+ Xvfb :99 -screen 0 1024x768x16 -ac &
135+ sleep 2
136+
137+ if [ "$SITL_AVAILABLE" = "true" ]; then
138+ FINAL_MARKER="sitl or not sitl"
139+ else
140+ FINAL_MARKER="not sitl"
141+ fi
142+
143+ echo "Running Ubuntu tests with markers: $FINAL_MARKER"
144+ uv run 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 "$FINAL_MARKER"
145+
145146 else
146- echo "Running tests without SITL (mocked tests only )"
147- uv run 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 sitl "
147+ echo "Running macOS headless tests (skipping GUI files entirely )"
148+ uv run 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" --ignore-glob="tests/acceptance_*.py" --ignore-glob="tests/*frontend*.py" --ignore-glob="tests/*tkinter*.py "
148149 fi
149150
150151 - name : Fix coverage paths
@@ -156,27 +157,24 @@ jobs:
156157
157158 - name : Display test results as GitHub job summary
158159 run : cat tests/results-${{ matrix.python-version }}.md >> $GITHUB_STEP_SUMMARY
159- # Use always() to always run this step to publish test results when there are test failures
160160 if : ${{ always() }}
161161
162162 - name : Upload coverage xml report
163+ if : matrix.os == 'ubuntu-latest' && always()
163164 uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
164165 with :
165166 name : coverage-${{ matrix.python-version }}-xml
166167 path : tests/*.xml
167168 retention-days : 1
168- # Use always() to always run this step to publish test results when there are test failures
169- if : ${{ always() }}
170169
171170 - name : Upload coverage report
171+ if : matrix.os == 'ubuntu-latest' && always()
172172 uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
173173 with :
174174 name : coverage-${{ matrix.python-version }}
175175 path : .coverage
176176 include-hidden-files : true
177177 retention-days : 1
178- # Use always() to always run this step to publish test results when there are test failures
179- if : ${{ always() }}
180178
181179 upload_coverage_to_coveralls :
182180 if : (github.event_name == 'push' && github.ref == 'refs/heads/master') && (success() || failure())
@@ -203,12 +201,10 @@ jobs:
203201 github-token : ${{ secrets.GITHUB_TOKEN }}
204202 files : coverage.xml
205203
206- # TODO: create a badge that presents the result of the Upload coverage xml report step
207-
208204 check_coverage :
209205 if : success() || failure()
210206 runs-on : ubuntu-latest
211- needs : pytest # This will ensure this job runs after 'pytest'
207+ needs : pytest
212208
213209 steps :
214210 - name : Harden the runner (Audit all outbound calls)
@@ -224,11 +220,10 @@ jobs:
224220 with :
225221 name : coverage-3.9
226222
227- # https://docs.astral.sh/uv/guides/integration/github/
228223 - name : Install uv and set the Python version
229224 uses : astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
230225 with :
231- python-version : ' 3.9' # Match with the coverage report Python version
226+ python-version : ' 3.9'
232227 activate-environment : true
233228
234229 - name : Install dependencies
@@ -237,7 +232,6 @@ jobs:
237232
238233 - name : Check coverage
239234 run : |
240- # Check if pytest job failed
241235 if [ "${{ needs.pytest.result }}" == "failure" ]; then
242236 echo "Pytest failed - failing coverage check"
243237 exit 1
@@ -248,11 +242,9 @@ jobs:
248242 if : always()
249243 name : " Publish Tests Results"
250244 runs-on : ubuntu-latest
251- needs : pytest # This will ensure this job runs after 'pytest'
245+ needs : pytest
252246 permissions :
253247 checks : write
254-
255- # only needed unless run with comment_mode: off
256248 pull-requests : write
257249
258250 steps :
@@ -296,7 +288,6 @@ jobs:
296288 path : badge.svg
297289
298290 - name : Upload badge to Gist
299- # Upload only for master branch
300291 if : >
301292 github.event_name == 'workflow_run' && github.event.workflow_run.head_branch == 'master' ||
302293 github.event_name != 'workflow_run' && github.ref == 'refs/heads/master'
@@ -309,7 +300,7 @@ jobs:
309300 add_coverage_to_pullrequest :
310301 if : github.event_name == 'pull_request' && (success() || failure())
311302 runs-on : ubuntu-latest
312- needs : pytest # This will ensure this job runs after 'pytest'
303+ needs : pytest
313304 permissions :
314305 contents : read
315306 pull-requests : write
0 commit comments