#!/bin/bash
|
# Round01 Space Lane Split View fixture QA. It verifies that a fullscreen Space
|
# containing two fullscreen windows is rendered as a left/right Split View tile,
|
# with app labels ordered by the real window frame.
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
OUTPUT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
# shellcheck source=build-output-paths.sh
|
source "$SCRIPT_DIR/build-output-paths.sh"
|
APP="$BUILD_CURRENT_APP"
|
REPORT="$BUILD_REPORT_ROOT/round01-space-lane-split-view-fixture-report.json"
|
REPORT_WAIT="${ALIGNER_ROUND1_SPLIT_VIEW_REPORT_WAIT:-6.0}"
|
APP_PID=""
|
|
fail() {
|
echo "Round01 Space Lane Split View fixture QA failed: $*" >&2
|
exit 1
|
}
|
|
aligner_pids_for_current_app() {
|
ps -axo pid=,args= | while read -r pid command; do
|
case "$command" in
|
"$APP/Contents/MacOS/Aligner"*) echo "$pid" ;;
|
esac
|
done
|
}
|
|
stop_current_aligner() {
|
for pid in $(aligner_pids_for_current_app); do
|
kill "$pid" 2>/dev/null || true
|
done
|
|
for _ in {1..30}; do
|
[ -z "$(aligner_pids_for_current_app)" ] && return
|
sleep 0.1
|
done
|
|
fail "current Aligner app did not exit before QA"
|
}
|
|
cleanup() {
|
if [ -n "${APP_PID:-}" ]; then
|
kill "$APP_PID" 2>/dev/null || true
|
wait "$APP_PID" 2>/dev/null || true
|
APP_PID=""
|
fi
|
stop_current_aligner
|
}
|
|
wait_for_loaded_report() {
|
/usr/bin/python3 - "$REPORT" "$REPORT_WAIT" <<'PY'
|
import json
|
import sys
|
import time
|
|
path = sys.argv[1]
|
timeout = float(sys.argv[2])
|
deadline = time.monotonic() + timeout
|
last_report = None
|
|
while time.monotonic() < deadline:
|
try:
|
with open(path, "r", encoding="utf-8") as file:
|
report = json.load(file)
|
last_report = report
|
if report.get("snapshotLoaded") is True:
|
sys.exit(0)
|
except FileNotFoundError:
|
pass
|
except json.JSONDecodeError:
|
pass
|
time.sleep(0.2)
|
|
if last_report is not None:
|
print(json.dumps(last_report, indent=2, ensure_ascii=False), file=sys.stderr)
|
print(f"Split View fixture report did not become loaded within {timeout:.1f}s", file=sys.stderr)
|
sys.exit(1)
|
PY
|
}
|
|
assert_report() {
|
/usr/bin/python3 - "$REPORT" <<'PY'
|
import json
|
import sys
|
|
path = sys.argv[1]
|
with open(path, "r", encoding="utf-8") as file:
|
report = json.load(file)
|
|
def require(condition, message):
|
if not condition:
|
print(message, file=sys.stderr)
|
print(json.dumps(report, indent=2, ensure_ascii=False), file=sys.stderr)
|
sys.exit(1)
|
|
def mid_x(frame):
|
return frame.get("x", 0) + frame.get("width", 0) / 2
|
|
def mid_y(frame):
|
return frame.get("y", 0) + frame.get("height", 0) / 2
|
|
require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true")
|
root = report.get("rootView", {})
|
segments = root.get("spaceLaneSegments", [])
|
require(segments, "Space Lane segments must be reported")
|
|
split_segments = [
|
segment for segment in segments
|
if segment.get("fullscreenMarkerKind") == "splitViewPair"
|
]
|
require(len(split_segments) == 1, "fixture must expose exactly one Split View Space tile")
|
split = split_segments[0]
|
require(split.get("label") == "02", "Split View fixture Space must be 02")
|
require(split.get("type") == "fullscreen", "Split View tile must still be a fullscreen Space")
|
require(split.get("windowCount") == 2, "Split View tile must count both fullscreen windows")
|
require(split.get("appCount") == 2, "Split View tile must count both Split View apps")
|
require(split.get("countVisible") is False, "Split View tile must hide the top-right count label")
|
require(split.get("countText") == "", "Split View tile must clear the top-right count text")
|
require(split.get("windowBlockCount") == 0, "Split View tile must not draw normal window blocks")
|
require("splitView" in split.get("visualStates", []), "Split View tile must expose splitView visual state")
|
require(split.get("fullscreenMarkerPathStyle") == "centerDividerOnly", "Split View marker must draw only the center divider")
|
require(
|
split.get("splitViewAppNames") == ["左侧应用", "右侧应用"],
|
"Split View app names must be ordered by physical window frame from left to right"
|
)
|
|
marker = split.get("fullscreenMarkerFrame", {})
|
labels = split.get("splitViewLabelFrames", [])
|
require(isinstance(marker, dict) and marker.get("width", 0) > 0, "Split View marker frame must be exposed")
|
require(len(labels) == 2, "Split View tile must expose exactly two app label frames")
|
left, right = labels
|
require(mid_x(left) < marker.get("x", 0) + marker.get("width", 0) / 2, "left Split View label must sit in the left half")
|
require(mid_x(right) > marker.get("x", 0) + marker.get("width", 0) / 2, "right Split View label must sit in the right half")
|
require(mid_x(left) < mid_x(right), "Split View label frames must be ordered left to right")
|
require(abs(mid_y(left) - mid_y(marker)) <= 2, "left Split View label must be vertically centered")
|
require(abs(mid_y(right) - mid_y(marker)) <= 2, "right Split View label must be vertically centered")
|
|
single_fullscreen = [
|
segment for segment in segments
|
if segment.get("type") == "fullscreen"
|
and segment.get("windowCount") == 1
|
]
|
require(single_fullscreen, "fixture must include a single fullscreen regression Space")
|
require(
|
all(segment.get("fullscreenMarkerKind") == "cornerBracket" for segment in single_fullscreen),
|
"single fullscreen Spaces must keep the corner bracket marker"
|
)
|
require(
|
all(not segment.get("splitViewAppNames") for segment in single_fullscreen),
|
"single fullscreen Spaces must not expose Split View labels"
|
)
|
|
print(json.dumps({
|
"splitSpace": split.get("label"),
|
"splitNames": split.get("splitViewAppNames"),
|
"markerKind": split.get("fullscreenMarkerKind"),
|
"markerPathStyle": split.get("fullscreenMarkerPathStyle"),
|
"countVisible": split.get("countVisible"),
|
"singleFullscreenLabels": [segment.get("label") for segment in single_fullscreen],
|
}, indent=2, ensure_ascii=False))
|
PY
|
}
|
|
trap cleanup EXIT
|
stop_current_aligner
|
"$SCRIPT_DIR/package-app.sh" >&2
|
rm -f "$REPORT"
|
|
"$APP/Contents/MacOS/Aligner" \
|
--round0-skip-permissions \
|
--round01-open-quick-switch \
|
--round01-fixture-split-view \
|
--round01-disable-screenshot-refresh \
|
--round01-quick-switch-report="$REPORT" &
|
|
APP_PID=$!
|
|
wait_for_loaded_report
|
assert_report
|
|
trap - EXIT
|
cleanup
|
echo "Round01 Space Lane Split View fixture QA passed"
|