ci(smoke): cover teardown, state transitions and fake hardware

The smoke test only exercised steady-state rendering and killed waybar with
SIGTERM, so whole crash classes were invisible: exit-time use-after-free
(#5182), module state transitions (#5183) and hardware-backed modules (#5179).
Add tiers that hit those paths under ASan (+ _GLIBCXX_ASSERTIONS):

- lib.sh: assert_clean_exit (SIGINT teardown -> checks segfault/abort + ASan
  report emitted during destruction), output hotplug helpers, signal/reload,
  WAYBAR_WRAP hook, opt-in leak detection.
- lifecycle.sh (gating): clean exit, runtime output hotplug (Bar/module
  destroy), toggle/reload churn, fast-interval teardown race (#5182 class).
- fuzz.sh (gating): pathological custom-backend output (empty, nonzero exit,
  invalid JSON, huge, non-UTF8, empty format).
- coverage.sh (gating): every Factory module must be classified for smoke
  coverage; a new unlisted module fails the job (#5179 slipped through).
- modules.sh: also render the whole matrix inside a group; SIGINT teardown per
  module instead of SIGTERM.
- state.sh (best-effort): real mpd driven through an empty queue (#5183) +
  stop/clear; pulseaudio + slider on a null sink.
- hardware.sh (best-effort): backlight, backlight/slider (#5179) and battery
  via umockdev.
- leakcheck.sh (report-only): clean-exit run under LSan.
This commit is contained in:
Alex
2026-07-05 22:21:46 +02:00
parent 445e2aec1f
commit 9a10949938
10 changed files with 558 additions and 13 deletions
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
# Tier — custom-backend fuzz. The custom module's exec output flows straight into
# formatting / sanitising / (optionally) JSON parsing, so feed it the pathological
# inputs that tend to segfault (empty, non-zero exit, invalid JSON, huge, non-UTF8,
# empty format) and assert no crash / critical, including on clean exit.
#
# Usage: fuzz.sh
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=lib.sh
source "$DIR/lib.sh"
smoke::setup
smoke::start_compositor
trap smoke::stop EXIT
fail=0
# run_case <name> <custom-module-json>
run_case() {
local name="$1" mod="$2" cfg
echo "::group::fuzz: $name"
cfg="$(mktemp --suffix=.json)"
cat > "$cfg" <<EOF
{ "layer": "top", "position": "top", "height": 30,
"modules-center": ["custom/x"], "custom/x": $mod }
EOF
smoke::launch_waybar "$cfg" "$DIR/style.css" 4
smoke::assert_alive || fail=1
smoke::assert_clean_exit || fail=1
echo "::endgroup::"
}
run_case "empty output" '{"exec":"true","interval":"once"}'
run_case "nonzero exit" '{"exec":"echo hi; exit 3","interval":"once"}'
run_case "invalid json" '{"exec":"echo not-json","return-type":"json","interval":"once"}'
run_case "huge output" '{"exec":"yes A | head -c 100000","interval":"once"}'
run_case "non-utf8 bytes" '{"exec":"printf \\377\\376\\375","interval":"once"}'
run_case "empty format" '{"exec":"echo x","interval":"once","format":""}'
run_case "newlines flood" '{"exec":"yes | head -n 500","interval":"once"}'
[ "$fail" = 0 ] || { echo "::error::fuzz tier failed"; exit 1; }
echo "✓ custom-backend fuzz passed"