#!/usr/bin/env bash

# Telling somebody when a program crashes.
#
# On GNOME, ABRT says so. Under a hand-assembled Hyprland desktop nothing did,
# and applications died silently, which is most of how "Linux is flaky" gets
# earned. Fedora ships systemd-coredump by default, so the information was
# already there and nobody was reading it.
#
# What must hold:
#
#   1. Once per program per session. This machine's portal backend crashes
#      between eleven and sixty times a day; a notification per crash would be
#      one every few minutes for something nobody can act on. The first is
#      news, the fortieth is why people turn notifications off.
#   2. Another user's crash is not reported. It is not this session's business
#      and it would leak what they are running.
#   3. The program is named by its executable, not by the kernel's comm field,
#      which is truncated to fifteen characters -- "panama-test-cra" for a
#      program called panama-test-crasher.
#   4. It waits for the notification server. The crash most worth reporting is
#      the one that took the shell down with it.
#   5. It follows from now rather than replaying the boot, so a session that
#      starts after a crash does not open with a notification about something
#      already lived through.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
watcher="$repo_dir/bin/panama-crash-watch"
unit="$repo_dir/config/local/share/systemd/user/panama-crash-watch.service"
autostart="$repo_dir/config/dot/hypr/autostart.lua"

findings=()
note() { findings+=("$1"); }

[[ -x "$watcher" ]] || { printf 'crash watch contract: %s is not executable\n' "$watcher" >&2; exit 1; }

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
calls="$work/calls"
stub="$work/bin"
mkdir -p "$stub"

# Two crashes of one program, one of another, and one belonging to somebody
# else. journalctl is replaced by a stub that emits them and exits, so the
# watcher's follow loop terminates instead of hanging the test.
uid="$(id -u)"
cat >"$stub/journalctl" <<STUB
#!/usr/bin/env bash
printf '%s\n' \\
  '{"COREDUMP_UID":"$uid","COREDUMP_EXE":"/usr/bin/panama-test-crasher","COREDUMP_COMM":"panama-test-cra"}' \\
  '{"COREDUMP_UID":"$uid","COREDUMP_EXE":"/usr/bin/panama-test-crasher","COREDUMP_COMM":"panama-test-cra"}' \\
  '{"COREDUMP_UID":"$uid","COREDUMP_EXE":"/usr/bin/other-program","COREDUMP_COMM":"other-program"}' \\
  '{"COREDUMP_UID":"99999","COREDUMP_EXE":"/usr/bin/someone-elses","COREDUMP_COMM":"someone-elses"}'
STUB
chmod +x "$stub/journalctl"

cat >"$stub/notify-send" <<STUB
#!/usr/bin/env bash
printf '%s\n' "\$*" >>"$calls"
STUB
chmod +x "$stub/notify-send"

# The bus is already up, so the wait loop falls straight through.
cat >"$stub/busctl" <<'STUB'
#!/usr/bin/env bash
exit 0
STUB
chmod +x "$stub/busctl"

: >"$calls"
PATH="$stub:$PATH" timeout 20 "$watcher" >/dev/null 2>&1

# ── 1. Once per program ─────────────────────────────────────────────────────

crasher_notices="$(grep -c 'panama-test-crasher' "$calls" || true)"
(( crasher_notices == 1 )) \
    || note "a program that crashed twice produced $crasher_notices notifications; it must produce one per session"

# A different program is still news.
grep -q 'other-program' "$calls" \
    || note 'a second, different program crashing was not reported'

# ── 2. Somebody else's crash is not ours ────────────────────────────────────

grep -q 'someone-elses' "$calls" \
    && note "another user's crash was reported, which leaks what they are running"

# ── 3. The name is not the truncated one ────────────────────────────────────

grep -q 'panama-test-cra ' "$calls" \
    && note 'the notification uses the truncated kernel comm field rather than the executable name'

# ── 4 & 5. How it listens ───────────────────────────────────────────────────

grep -q 'org.freedesktop.Notifications' "$watcher" \
    || note 'the watcher does not wait for the notification server, so a shell crash would report to nobody'
grep -q -- '-f -n 0' "$watcher" \
    || note 'the watcher replays the journal rather than following from now, so a session would open with old crashes'
grep -q 'MESSAGE_ID=' "$watcher" \
    || note 'the watcher matches on log text rather than the coredump message id'

# ── Installed and started ───────────────────────────────────────────────────

[[ -r "$unit" ]] || note 'there is no user unit for the crash watcher'
grep -q 'panama-crash-watch' "$autostart" \
    || note 'nothing starts the crash watcher at login'
grep -q 'PANAMA_PATH' "$unit" \
    || note 'the unit hardcodes the repository path, so a clone elsewhere would not start'

if (( ${#findings[@]} > 0 )); then
    printf 'crash watch contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'crash watch contract: PASS\n'
