Files
Waybar/test/smoke/a11y.py
T
Alex eb10a511f0 ci(smoke): sanitizer build + module/interaction/a11y/layout coverage
Turn the smoke test into a real runtime safety net. Waybar is now built with
AddressSanitizer and exercised end to end in a headless compositor:

- Tier 0: run everything under ASan; fail on ASan reports and Gtk/GLib
  criticals in the log (lib.sh assert_clean)
- Tier 1: per-module render matrix (modules.sh) — each headless-safe module
  rendered in isolation
- Tier 2: pointer interaction (interact.sh) — inject clicks via sway, assert
  on-click side effect and format-alt toggle; AT-SPI assertions (a11y.sh/.py)
  check module labels semantically instead of by pixels
- Tier 3: layout matrix (positions.sh) — top/bottom/left + HiDPI scale 2;
  second compositor run under labwc

Shared helpers extracted to lib.sh. Adds a workflow_dispatch `bless` input to
regenerate the golden reference in one click. Trigger push only on master to
avoid duplicate PR runs. AT-SPI and labwc steps are continue-on-error.
2026-07-05 10:25:49 +02:00

59 lines
1.5 KiB
Python

#!/usr/bin/env python3
"""Tier 2 — assert on Waybar's accessibility (AT-SPI) tree instead of pixels.
Walks the running Waybar's GTK accessibility tree and checks that the expected
module labels are exposed. This is robust to fonts/anti-aliasing (unlike the
golden image) and verifies actual widget content.
"""
import sys
import pyatspi
EXPECT = ["Waybar", "CI smoke test", "12:34"]
def collect(acc, out):
try:
name = acc.name or ""
except Exception:
name = ""
if name:
out.append(name)
try:
count = acc.childCount
except Exception:
count = 0
for i in range(count):
try:
child = acc.getChildAtIndex(i)
except Exception:
child = None
if child is not None:
collect(child, out)
def main():
desktop = pyatspi.Registry.getDesktop(0)
texts, found = [], False
for i in range(desktop.childCount):
app = desktop.getChildAtIndex(i)
if app is None:
continue
if (app.name or "").lower().startswith("waybar"):
found = True
collect(app, texts)
print("AT-SPI labels seen on waybar:", texts)
if not found:
print("ERROR: waybar did not appear on the AT-SPI bus")
return 1
missing = [e for e in EXPECT if not any(e in t for t in texts)]
if missing:
print("ERROR: expected labels missing from AT-SPI tree:", missing)
return 1
print("OK: all expected labels present via AT-SPI")
return 0
if __name__ == "__main__":
sys.exit(main())