joeylib2/scripts/verify-be-display.sh

140 lines
4.8 KiB
Bash
Executable file

#!/usr/bin/env bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# verify-be-display.sh - Display parity gate for a Be API port (haiku,
# haiku32, beos): what the window shows must be the stage, at every window
# scale and on the full screen.
#
# The golden hashes gate the stage, never the display. The Be backend copies
# into its bitmap, and draws, only the rectangles that changed at each show,
# so a stale or misplaced rectangle would never reach a hash. UBER's probe
# build (make <port> builds it, into build/<port>/probe) ends with P13, the
# battery the other display gates run, which after every step asks the
# backend (hostBeProbeDisplayMismatch in src/hosted/be/beBackend.cpp) how many
# pixels differ: the frame's against the stage, the bitmap's against the
# frame, and the window as the shows left it against the window fully
# repainted, both read back from the screen (BScreen::ReadBitmap). Every
# count must be 0, and every pixel must have been compared: 64000 twice,
# plus the window's (the size the probe logs).
#
# One VM boot (scripts/lib/beGate.sh) runs UBERPROB once a spec, with
# JOEYLIB_SCALE / JOEYLIB_ASPECT / JOEYLIB_FULLSCREEN set as it asks.
#
# Usage: scripts/verify-be-display.sh [haiku|haiku32|beos] [SCALE[:square] | full]...
# (default haiku; specs 1:square 2:square 3 full)
# Requires: QEMU and the port's VM (install.sh install emulator:<port>);
# build/<port>/probe/UBERPROB (make <port>).
set -uo pipefail
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
source "$repo/scripts/lib/joeyEnv.sh"
source "$repo/scripts/lib/beGate.sh"
port=${1:-haiku}
shift || true
label="verify-be-display ($port)"
beNeed "$port"
program=$repo/build/$port/probe/UBERPROB
probeLine="UBER-PROBE: beDisplayMismatch"
sizeLine="UBER-PROBE: beWindow"
doneLine="UBER: press any key to exit"
stagePixels=$((320 * 200))
fullSpec=full
bootSeconds=120
runSeconds=120 # one probe run, start to done line
defaults=(1:square 2:square 3 "$fullSpec")
specs=("$@")
[ ${#specs[@]} -gt 0 ] || specs=("${defaults[@]}")
[ -x "$program" ] || { echo "$label: the probe build is missing; run: make $port" >&2; exit 2; }
work=$(mktemp -d -t joeylib-be-display.XXXXXX)
cleanup() {
beStop
rm -rf "$work"
}
trap cleanup EXIT
# The job: each spec's run in a subshell of its own (its folder and
# settings stay there), its log kept as log<N>.txt.
mkdir -p "$work/job/UBERPROB"
cp "$program" "$work/job/UBERPROB/UBERPROB"
: > "$work/job/job.sh"
n=0
for spec in "${specs[@]}"; do
if [ "$spec" = "$fullSpec" ]; then
settings=(JOEYLIB_FULLSCREEN=1)
elif [[ "$spec" =~ ^([0-9]+)(:square)?$ ]]; then
settings=(JOEYLIB_SCALE="${BASH_REMATCH[1]}")
[ -n "${BASH_REMATCH[2]}" ] && settings+=(JOEYLIB_ASPECT=square)
else
echo "$label: bad spec \"$spec\" (SCALE[:square] or $fullSpec)" >&2
exit 2
fi
{
printf '(\n'
printf 'rm -f UBERPROB/joeylog.txt\n'
beJobScript UBERPROB UBERPROB "$doneLine" "$runSeconds" "${settings[@]}"
printf ')\n'
printf 'cp UBERPROB/joeylog.txt log%s.txt\n' "$n"
} >> "$work/job/job.sh"
n=$((n + 1))
done
beJobDisk "$work/job.img" "$work/job"
beStart "$work" "$work/job.img"
if ! beWaitJob "$work" "$bootSeconds"; then
echo "$label: FAIL - the VM did not start the job within ${bootSeconds} s" >&2
exit 1
fi
if ! beWaitExit "$work" "$((runSeconds * ${#specs[@]} + 60))"; then
echo "$label: FAIL - the runs did not finish" >&2
exit 1
fi
fails=""
passed=0
# fail SPEC WHAT -- note a failed run; the gate goes on with the rest.
fail() {
echo "$label: FAIL - $1: $2"
fails="${fails:+$fails; }$1"
}
n=0
for spec in "${specs[@]}"; do
log=$work/log$n.txt
n=$((n + 1))
beJobGet "$work/job.img" "log$((n - 1)).txt" "$log" || true
row=$(grep -F "$probeLine" "$log" 2> /dev/null | tr -d '\r' | tail -1)
size=$(grep -F "$sizeLine" "$log" 2> /dev/null | tr -d '\r' | tail -1)
if [ -z "$row" ] || [ -z "$size" ]; then
fail "$spec" "no \"$probeLine\" line"
continue
fi
size=${size#*"$sizeLine" }
w=${size%x*}
h=${size#*x}
want=$((2 * stagePixels + w * h))
counts=${row#*"$probeLine" }
echo "$label: $spec (${w}x${h}): $counts"
bad=$(printf '%s\n' "$counts" | tr ' ' '\n' | grep '^after' | grep -v '=0$' | tr '\n' ' ')
compared=$(printf '%s\n' "$counts" | sed -n 's/.*compared=\([0-9]*\).*/\1/p')
if [ -n "$bad" ]; then
fail "$spec" "pixels differ: $bad"
elif [ "$compared" != "$want" ]; then
fail "$spec" "compared $compared, want $want"
else
passed=$((passed + 1))
fi
done
if [ -n "$fails" ]; then
echo "$label: FAIL - $fails"
exit 1
fi
echo "$label: PASS ($passed window sizes: every pixel shown matches the stage after every present)"