screen-locker/.github/workflows/python-tests.yml
Krzysztof kuhy Rudnicki f04b0d0efd Commit gate: pytest 67.7 s serial -> 9.4 s, screens-fit hook 3.6 s -> 0.7 s
- addopts: -n 4 --dist worksteal (fixed, not auto: 24 workers would break
  capped.sh's 4 GiB). CI's own -n auto still wins.
- coverage core = sysmon (PEP 669): ~40% less tracing overhead, same data.
- shutdown ceiling invariant: one plain-function patch of earned_units per
  day instead of a MagicMock per unit (~5000 per case); every day and every
  case still checked.
- poke/web server tests: serve_forever(poll_interval=0.01), so shutdown()
  no longer waits 0.5 s per test.
- autouse _no_real_ntp: check_clock_skew sent real UDP to pool.ntp.org from
  every verification test, and a lost reply cost a random test 5 s.
- temperature hard-timeout test: pins the default to 5 s and times the
  mechanism at 0.1 s instead of waiting 5 real seconds.
- _xvfb_reexec: Xvfb -displayfd instead of xvfb-run's fixed 3 s sleep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 17:27:27 +01:00

147 lines
5.2 KiB
YAML

name: Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
# Bucket count for the Flutter suite; the matrix below lists 0..N-1.
# 10, not more: the account runs at most 20 jobs at once, and a push also
# starts ~10 others (pre-commit, freshness, the rest of this file). At 12
# three shards queued for up to 34 s and the workflow took 91 s.
FLUTTER_SHARDS: 10
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
# uv with a cached wheel store installs requirements.txt in seconds;
# plain pip took ~26 s of a ~70 s job.
- uses: astral-sh/setup-uv@v10.2.0
with:
enable-cache: true
cache-dependency-glob: requirements.txt
- name: Install dependencies
run: uv pip install --system -r requirements.txt
# -n auto spreads the suite over every runner core (pytest-xdist is in
# requirements.txt); pytest-cov merges the workers' coverage, and every
# worker loads conftest.py, so the state-file redirects hold in each.
# -q cancels pyproject's -v: 1368 test names were most of the log.
- name: Run tests with coverage
run: python -m pytest -n auto -p no:sugar -q
# The pytest suite replaces the whole `tk` module so it can never reach a
# display, which means the lock-screen widget behaviour it guards cannot be
# asserted there. This job runs the real-Tk half against a throwaway X
# server. See scripts/verify_lock_popup_safety.py.
lock-popup-safety:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- uses: astral-sh/setup-uv@v10.2.0
with:
enable-cache: true
cache-dependency-glob: requirements.txt
# No apt step: the runner image already ships Xvfb, and
# setup-python's interpreter has its own tkinter (python3-tk is for the
# system python, which this job never runs). The apt-get update +
# install cost ~35 s. If either ever goes missing the check does not
# skip -- _xvfb_reexec returns 1 without Xvfb, and the import fails
# without tkinter.
- name: Install dependencies
run: |
uv pip install --system -r requirements.txt
# The script imports screen_locker directly rather than through
# pytest, which would put the rootdir on sys.path for it.
uv pip install --system -e . --no-deps
- name: Sport selector survives gatelock's recovery tick
# Run as a module (like the screen-fits gate) so scripts/ resolves as a
# package and the check can import the helpers it was split into.
run: python3 -m scripts.verify_lock_popup_safety
# The two coverage bars below used to run only in the local pre-push hook,
# which became a 1-second stamp check on 2026-09-20 (pre-commit's manual
# stage holds them now). Nothing else in CI runs the Flutter or web suites,
# so these jobs are the only place the 100% bars are enforced on every push.
#
# The Flutter suite took ~150 s on one runner, nearly all of it compiling 77
# test files, so it runs as FLUTTER_SHARDS buckets of whole files in
# parallel and flutter-coverage applies the unchanged 100% bar and
# every-lib-file-present guard to their union.
flutter-shard:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
name: flutter test (shard ${{ matrix.shard }})
steps:
- uses: actions/checkout@v4
# The slim cached SDK (utils/flutter-setup): subosito's full-SDK
# cache cost ~38 s to restore even on a hit.
- uses: kuhyx/utils/flutter-setup@flutter-setup-v1
with:
flutter-version: 3.47.7
working-directory: stronglift_replacement/workout_app
- name: flutter test --coverage, bucket ${{ matrix.shard }}
run: bash scripts/check_flutter_coverage.sh --shard "${{ matrix.shard }}/$FLUTTER_SHARDS"
- uses: actions/upload-artifact@v4
with:
name: flutter-lcov-${{ matrix.shard }}
path: stronglift_replacement/workout_app/coverage/lcov.info
if-no-files-found: error
retention-days: 1
flutter-coverage:
needs: flutter-shard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: flutter-lcov-*
path: lcov-shards
- name: flutter test --coverage, 100% of lib/ (workout app)
run: bash scripts/check_flutter_coverage.sh --merged "$FLUTTER_SHARDS" lcov-shards/*/lcov.info
web-coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
package_json_file: web/package.json
- uses: actions/setup-node@v4
with:
node-version: 24.22.1
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: vitest --coverage, 100% (status web UI)
run: |
pnpm --dir web install --frozen-lockfile
pnpm --dir web run coverage