Unit: Tiny Pebbles' file (desktop 10, panel 1), a healthy River Kids file left byte for byte, a late desktop beside a good one, idempotence, the refusal under a running shell. Lab: Plasma makes a bare desktop itself through a new activity, and the screen is read, not the file. Container gate: the installed package's fallback is the package, and its repair fixes the room's file and leaves the panel alone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
239 lines
10 KiB
Bash
Executable File
239 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
# Does every desktop show the classroom's wallpaper — including one Plasma
|
|
# makes by itself after the layout has run?
|
|
#
|
|
# Tiny Pebbles came through the 0.2.2 upgrade with its desktop numbered 10
|
|
# and nothing configured on it, and showed Plasma's own purple wallpaper.
|
|
# The layout only reaches the desktops that exist while it runs, and a
|
|
# room whose updater reboots it at three in the morning with the
|
|
# television off has none then. The lab cannot switch a television on, so
|
|
# it makes Plasma create a desktop the other way it does, through a new
|
|
# activity: the same code in the shell, and measured to give the same
|
|
# bare containment.
|
|
#
|
|
# tools/lab/wallpaper-check against the running lab session
|
|
#
|
|
# Each answer is read off the screen, not out of a file: a configuration
|
|
# can say anything, and the question is what the room sees. The sky at the
|
|
# top of the classroom's picture is pale blue; Plasma's is purple.
|
|
set -uo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
LAB="$ROOT/tools/lab/lab"
|
|
NAME="${LAB_NAME:-classroom-v2-lab}"
|
|
APPLETS=/home/classroom/.config/plasma-org.kde.plasma.desktop-appletsrc
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
check() {
|
|
if [[ "$2" == "$3" ]]; then
|
|
printf ' \033[32mPASS\033[0m %-58s %s\n' "$1" "$3"
|
|
PASS=$((PASS + 1))
|
|
else
|
|
printf ' \033[31mFAIL\033[0m %-58s got [%s] want [%s]\n' "$1" "$3" "$2"
|
|
FAIL=$((FAIL + 1))
|
|
fi
|
|
}
|
|
|
|
section() { printf '\n%s\n' "$1"; }
|
|
in_lab() { docker exec "$NAME" bash -c "$1" 2>/dev/null; }
|
|
# The same, with this script's input handed through — for a program on a
|
|
# here-document. Without -i docker gives it nothing to read, and it runs
|
|
# on nothing and says nothing.
|
|
in_lab_fed() { docker exec -i "$NAME" bash -c "$1"; }
|
|
user() { "$LAB" run "$@" 2>/dev/null; }
|
|
activities() {
|
|
user busctl --user call org.kde.ActivityManager /ActivityManager/Activities \
|
|
org.kde.ActivityManager.Activities "$@"
|
|
}
|
|
|
|
# What is on the screen at the top middle, where the classroom's picture
|
|
# is sky and Plasma's is purple: "classroom", "plasma", or the colour.
|
|
screen() {
|
|
"$LAB" outer grim -t ppm - 2>/dev/null | python3 -c '
|
|
import sys
|
|
data = sys.stdin.buffer.read()
|
|
fields, at = [], 0
|
|
while len(fields) < 4:
|
|
while data[at:at + 1].isspace():
|
|
at += 1
|
|
end = at
|
|
while not data[end:end + 1].isspace():
|
|
end += 1
|
|
fields.append(data[at:end])
|
|
at = end
|
|
width, height = int(fields[1]), int(fields[2])
|
|
pixels = data[at + 1:]
|
|
total = [0, 0, 0]
|
|
count = 0
|
|
for y in range(20, 140, 4):
|
|
for x in range(width * 2 // 5, width * 3 // 5, 8):
|
|
offset = (y * width + x) * 3
|
|
for channel in range(3):
|
|
total[channel] += pixels[offset + channel]
|
|
count += 1
|
|
r, g, b = (value / count for value in total)
|
|
if b > r + 25 and g > r + 10:
|
|
print("classroom")
|
|
elif r > g + 25 and b > g + 25:
|
|
print("plasma")
|
|
else:
|
|
print(f"rgb({r:.0f},{g:.0f},{b:.0f})")
|
|
'
|
|
}
|
|
|
|
# The shell writes its file a few seconds after a change, not at once.
|
|
settle() { sleep "${1:-35}"; }
|
|
|
|
# The shell's file, read as groups rather than grepped, so nothing here
|
|
# can pick a line out of the wrong containment.
|
|
#
|
|
# applets panels each panel's number, its applets' numbers and plugins
|
|
# and their order. Not the panel's text: the shell adds
|
|
# keys of its own to applets as soon as it runs (popup
|
|
# sizes, the volume's migration), and that is Plasma,
|
|
# not the repair. tests/ checks the repair's own
|
|
# output byte for byte.
|
|
# applets desktops the desktop containments as "id:image" or "id:none",
|
|
# found by plugin and never by number
|
|
applets() {
|
|
in_lab_fed "python3 - $1 $APPLETS" <<'PY'
|
|
import re, sys
|
|
mode, path = sys.argv[1], sys.argv[2]
|
|
groups, name = {}, None
|
|
for line in open(path).read().splitlines():
|
|
if line.startswith("[") and line.endswith("]"):
|
|
name = line
|
|
groups[name] = []
|
|
elif name and line:
|
|
groups[name].append(line)
|
|
def top(cid):
|
|
return groups.get(f"[Containments][{cid}]", [])
|
|
ids = [m.group(1) for g in groups for m in [re.match(r"^\[Containments\]\[(\d+)\]$", g)] if m]
|
|
if mode == "panels":
|
|
for c in ids:
|
|
if "plugin=org.kde.panel" not in top(c):
|
|
continue
|
|
applets = sorted(
|
|
(int(m.group(1)), line[len("plugin=org.kde.plasma."):])
|
|
for g in groups
|
|
for m in [re.match(r"^\[Containments\]\[%s\]\[Applets\]\[(\d+)\]$" % c, g)] if m
|
|
for line in groups[g] if line.startswith("plugin=")
|
|
)
|
|
order = [l[len("AppletOrder="):] for l in groups.get(f"[Containments][{c}][General]", [])
|
|
if l.startswith("AppletOrder=")]
|
|
print(f"panel {c}:", " ".join(f"{n}={name}" for n, name in applets), "order", *order)
|
|
else:
|
|
image = "Image=file:///usr/share/backgrounds/ekklesia/classroom-calm.png"
|
|
out = []
|
|
for c in ids:
|
|
if {"plugin=org.kde.plasma.folder", "plugin=org.kde.desktopcontainment"} & set(top(c)):
|
|
wallpaper = groups.get(f"[Containments][{c}][Wallpaper][org.kde.image][General]", [])
|
|
out.append(f"{c}:{'image' if image in wallpaper else 'none'}")
|
|
print(" ".join(out))
|
|
PY
|
|
}
|
|
|
|
panel_groups() { applets panels; }
|
|
desktops() { applets desktops; }
|
|
|
|
last_repair() {
|
|
user journalctl --user -u plasma-plasmashell.service --no-pager -o cat \
|
|
| grep '^classroom-desktop-wallpaper:' | tail -1
|
|
}
|
|
|
|
restart_shell() {
|
|
user systemctl --user restart plasma-plasmashell.service
|
|
for _ in $(seq 1 30); do
|
|
in_lab 'pgrep -x plasmashell >/dev/null' && break
|
|
sleep 1
|
|
done
|
|
sleep 10
|
|
}
|
|
|
|
section "the package"
|
|
check "the look and feel's default names a package" EkklesiaClassroom \
|
|
"$(in_lab 'sed -n "/^\[Wallpaper\]/,/^\[/s/^Image=//p" /usr/share/plasma/look-and-feel/org.ekklesia.classroom.desktop/contents/defaults')"
|
|
check "which is installed, and is the classroom's picture" /usr/share/backgrounds/ekklesia/classroom-calm.png \
|
|
"$(in_lab 'readlink -f /usr/share/wallpapers/EkklesiaClassroom/contents/images/*.png')"
|
|
check "the repair runs before every shell start" 1 \
|
|
"$(in_lab 'grep -c "^ExecStartPre=-/usr/local/bin/classroom-desktop-wallpaper" /etc/systemd/user/plasma-plasmashell.service.d/ekklesia-classroom.conf')"
|
|
|
|
section "the session as it came up"
|
|
check "the room shows the classroom's picture" classroom "$(screen)"
|
|
original=$(activities CurrentActivity | cut -d'"' -f2)
|
|
|
|
section "a desktop Plasma makes by itself (Tiny Pebbles' television)"
|
|
panels_before=$(panel_groups)
|
|
late=$(activities AddActivity s "Late screen" | cut -d'"' -f2)
|
|
activities SetCurrentActivity s "$late" >/dev/null
|
|
settle
|
|
now=$(desktops)
|
|
check "Plasma made a second desktop" 2 "$(wc -w <<< "$now")"
|
|
check "and wrote no wallpaper into it, as on Tiny Pebbles" 1 "$(grep -o ':none' <<< "$now" | wc -l)"
|
|
check "the room still shows the classroom's picture" classroom "$(screen)"
|
|
|
|
section "the shell restarted, without a login"
|
|
restart_shell
|
|
check "the repair wrote it in" "classroom-desktop-wallpaper: the wallpaper is now configured on desktop $(tr ' ' '\n' <<< "$now" | cut -d: -f1 | paste -sd, | sed 's/,/, /g')" \
|
|
"$(last_repair)"
|
|
check "every desktop now names the picture" 0 "$(desktops | grep -o ':none' | wc -l)"
|
|
check "the panel keeps its numbers, applets and order" "$panels_before" "$(panel_groups)"
|
|
check "the room shows the classroom's picture" classroom "$(screen)"
|
|
restart_shell
|
|
check "a second restart finds nothing to do" \
|
|
"classroom-desktop-wallpaper: desktop $(desktops | tr ' ' '\n' | cut -d: -f1 | paste -sd, | sed 's/,/, /g'): the classroom wallpaper already" \
|
|
"$(last_repair)"
|
|
check "and still does" "$panels_before" "$(panel_groups)"
|
|
|
|
activities SetCurrentActivity s "$original" >/dev/null
|
|
activities RemoveActivity s "$late" >/dev/null
|
|
sleep 3
|
|
|
|
section "Tiny Pebbles' own file: desktop 10, panel 1, no wallpaper"
|
|
user systemctl --user stop plasma-plasmashell.service
|
|
in_lab_fed "python3 - '$original' $APPLETS" <<'PY'
|
|
import sys
|
|
activity, path = sys.argv[1], sys.argv[2]
|
|
applets = ["quicklaunch", "panelspacer", "icontasks", "panelspacer",
|
|
"networkmanagement", "volume", "notifications", "digitalclock"]
|
|
out = ["[Containments][1]", "activityId=", "formfactor=2", "immutability=1",
|
|
"lastScreen=0", "location=4", "plugin=org.kde.panel",
|
|
"wallpaperplugin=org.kde.image", ""]
|
|
for number, applet in enumerate(applets, start=2):
|
|
out += [f"[Containments][1][Applets][{number}]", "immutability=1",
|
|
f"plugin=org.kde.plasma.{applet}", ""]
|
|
out += ["[Containments][1][Applets][4][Configuration][General]",
|
|
"launchers=applications:ekklesia-wonderink.desktop,applications:google-chrome.desktop,applications:ekklesia-dvd.desktop",
|
|
"sortingStrategy=1", "",
|
|
"[Containments][1][General]", "AppletOrder=2;3;4;5;6;7;8;9", "",
|
|
"[Containments][10]", f"activityId={activity}", "formfactor=0",
|
|
"immutability=1", "lastScreen=0", "location=0",
|
|
"plugin=org.kde.plasma.folder", "wallpaperplugin=org.kde.image", "",
|
|
"[Containments][10][General]", 'positions={"1920x1080":[]}', ""]
|
|
open(path, "w").write("\n".join(out))
|
|
PY
|
|
in_lab "chown classroom:classroom $APPLETS"
|
|
panels_before=$(panel_groups)
|
|
user systemctl --user start plasma-plasmashell.service
|
|
sleep 12
|
|
check "the repair found desktop 10 and wrote it" \
|
|
"classroom-desktop-wallpaper: the wallpaper is now configured on desktop 10" "$(last_repair)"
|
|
check "desktop 10 names the picture" "10:image" "$(desktops)"
|
|
check "the panel is still containment 1, applets 2 to 9, in order" "$panels_before" "$(panel_groups)"
|
|
check "the room shows the classroom's picture" classroom "$(screen)"
|
|
|
|
section "and at the next login"
|
|
"$LAB" plasma-down >/dev/null
|
|
"$LAB" plasma-up >/dev/null
|
|
settle 10
|
|
check "the reset left the repair nothing to do" \
|
|
"classroom-desktop-wallpaper: no desktop configuration yet; the layout writes the wallpaper" \
|
|
"$(last_repair)"
|
|
check "the layout rebuilt one desktop with the picture" 1 "$(desktops | grep -o ':image' | wc -l)"
|
|
check "the room shows the classroom's picture" classroom "$(screen)"
|
|
|
|
printf '\n%s passed, %s failed\n' "$PASS" "$FAIL"
|
|
[[ "$FAIL" -eq 0 ]]
|