Windows Addon Unit Tests #151
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Windows Addon Unit Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| schedule: | |
| # Nightly drift check at 06:45 UTC — staggered 15min after the | |
| # Linux addon-unit cron (06:30) so the two runners do not contend | |
| # for the same upstream resources. Surfaces addon-side regressions | |
| # that only manifest on Windows (MSYS2 dependency drift, | |
| # path-handling assumptions, test_windows_* coverage). | |
| - cron: '45 6 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| addons: | |
| description: 'Space-separated addon names (empty = all with tests/)' | |
| default: '' | |
| gramps_repo: | |
| description: 'gramps repo (owner/name); empty = same-owner fork' | |
| default: '' | |
| gramps_ref: | |
| description: 'gramps branch/tag/sha' | |
| default: 'maintenance/gramps61' | |
| addons_source_repo: | |
| description: 'addons-source repo (owner/name); empty = same-owner fork' | |
| default: '' | |
| addons_ref: | |
| description: 'addons-source branch/tag/sha' | |
| # gramps60 by default — the branch addons are authored on. Paired | |
| # with gramps_ref=gramps61 on purpose; see the Checkout step below. | |
| default: 'maintenance/gramps60' | |
| permissions: | |
| contents: read | |
| checks: write | |
| pull-requests: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| jobs: | |
| windows-addon-unit-tests: | |
| name: '[upstream] Windows Addon Unit Tests' | |
| runs-on: windows-latest | |
| timeout-minutes: 30 | |
| # Advisory gate. Same rationale as addon-unit-tests.yml: addon tests | |
| # run upstream addon code, so failures usually reflect addon/upstream | |
| # bugs rather than testbed issues. | |
| continue-on-error: true | |
| defaults: | |
| run: | |
| shell: msys2 {0} | |
| steps: | |
| - name: Checkout testbed | |
| uses: actions/checkout@v4 | |
| with: | |
| path: gramps-testbed | |
| - name: Checkout gramps fork | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event.inputs.gramps_repo || vars.GRAMPS_REPO || format('{0}/gramps', github.repository_owner) }} | |
| ref: ${{ github.event.inputs.gramps_ref || vars.GRAMPS_REF || 'maintenance/gramps61' }} | |
| path: gramps | |
| # DELIBERATE cross-version pairing: gramps60 addon against fresh | |
| # gramps61 core — the forward-compat early-warning this job exists for. | |
| # Full rationale in addon-unit-tests.yml's Checkout addons-source step. | |
| # Do NOT "fix" this to 61/61. | |
| - name: Checkout addons-source fork | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.event.inputs.addons_source_repo || vars.ADDONS_SOURCE_REPO || format('{0}/addons-source', github.repository_owner) }} | |
| ref: ${{ github.event.inputs.addons_ref || vars.ADDONS_SOURCE_REF || 'maintenance/gramps60' }} | |
| path: addons-source | |
| - name: Set up MSYS2 UCRT64 | |
| # Kept in sync with windows-unit-tests.yml. Authoritative | |
| # reference is ../gramps/aio/build.sh on maintenance/gramps61. | |
| # UCRT64 (vs MINGW64) is required so that orjson resolves under | |
| # `pip install -e gramps[testing]`; see windows-unit-tests.yml | |
| # for the longer rationale and the upstream #2198 link. | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: UCRT64 | |
| update: true | |
| install: >- | |
| mingw-w64-ucrt-x86_64-python | |
| mingw-w64-ucrt-x86_64-python-pip | |
| mingw-w64-ucrt-x86_64-python-gobject | |
| mingw-w64-ucrt-x86_64-python-cairo | |
| mingw-w64-ucrt-x86_64-python-icu | |
| mingw-w64-ucrt-x86_64-python-lxml | |
| mingw-w64-ucrt-x86_64-python-jsonschema | |
| mingw-w64-ucrt-x86_64-python-pillow | |
| mingw-w64-ucrt-x86_64-python-maturin | |
| mingw-w64-ucrt-x86_64-rust | |
| mingw-w64-ucrt-x86_64-gexiv2 | |
| mingw-w64-ucrt-x86_64-gtk3 | |
| mingw-w64-ucrt-x86_64-osm-gps-map | |
| mingw-w64-ucrt-x86_64-goocanvas | |
| mingw-w64-ucrt-x86_64-gettext | |
| intltool | |
| - name: Install pinned-URL packages (graphviz, gspell, enchant) | |
| # Mirrors aio/build.sh — the current main-repo versions of | |
| # graphviz/gspell/enchant break pygraphviz/spellcheck, so | |
| # upstream pins the 6.0.8-era versions via `pacman -U`. Keep in | |
| # sync with aio/build.sh. See windows-unit-tests.yml for the | |
| # longer rationale. | |
| run: | | |
| pacman -U --needed --noconfirm \ | |
| https://repo.msys2.org/mingw/ucrt64/mingw-w64-ucrt-x86_64-enchant-2.6.7-5-any.pkg.tar.zst \ | |
| https://repo.msys2.org/mingw/ucrt64/mingw-w64-ucrt-x86_64-graphviz-12.2.1-4-any.pkg.tar.zst \ | |
| https://repo.msys2.org/mingw/ucrt64/mingw-w64-ucrt-x86_64-gspell-1.14.0-4-any.pkg.tar.zst | |
| - name: Set up Python venv (inherits gi/cairo/icu from pacman) | |
| # MSYS2 UCRT64 Python uses POSIX-style venv layout (bin/), not | |
| # Windows-native CPython's Scripts/ — see windows-unit-tests.yml | |
| # for the longer rationale. | |
| run: | | |
| /ucrt64/bin/python -m venv --system-site-packages .venv | |
| echo "$(cygpath -u "$GITHUB_WORKSPACE")/.venv/bin" >> "$GITHUB_PATH" | |
| .venv/bin/python -m pip install --upgrade pip | |
| - name: Restore pip cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: pip-${{ runner.os }}-${{ hashFiles('gramps-testbed/requirements-test.txt') }} | |
| restore-keys: | | |
| pip-${{ runner.os }}- | |
| - name: Install Python dependencies | |
| # See windows-unit-tests.yml for the full rationale around: | |
| # - source .venv/bin/activate (PEP 668 + $GITHUB_PATH) | |
| # - --no-build-isolation + pacman python-maturin (orjson on UCRT64) | |
| # - orjson==3.11.7 pin (mirrors aio/build.sh:83) | |
| # - import-smoke-test after build (catch ABI mismatches early) | |
| run: | | |
| source .venv/bin/activate | |
| maturin --version | |
| python -c "import maturin; print('maturin', maturin.__file__)" | |
| pip install --no-build-isolation --upgrade 'orjson==3.11.7' | |
| python -c "import orjson; print(orjson.__version__); print(orjson.dumps({'x':1})); print(orjson.loads(b'{\"y\":2}'))" | |
| pip install -r gramps-testbed/requirements-test.txt | |
| pip install -e 'gramps[testing]' | |
| python -c "import gramps.gen.lib.json_utils" | |
| - name: Install addon runtime deps (derived from requires_mod) | |
| # Auto-derive the union of requires_mod across every .gpr.py in | |
| # the checked-out addons-source/. Mirrors the Linux job; failures | |
| # are tolerated per-package so a broken transitive build (e.g. | |
| # psycopg2 without libpq) does not abort the batch — the | |
| # affected addon's tests will skip or fail in isolation. | |
| # `source .venv/bin/activate` — see Install Python dependencies. | |
| run: | | |
| source .venv/bin/activate | |
| addon_mods=$(python \ | |
| gramps-testbed/agent-work/scripts/lib/addon_python_deps.py addons-source) | |
| if [ -n "$addon_mods" ]; then | |
| echo "→ addon deps: $addon_mods" | |
| for mod in $addon_mods; do | |
| pip install "$mod" || \ | |
| echo "× $mod failed to install (continuing)" | |
| done | |
| else | |
| echo "no requires_mod declarations found" | |
| fi | |
| - name: Compile Gramps translations | |
| working-directory: gramps | |
| run: | | |
| for po in po/*.po; do | |
| lang=$(basename "$po" .po) | |
| dest="build/mo/$lang/LC_MESSAGES" | |
| mkdir -p "$dest" | |
| msgfmt "$po" -o "$dest/gramps.mo" | |
| done | |
| - name: Run addon unit tests | |
| # Mirrors agent-work/scripts/windows/run-addon-unit.sh. The filename filter | |
| # is inverted from the Linux jobs: | |
| # test_*.py general — runs here | |
| # test_linux_*.py Linux-only — skipped | |
| # test_windows_*.py Windows-only — runs here | |
| # test_integration_*.py Linux-only — skipped | |
| # cygpath -w converts POSIX paths to Windows paths for the | |
| # MSYS2-native Python interpreter, which is the form xmlrunner | |
| # writes to and GRAMPS_RESOURCES is read as. | |
| # `source .venv/bin/activate` — see Install Python dependencies. | |
| env: | |
| ADDONS: ${{ github.event.inputs.addons }} | |
| run: | | |
| source "$(cygpath -u "$GITHUB_WORKSPACE")/.venv/bin/activate" | |
| ws_posix="$(cygpath -u "$GITHUB_WORKSPACE")" | |
| results_dir="$ws_posix/gramps-testbed/test-results" | |
| mkdir -p "$results_dir" | |
| gramps_resources_win="$(cygpath -w "$ws_posix/gramps")" | |
| if [ -n "$ADDONS" ]; then | |
| addons=( $ADDONS ) | |
| else | |
| addons=() | |
| for d in "$ws_posix"/addons-source/*/tests; do | |
| [ -d "$d" ] || continue | |
| parent_name=$(basename "$(dirname "$d")") | |
| [ "$parent_name" = "addons-source" ] && continue | |
| compgen -G "$d/test_*.py" >/dev/null || continue | |
| addons+=( "$parent_name" ) | |
| done | |
| fi | |
| if [ ${#addons[@]} -eq 0 ]; then | |
| echo "no addons with tests/test_*.py were found" >&2 | |
| exit 1 | |
| fi | |
| echo "→ addon unit tests: ${addons[*]}" | |
| fail=0 | |
| for addon in "${addons[@]}"; do | |
| test_dir="$ws_posix/addons-source/$addon/tests" | |
| if [ ! -d "$test_dir" ]; then | |
| echo "× $addon: addons-source/$addon/tests/ not found" >&2 | |
| fail=1 | |
| continue | |
| fi | |
| echo | |
| echo "=== $addon ===" | |
| out_dir="$results_dir/$addon" | |
| mkdir -p "$out_dir" | |
| modules=() | |
| for f in "$ws_posix"/addons-source/"$addon"/tests/test_*.py; do | |
| [ -f "$f" ] || continue | |
| case "$(basename "$f")" in | |
| test_linux_*|test_integration_*) continue ;; | |
| esac | |
| rel="${f#$ws_posix/addons-source/}" | |
| mod="${rel%.py}" | |
| mod="${mod//\//.}" | |
| modules+=( "$mod" ) | |
| done | |
| if [ ${#modules[@]} -eq 0 ]; then | |
| echo "× $addon: no Windows-eligible test_*.py in tests/" >&2 | |
| fail=1 | |
| continue | |
| fi | |
| rc=0 | |
| ( | |
| cd "$ws_posix/addons-source" | |
| GRAMPS_RESOURCES="$gramps_resources_win" \ | |
| python -m xmlrunner "${modules[@]}" \ | |
| -o "$(cygpath -w "$out_dir")" \ | |
| -v | |
| ) || rc=$? | |
| # Coverage accounting from the JUnit XML — identical guard to the | |
| # Linux job (addon-unit-tests.yml): a wholly-skipped module still | |
| # exits 0, so exit-code-only would call it PASS. A Windows addon | |
| # whose requires_mod failed to install and whose tests all skip | |
| # would otherwise report green. cygpath -w because the | |
| # MSYS2-native Python reads Windows paths (same reason the | |
| # xmlrunner -o above uses it). | |
| read -r t s < <(python \ | |
| "$(cygpath -w "$ws_posix/gramps-testbed/agent-work/scripts/lib/junit_coverage.py")" \ | |
| "$(cygpath -w "$out_dir")") | |
| if [ "$rc" -ne 0 ]; then | |
| fail=1 | |
| elif [ "${t:-0}" -gt 0 ] && [ "${s:-0}" -eq "${t:-0}" ]; then | |
| echo "::error::$addon: all ${t} tests skipped (degraded coverage)" | |
| fail=1 | |
| fi | |
| done | |
| exit $fail | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-addon-unit-test-results | |
| path: gramps-testbed/test-results/ | |
| - name: Publish test report | |
| if: always() | |
| uses: dorny/test-reporter@v1 | |
| with: | |
| working-directory: gramps-testbed | |
| name: '[upstream] Windows Addon Unit Tests' | |
| path: test-results/**/*.xml | |
| reporter: java-junit | |
| fail-on-error: false # report, don't gate — see interface-tests.yml |