makepad/tools/linux/display-layout-env.test.sh

369 lines
15 KiB
Bash

#!/usr/bin/env bash
# tools/linux/display-layout-env.test.sh
#
# Exercises tools/linux/display-layout-env.sh against fake sysfs/config
# trees. No root, no real hardware, no network. `bash tools/linux/display-
# layout-env.test.sh` (from anywhere; paths below are resolved from this
# file's own location). Exit 0 = pass.
#
# Scenarios: a happy-path layout; cards renumbered (same pci, different
# cardN than a hypothetical earlier boot); a screen whose card is missing
# this boot; a malformed file (bad render-on, a too-short screen line, an
# unknown keyword, a duplicate screen key, an invalid mode=); a pre-set
# env var winning over the file for each of the three variables, and the
# per-variable MAKEPAD_WM_ORDER_FROM_SAVED/MAKEPAD_WM_MODES_FROM_SAVED
# markers that go with MAKEPAD_DISPLAY_ORDER/MAKEPAD_DRM_MODES (set only
# when *this* var was set from the file, cleared when it was left alone,
# and never left stale from a previous process even when the caller's
# environment already carried one); the legacy display-gpu fallback,
# both when it should apply (no display-layout file at all) and when it
# must not (display-layout exists, even without a render-on line); and
# an unreadable-but-present display-layout read as empty rather than
# falling back.
set -u
script_dir=$(cd "$(dirname "$0")" && pwd)
helper="$script_dir/display-layout-env.sh"
pass_count=0
fail_count=0
fail() {
fail_count=$((fail_count + 1))
echo "FAIL: $1" >&2
}
pass() {
pass_count=$((pass_count + 1))
}
assert_eq() {
# $1 = case label, $2 = field, $3 = expected, $4 = actual
if [ "$3" = "$4" ]; then
pass
else
fail "$1: $2 expected '$3' got '$4'"
fi
}
# Builds a fresh scratch dir with empty sysfs/ and cfg/ subtrees; echoes
# its path.
new_scratch() {
local dir
dir=$(mktemp -d "${TMPDIR:-/tmp}/mdl-test.XXXXXX")
mkdir -p "$dir/sysfs" "$dir/cfg/makepad/wm"
printf '%s\n' "$dir"
}
# add_card SCRATCH CARDNUM PCI VENDOR_HEX DEVICE_HEX
# Creates sysfs/cardN/device -> a fake PCI device directory carrying
# vendor/device files in sysfs's own "0x"-prefixed form, so the helper's
# own `${ven#0x}` stripping is exercised exactly as it is on real sysfs.
add_card() {
local scratch="$1" num="$2" pci="$3" ven="$4" dev="$5"
local pci_dir="$scratch/pci-devices/$pci"
mkdir -p "$pci_dir" "$scratch/sysfs/card$num"
printf '0x%s' "$ven" >"$pci_dir/vendor"
printf '0x%s' "$dev" >"$pci_dir/device"
ln -s "$pci_dir" "$scratch/sysfs/card$num/device"
}
# write_layout SCRATCH TEXT
write_layout() {
printf '%s' "$2" >"$1/cfg/makepad/wm/display-layout"
}
# write_gpu SCRATCH TEXT (no trailing newline, matching the legacy
# file's own one-ASCII-line shape)
write_gpu() {
printf '%s' "$2" >"$1/cfg/makepad/wm/display-gpu"
}
# run_case SCRATCH [EXTRA_ENV_ASSIGNMENT ...]
# Sources the helper in a brand-new, otherwise-empty bash process (so no
# variable from this test script, or from a previous case, can leak in)
# and prints the production code's variables, one per line, each
# `<unset>` when the helper left it alone: the three the platform reads,
# and the three from-saved markers (one for the GPU, one each for order
# and modes). Stderr (the `display-layout:` log lines) is captured to
# $run_stderr for the few cases that check it, and the subshell's own
# exit status is captured to $run_status (expected 0 always: the helper
# must never fail the caller).
run_stdout=""
run_stderr=""
run_status=0
run_case() {
local scratch="$1"
shift
local stderr_file
stderr_file=$(mktemp "${TMPDIR:-/tmp}/mdl-test-stderr.XXXXXX")
run_stdout=$(env -i \
PATH="$PATH" \
HOME="$scratch/home-unused" \
HELPER="$helper" \
MAKEPAD_SYSFS_DRM="$scratch/sysfs" \
XDG_CONFIG_HOME="$scratch/cfg" \
"$@" \
bash -c '
set -eu
. "$HELPER"
printf "%s\n" "${MAKEPAD_DISPLAY_ORDER:-<unset>}"
printf "%s\n" "${MAKEPAD_DRM_MODES:-<unset>}"
printf "%s\n" "${MAKEPAD_VULKAN_COMPOSITOR_PCI:-<unset>}"
printf "%s\n" "${MAKEPAD_WM_GPU_FROM_SAVED:-<unset>}"
printf "%s\n" "${MAKEPAD_WM_ORDER_FROM_SAVED:-<unset>}"
printf "%s\n" "${MAKEPAD_WM_MODES_FROM_SAVED:-<unset>}"
printf "%s\n" "${MAKEPAD_VIRTUAL_DISPLAY:-<unset>}"
printf "%s\n" "${MAKEPAD_WM_VIRTUAL_FROM_SAVED:-<unset>}"
' 2>"$stderr_file")
run_status=$?
run_stderr=$(cat "$stderr_file")
rm -f "$stderr_file"
IFS=$'\n' read -r -d '' field_order field_modes field_pci field_fromsaved \
field_order_fromsaved field_modes_fromsaved field_virtual field_virtual_fromsaved <<<"$run_stdout"
}
# ---------------------------------------------------------------------
# 1. Happy path: two screens, one with a mode, a render-on GPU.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 0 0000:00:02.0 8086 a780
add_card "$s" 1 0000:01:00.0 10de 2b85
write_layout "$s" 'makepad-display-layout 1
render-on 0000:01:00.0 10de:2b85
screen 0000:00:02.0 HDMI-A-2 mode=3840x2160@30
screen 0000:01:00.0 HDMI-A-1 main
'
run_case "$s"
assert_eq "happy-path" "status" "0" "$run_status"
assert_eq "happy-path" "order" "card0-HDMI-A-2,card1-HDMI-A-1" "$field_order"
assert_eq "happy-path" "modes" "card0-HDMI-A-2=3840x2160@30" "$field_modes"
assert_eq "happy-path" "pci" "0000:01:00.0" "$field_pci"
assert_eq "happy-path" "from_saved" "1" "$field_fromsaved"
assert_eq "happy-path" "order_from_saved" "1" "$field_order_fromsaved"
assert_eq "happy-path" "modes_from_saved" "1" "$field_modes_fromsaved"
rm -rf "$s"
# ---------------------------------------------------------------------
# 2. Cards renumbered: the same pci addresses as case 1, but assigned to
# different card numbers this boot. Resolution must follow pci, not
# any assumption about which cardN a screen had last time.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 7 0000:01:00.0 10de 2b85
add_card "$s" 3 0000:00:02.0 8086 a780
write_layout "$s" 'makepad-display-layout 1
render-on 0000:01:00.0 10de:2b85
screen 0000:00:02.0 HDMI-A-2 mode=3840x2160@30
screen 0000:01:00.0 HDMI-A-1 main
'
run_case "$s"
assert_eq "renumbered" "status" "0" "$run_status"
assert_eq "renumbered" "order" "card3-HDMI-A-2,card7-HDMI-A-1" "$field_order"
assert_eq "renumbered" "modes" "card3-HDMI-A-2=3840x2160@30" "$field_modes"
assert_eq "renumbered" "pci" "0000:01:00.0" "$field_pci"
rm -rf "$s"
# ---------------------------------------------------------------------
# 3. A missing card: the saved HDMI-A-1 screen's GPU is not present this
# boot. It must be dropped (never matched by connector alone), the
# other screen still resolves, and the helper does not fail.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 0 0000:00:02.0 8086 a780
write_layout "$s" 'makepad-display-layout 1
screen 0000:00:02.0 HDMI-A-2 mode=3840x2160@30
screen 0000:01:00.0 HDMI-A-1 main
'
run_case "$s"
assert_eq "missing-card" "status" "0" "$run_status"
assert_eq "missing-card" "order" "card0-HDMI-A-2" "$field_order"
assert_eq "missing-card" "modes" "card0-HDMI-A-2=3840x2160@30" "$field_modes"
case "$run_stderr" in
*"0000:01:00.0 HDMI-A-1 not present this boot"*) pass ;;
*) fail "missing-card: expected a stderr note about the dropped screen, got: $run_stderr" ;;
esac
rm -rf "$s"
# ---------------------------------------------------------------------
# 4. A malformed file: a bad render-on (odd token count), a too-short
# screen line, an unknown keyword, a duplicate screen key (the
# second copy's mode must lose to the first's), an invalid mode=,
# and ordinary comments/blank lines all mixed in with valid lines.
# None of this may abort the helper.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 0 0000:00:02.0 8086 a780
add_card "$s" 1 0000:01:00.0 10de 2b85
write_layout "$s" '# a comment
makepad-display-layout 1
render-on only-one-token
some-unknown-keyword with args here
screen
screen 0000:00:02.0 HDMI-A-2 mode=3840x2160@30
screen 0000:00:02.0 HDMI-A-2 mode=1920x1080
screen 0000:01:00.0 HDMI-A-1 mode=not-a-mode
'
run_case "$s"
assert_eq "malformed" "status" "0" "$run_status"
assert_eq "malformed" "order" "card0-HDMI-A-2,card1-HDMI-A-1" "$field_order"
assert_eq "malformed" "modes (first screen wins over its duplicate)" "card0-HDMI-A-2=3840x2160@30" "$field_modes"
assert_eq "malformed" "pci (bad render-on leaves it unset)" "<unset>" "$field_pci"
rm -rf "$s"
# ---------------------------------------------------------------------
# 5. A pre-set env var wins over the file, for each of the three
# variables independently.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 0 0000:00:02.0 8086 a780
add_card "$s" 1 0000:01:00.0 10de 2b85
write_layout "$s" 'makepad-display-layout 1
render-on 0000:01:00.0 10de:2b85
screen 0000:00:02.0 HDMI-A-2 mode=3840x2160@30
screen 0000:01:00.0 HDMI-A-1 main
'
run_case "$s" MAKEPAD_DISPLAY_ORDER=keep-me
assert_eq "preset-order" "order" "keep-me" "$field_order"
assert_eq "preset-order" "order_from_saved stays unset (not our export)" "<unset>" "$field_order_fromsaved"
assert_eq "preset-order" "modes still applied" "card0-HDMI-A-2=3840x2160@30" "$field_modes"
assert_eq "preset-order" "modes_from_saved still set (independent of the order pin)" "1" "$field_modes_fromsaved"
assert_eq "preset-order" "pci still applied" "0000:01:00.0" "$field_pci"
run_case "$s" MAKEPAD_DRM_MODES=keep-me
assert_eq "preset-modes" "modes" "keep-me" "$field_modes"
assert_eq "preset-modes" "modes_from_saved stays unset (not our export)" "<unset>" "$field_modes_fromsaved"
assert_eq "preset-modes" "order still applied" "card0-HDMI-A-2,card1-HDMI-A-1" "$field_order"
assert_eq "preset-modes" "order_from_saved still set (independent of the modes pin)" "1" "$field_order_fromsaved"
# A caller whose environment already carries a stale from-saved marker
# (e.g. inherited from an earlier, unrelated process) must not have it
# survive once the var itself is externally pinned: both markers are
# unset first, before either var is even looked at.
run_case "$s" MAKEPAD_DISPLAY_ORDER=keep-me MAKEPAD_WM_ORDER_FROM_SAVED=1
assert_eq "preset-order-stale-marker" "order_from_saved is cleared, not left stale" "<unset>" "$field_order_fromsaved"
run_case "$s" MAKEPAD_DRM_MODES=keep-me MAKEPAD_WM_MODES_FROM_SAVED=1
assert_eq "preset-modes-stale-marker" "modes_from_saved is cleared, not left stale" "<unset>" "$field_modes_fromsaved"
run_case "$s" MAKEPAD_VULKAN_COMPOSITOR_PCI=keep-me
assert_eq "preset-pci" "pci" "keep-me" "$field_pci"
assert_eq "preset-pci" "from_saved stays unset (not our export)" "<unset>" "$field_fromsaved"
assert_eq "preset-pci" "order still applied" "card0-HDMI-A-2,card1-HDMI-A-1" "$field_order"
run_case "$s" MAKEPAD_VULKAN_COMPOSITOR_UUID=deadbeefdeadbeefdeadbeefdeadbeef
assert_eq "preset-uuid" "pci left unset when UUID is externally pinned" "<unset>" "$field_pci"
rm -rf "$s"
# ---------------------------------------------------------------------
# 6. Legacy display-gpu fallback: only when display-layout does not
# exist at all.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 1 0000:01:00.0 10de 2b85
write_gpu "$s" '0000:01:00.0 10de:2b85'
run_case "$s"
assert_eq "legacy-fallback" "status" "0" "$run_status"
assert_eq "legacy-fallback" "pci (from display-gpu)" "0000:01:00.0" "$field_pci"
assert_eq "legacy-fallback" "from_saved" "1" "$field_fromsaved"
rm -rf "$s"
# display-layout exists (even with nothing useful in it) -> the legacy
# file must be ignored even though it names a present, valid GPU.
s=$(new_scratch)
add_card "$s" 1 0000:01:00.0 10de 2b85
write_gpu "$s" '0000:01:00.0 10de:2b85'
write_layout "$s" 'makepad-display-layout 1
'
run_case "$s"
assert_eq "layout-supersedes-legacy" "status" "0" "$run_status"
assert_eq "layout-supersedes-legacy" "pci (Auto, not the stale legacy file)" "<unset>" "$field_pci"
rm -rf "$s"
# An unreadable-but-present display-layout reads as empty: no legacy
# fallback either (the file's presence, not its readability, decides).
s=$(new_scratch)
add_card "$s" 1 0000:01:00.0 10de 2b85
write_gpu "$s" '0000:01:00.0 10de:2b85'
write_layout "$s" 'makepad-display-layout 1
render-on 0000:01:00.0 10de:2b85
'
chmod 000 "$s/cfg/makepad/wm/display-layout"
if [ -r "$s/cfg/makepad/wm/display-layout" ]; then
echo "SKIP: unreadable-layout case needs a non-root user to enforce file permissions" >&2
else
run_case "$s"
assert_eq "unreadable-layout" "status" "0" "$run_status"
assert_eq "unreadable-layout" "pci (no fallback to the legacy file)" "<unset>" "$field_pci"
fi
chmod 600 "$s/cfg/makepad/wm/display-layout"
rm -rf "$s"
# ---------------------------------------------------------------------
# 7. No display-layout and no display-gpu: must not fail, nothing set.
# ---------------------------------------------------------------------
s=$(new_scratch)
add_card "$s" 0 0000:00:02.0 8086 a780
run_case "$s"
assert_eq "nothing-saved" "status" "0" "$run_status"
assert_eq "nothing-saved" "order" "<unset>" "$field_order"
assert_eq "nothing-saved" "modes" "<unset>" "$field_modes"
assert_eq "nothing-saved" "pci" "<unset>" "$field_pci"
rm -rf "$s"
# ---------------------------------------------------------------------
# 8. No sysfs tree at all under MAKEPAD_SYSFS_DRM (e.g. a stale
# override): still must not fail.
# ---------------------------------------------------------------------
s=$(new_scratch)
rm -rf "$s/sysfs"
write_layout "$s" 'makepad-display-layout 1
screen 0000:00:02.0 HDMI-A-2
'
run_case "$s"
assert_eq "no-sysfs" "status" "0" "$run_status"
assert_eq "no-sysfs" "order" "<unset>" "$field_order"
rm -rf "$s"
# ---------------------------------------------------------------------
# 9. The virtual line: exported with its panels word and the marker; the
# first valid line wins; a malformed one is skipped; a pre-set value
# wins and clears the marker; absent = unset.
# ---------------------------------------------------------------------
s=$(new_scratch)
write_layout "$s" 'makepad-display-layout 1
virtual 7680x2160@120 panels=off scale=200 follow=on
virtual 1920x1080@60
'
run_case "$s"
assert_eq "virtual" "status" "0" "$run_status"
assert_eq "virtual" "env" "7680x2160@120 off" "$field_virtual"
assert_eq "virtual" "marker" "1" "$field_virtual_fromsaved"
run_case "$s" MAKEPAD_VIRTUAL_DISPLAY="3840x2160@60 mirror" MAKEPAD_WM_VIRTUAL_FROM_SAVED=1
assert_eq "virtual-pinned" "env" "3840x2160@60 mirror" "$field_virtual"
assert_eq "virtual-pinned" "marker cleared" "<unset>" "$field_virtual_fromsaved"
rm -rf "$s"
s=$(new_scratch)
write_layout "$s" 'makepad-display-layout 1
virtual banana
virtual 5120x1440@59.94
'
run_case "$s"
assert_eq "virtual-default-panels" "env" "5120x1440@59.94 mirror" "$field_virtual"
rm -rf "$s"
s=$(new_scratch)
write_layout "$s" 'makepad-display-layout 1
'
run_case "$s"
assert_eq "virtual-absent" "env" "<unset>" "$field_virtual"
assert_eq "virtual-absent" "marker" "<unset>" "$field_virtual_fromsaved"
rm -rf "$s"
echo "display-layout-env.test.sh: $pass_count passed, $fail_count failed"
if [ "$fail_count" -ne 0 ]; then
exit 1
fi
exit 0