Parent Call told rooms they had never been set up. Nothing about the rooms was wrong. Read from the church broker: …/coordinator and …/pending are retained and …/mode is absent, and the coordinator's own source says why — it publishes the retained mode only in answer to an operator writing to …/mode/set, and nobody ever has. Every classroom waited a second and a half for a message that will never exist. A missing mode is now read as the coordinator's own default rather than as an unconfigured room: the room offers the call and lets the sanctuary end answer, because that is the end that knows. A mode that really is off still refuses, in the coordinator's words, and a broker that is missing or unreachable still says which of those it is. Proved against a rig broker made to match the church's exactly, with a call landing on the sanctuary mock. The card is anchored to the bottom-left corner instead of centred, by layer shell, which is what the disc player's cover has used under KWin for months. Anchoring rather than positioning is what makes it right at 1080p and at 4K without a coordinate anywhere. It falls back to the centred card if the library is missing. Notifications are sized for a room. Every Plasma measurement is a grid unit and a grid unit is the height of the general font, so the font is the lever — 15 point instead of Breeze's 10, which takes the card from about 350x110 to about 410x145 at 100% and doubles again at 200%. Text, icon, padding and buttons move together. The chime stays off. No shell package: that path cost the panel and is not coming back. And the product name is gone from everything a teacher sees. The notification card is from Classroom, the disc player's from DVD. Co-Authored-By: Claude Opus 5 <[email protected]>
309 lines
9.6 KiB
Bash
Executable File
309 lines
9.6 KiB
Bash
Executable File
#!/bin/bash
|
|
set -uo pipefail
|
|
|
|
# Tests the session resolution in classroom-notify-test.
|
|
#
|
|
# The compositor is stood in for by a real background process started with a known
|
|
# environment, so /proc/<pid>/environ is genuine rather than mocked. id,
|
|
# pgrep, runuser and notify-send are shimmed onto PATH so every branch
|
|
# can be reached from an ordinary account.
|
|
#
|
|
# tests/test-classroom-notify-test.sh
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
SCRIPT="$ROOT/scripts/classroom-notify"
|
|
|
|
WORK="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK"; kill "${FAKE_SESSION_PID:-}" 2>/dev/null' EXIT
|
|
|
|
passed=0
|
|
failed=0
|
|
|
|
check() {
|
|
local label="$1" expected="$2" actual="$3"
|
|
|
|
if [[ "$actual" == *"$expected"* ]]; then
|
|
echo " ok $label"
|
|
passed=$((passed + 1))
|
|
else
|
|
echo " FAIL $label"
|
|
echo " expected to contain: $expected"
|
|
echo " got: $actual"
|
|
failed=$((failed + 1))
|
|
fi
|
|
}
|
|
|
|
check_absent() {
|
|
local label="$1" unexpected="$2" actual="$3"
|
|
|
|
if [[ "$actual" != *"$unexpected"* ]]; then
|
|
echo " ok $label"
|
|
passed=$((passed + 1))
|
|
else
|
|
echo " FAIL $label"
|
|
echo " expected NOT to contain: $unexpected"
|
|
echo " got: $actual"
|
|
failed=$((failed + 1))
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------- shims
|
|
|
|
mkdir -p "$WORK/bin"
|
|
|
|
cat > "$WORK/bin/id" <<'SHIM'
|
|
#!/bin/bash
|
|
if [[ "${1:-}" == "-u" && -n "${2:-}" ]]; then
|
|
echo "$FAKE_CLASSROOM_UID"
|
|
exit 0
|
|
fi
|
|
if [[ "${1:-}" == "-u" ]]; then
|
|
echo "$FAKE_CURRENT_UID"
|
|
exit 0
|
|
fi
|
|
exec /usr/bin/id "$@"
|
|
SHIM
|
|
|
|
cat > "$WORK/bin/pgrep" <<'SHIM'
|
|
#!/bin/bash
|
|
case "${!#}" in
|
|
kwin_wayland)
|
|
[[ -n "${FAKE_SESSION_PID:-}" ]] || exit 1
|
|
echo "$FAKE_SESSION_PID"
|
|
;;
|
|
plasmashell)
|
|
[[ "${FAKE_SHELL:-1}" == "1" ]] || exit 1
|
|
echo 4242
|
|
;;
|
|
*)
|
|
exit 1
|
|
;;
|
|
esac
|
|
SHIM
|
|
|
|
cat > "$WORK/bin/runuser" <<'SHIM'
|
|
#!/bin/bash
|
|
echo "runuser $*" >> "$RUNUSER_LOG"
|
|
shift 2
|
|
[[ "${1:-}" == "--" ]] && shift
|
|
exec "$@"
|
|
SHIM
|
|
|
|
cat > "$WORK/bin/notify-send" <<'SHIM'
|
|
#!/bin/bash
|
|
{
|
|
echo "args: $*"
|
|
echo "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-}"
|
|
echo "DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS:-}"
|
|
echo "WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-}"
|
|
} >> "$NOTIFY_LOG"
|
|
SHIM
|
|
|
|
chmod +x "$WORK/bin"/*
|
|
|
|
export PATH="$WORK/bin:$PATH"
|
|
export NOTIFY_LOG="$WORK/notify.log"
|
|
export RUNUSER_LOG="$WORK/runuser.log"
|
|
export FAKE_CLASSROOM_UID=4000
|
|
export FAKE_CURRENT_UID=4000
|
|
|
|
RUNTIME="$WORK/run"
|
|
mkdir -p "$RUNTIME"
|
|
|
|
# A real unix socket, because the script checks the bus is actually there.
|
|
python3 -c 'import socket, sys; socket.socket(socket.AF_UNIX).bind(sys.argv[1])' \
|
|
"$RUNTIME/bus"
|
|
|
|
run() {
|
|
: > "$NOTIFY_LOG"
|
|
: > "$RUNUSER_LOG"
|
|
"$SCRIPT" "$@" 2>&1
|
|
}
|
|
|
|
start_fake_session() {
|
|
env "$@" sleep 300 &
|
|
FAKE_SESSION_PID=$!
|
|
export FAKE_SESSION_PID
|
|
# Give the exec time to land so /proc/<pid>/environ is populated.
|
|
while [[ ! -r "/proc/$FAKE_SESSION_PID/environ" ]]; do sleep 0.05; done
|
|
}
|
|
|
|
stop_fake_session() {
|
|
[[ -n "${FAKE_SESSION_PID:-}" ]] && kill "$FAKE_SESSION_PID" 2>/dev/null
|
|
wait "${FAKE_SESSION_PID:-}" 2>/dev/null
|
|
unset FAKE_SESSION_PID
|
|
}
|
|
|
|
unset WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS
|
|
|
|
# --------------------------------------------- 1. resolve from environ
|
|
|
|
echo "1. resolves the session from the compositor's environment"
|
|
|
|
start_fake_session \
|
|
XDG_RUNTIME_DIR="$RUNTIME" \
|
|
DBUS_SESSION_BUS_ADDRESS="unix:path=$RUNTIME/bus" \
|
|
WAYLAND_DISPLAY=wayland-7
|
|
|
|
out="$(run)"
|
|
check "reports delivery to the classroom session" "Sent normal notification to the classroom desktop session" "$out"
|
|
|
|
log="$(cat "$NOTIFY_LOG")"
|
|
check "runtime dir taken from the compositor" "XDG_RUNTIME_DIR=$RUNTIME" "$log"
|
|
check "bus address taken from the compositor" "DBUS_SESSION_BUS_ADDRESS=unix:path=$RUNTIME/bus" "$log"
|
|
check "wayland display from the compositor" "WAYLAND_DISPLAY=wayland-7" "$log"
|
|
check "styling preserved" "--app-name=Classroom" "$log"
|
|
check "default sample title" "DVD Ready" "$log"
|
|
check "default sample body" "A disc is ready to play." "$log"
|
|
|
|
# ------------------------------------------------- 2. dry run reporting
|
|
|
|
echo "2. --dry-run reports the resolved session and sends nothing"
|
|
|
|
out="$(run --dry-run)"
|
|
check "shows the compositor pid" "compositor pid: $FAKE_SESSION_PID" "$out"
|
|
check "shows the runtime dir" "XDG_RUNTIME_DIR: $RUNTIME" "$out"
|
|
check "shows the wayland display" "WAYLAND_DISPLAY: wayland-7" "$out"
|
|
check_absent "nothing was sent" "args:" "$(cat "$NOTIFY_LOG")"
|
|
|
|
# ------------------------------------------------------ 3. urgency/args
|
|
|
|
echo "3. urgency and custom text still work"
|
|
|
|
out="$(run --critical "Parent Call" "Room 3 needs a parent.")"
|
|
log="$(cat "$NOTIFY_LOG")"
|
|
check "critical urgency passed" "--urgency=critical" "$log"
|
|
check "custom title passed" "Parent Call" "$log"
|
|
check "custom body passed" "Room 3 needs a parent." "$log"
|
|
|
|
# ------------------------------------------------ 4. root uses runuser
|
|
|
|
echo "4. root delivers as the classroom user"
|
|
|
|
FAKE_CURRENT_UID=0
|
|
out="$(run)"
|
|
check "runuser targets classroom" "runuser -u classroom --" "$(cat "$RUNUSER_LOG")"
|
|
check "notification still sent" "--app-name=Classroom" "$(cat "$NOTIFY_LOG")"
|
|
FAKE_CURRENT_UID=4000
|
|
|
|
# ------------------------------------ 5. unprivileged admin is told how
|
|
|
|
echo "5. an unprivileged administrator gets told what to do"
|
|
|
|
FAKE_CURRENT_UID=9999
|
|
out="$(run)"
|
|
check "explains it needs root" "run this as root (sudo classroom-notify)" "$out"
|
|
check_absent "nothing was sent" "args:" "$(cat "$NOTIFY_LOG")"
|
|
FAKE_CURRENT_UID=4000
|
|
|
|
# ------------------------------- 6. the shell must already be up
|
|
|
|
echo "6. refuses to let D-Bus start a notification service"
|
|
|
|
export FAKE_SHELL=0
|
|
out="$(run)"
|
|
check "explains the shell is not running" "the classroom shell is not running" "$out"
|
|
check_absent "nothing was sent" "args:" "$(cat "$NOTIFY_LOG")"
|
|
unset FAKE_SHELL
|
|
|
|
stop_fake_session
|
|
|
|
# --------------------------------- 7. display derived from the lock fd
|
|
|
|
echo "7. derives the display from the compositor's wayland lock when environ has none"
|
|
|
|
touch "$RUNTIME/wayland-9.lock"
|
|
env -u WAYLAND_DISPLAY \
|
|
XDG_RUNTIME_DIR="$RUNTIME" \
|
|
DBUS_SESSION_BUS_ADDRESS="unix:path=$RUNTIME/bus" \
|
|
bash -c "exec 9< '$RUNTIME/wayland-9.lock'; sleep 300" &
|
|
FAKE_SESSION_PID=$!
|
|
export FAKE_SESSION_PID
|
|
while [[ ! -r "/proc/$FAKE_SESSION_PID/fd/9" ]]; do sleep 0.05; done
|
|
|
|
out="$(run --dry-run)"
|
|
check "display derived from the lock" "WAYLAND_DISPLAY: wayland-9" "$out"
|
|
|
|
stop_fake_session
|
|
|
|
# ------------------------------------------- 8. no session at all
|
|
|
|
echo "8. says so clearly when no classroom session is running"
|
|
|
|
out="$(run)"
|
|
check "explains there is no session" "no classroom desktop session is running" "$out"
|
|
check_absent "nothing was sent" "args:" "$(cat "$NOTIFY_LOG")"
|
|
|
|
# ------------------------------------- 9. falls back when environ is thin
|
|
|
|
echo "9. falls back to session defaults when the compositor's environment is unreadable"
|
|
|
|
start_fake_session XDG_RUNTIME_DIR="$RUNTIME"
|
|
|
|
out="$(run --dry-run)"
|
|
check "derives the bus from the runtime dir" \
|
|
"DBUS_SESSION_BUS_ADDRESS: unix:path=$RUNTIME/bus" "$out"
|
|
|
|
out="$(run)"
|
|
check "still delivers" "Sent normal notification to the classroom desktop session" "$out"
|
|
check "bus reached notify-send" "DBUS_SESSION_BUS_ADDRESS=unix:path=$RUNTIME/bus" \
|
|
"$(cat "$NOTIFY_LOG")"
|
|
|
|
stop_fake_session
|
|
|
|
# ------------------------------------------ 10. missing bus is explained
|
|
|
|
echo "10. explains a missing session bus instead of hanging"
|
|
|
|
MISSING="$WORK/empty"
|
|
mkdir -p "$MISSING"
|
|
start_fake_session XDG_RUNTIME_DIR="$MISSING"
|
|
|
|
out="$(run)"
|
|
check "names the missing socket" "bus socket $MISSING/bus does not exist" "$out"
|
|
check_absent "nothing was sent" "args:" "$(cat "$NOTIFY_LOG")"
|
|
|
|
stop_fake_session
|
|
|
|
# ----------------------------- 11. already inside the classroom session
|
|
|
|
echo "11. sends directly when already inside the classroom session"
|
|
|
|
# No compositor process to find: an in-session caller must not need one.
|
|
export WAYLAND_DISPLAY=wayland-4
|
|
export DBUS_SESSION_BUS_ADDRESS="unix:path=$RUNTIME/bus"
|
|
export XDG_RUNTIME_DIR="$RUNTIME"
|
|
|
|
out="$(run)"
|
|
check "reports delivery to this session" "Sent normal notification to this session" "$out"
|
|
check "notification was sent" "--app-name=Classroom" "$(cat "$NOTIFY_LOG")"
|
|
check "inherits the session display" "WAYLAND_DISPLAY=wayland-4" "$(cat "$NOTIFY_LOG")"
|
|
check_absent "did not go through runuser" "runuser" "$(cat "$RUNUSER_LOG")"
|
|
|
|
out="$(run --dry-run)"
|
|
check "dry run names this session" "Would notify this session" "$out"
|
|
|
|
unset WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR
|
|
|
|
# --------------------------------- 12. classroom-notify-test delegates
|
|
|
|
echo "12. the sample sender delegates to classroom-notify"
|
|
|
|
export WAYLAND_DISPLAY=wayland-4
|
|
export DBUS_SESSION_BUS_ADDRESS="unix:path=$RUNTIME/bus"
|
|
export XDG_RUNTIME_DIR="$RUNTIME"
|
|
|
|
: > "$NOTIFY_LOG"
|
|
out="$("$ROOT/scripts/classroom-notify-test" 2>&1)"
|
|
check "sample still reports being sent" "Sent normal notification" "$out"
|
|
check "sample title unchanged" "DVD Ready" "$(cat "$NOTIFY_LOG")"
|
|
check "sample body unchanged" "A disc is ready to play." "$(cat "$NOTIFY_LOG")"
|
|
|
|
unset WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR
|
|
|
|
# ----------------------------------------------------------- summary
|
|
|
|
echo
|
|
echo "passed: $passed failed: $failed"
|
|
[[ "$failed" -eq 0 ]]
|