#!/bin/bash # Round01 window activation fixture QA. It verifies that Enter and mouse # commits call the activation service, close Quick Switch, and distinguish # normal activation from minimized-window restore. 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" MINIMIZED_REPORT="$BUILD_REPORT_ROOT/round01-window-activation-minimized-report.json" NORMAL_REPORT="$BUILD_REPORT_ROOT/round01-window-activation-normal-report.json" APP_ONLY_REPORT="$BUILD_REPORT_ROOT/round01-window-activation-app-only-report.json" REPORT_WAIT="${ALIGNER_ROUND1_ACTIVATION_REPORT_WAIT:-6.0}" fail() { echo "Round01 window activation 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" } wait_for_activation_report() { local report="$1" /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 and report.get("quickSwitchVisible") is False and report.get("lastActivationResult") is not None ): 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"activation report did not become hidden+activated within {timeout:.1f}s", file=sys.stderr) sys.exit(1) PY } assert_activation_report() { local report="$1" local expected_source="$2" local expected_result="$3" local expected_app_group_index="$4" local expected_window_id="$5" local expected_minimized="$6" /usr/bin/python3 - "$report" "$expected_source" "$expected_result" "$expected_app_group_index" "$expected_window_id" "$expected_minimized" <<'PY' import json import sys path = sys.argv[1] expected_source = sys.argv[2] expected_result = sys.argv[3] expected_app_group_index = int(sys.argv[4]) expected_window_id = int(sys.argv[5]) expected_minimized = sys.argv[6] == "true" 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) root = report.get("rootView", {}) cards = [ card for column in root.get("waterfallColumns", []) for card in column.get("cards", []) ] selected_cards = [card for card in cards if card.get("isSelected") is True] require(report.get("snapshotLoaded") is True, "activation snapshotLoaded must be true") require(report.get("quickSwitchVisible") is False, "activation commit must close Quick Switch") require(report.get("appCount") == 2, "activation fixture must expose two Apps") require(report.get("windowCount") == 3, "activation fixture must expose three windows") require(root.get("selectedAppGroupIndex") == expected_app_group_index, "selected App group must match activation target") require(root.get("selectedWindowID") == expected_window_id, "selectedWindowID must match activation target") require(report.get("lastCommittedWindowID") == expected_window_id, "commit window must match activation target") require(report.get("lastCommitSource") == expected_source, "commit source must match expected path") require(report.get("lastActivationWindowID") == expected_window_id, "activation window must match selected target") require(report.get("lastActivationResult") == expected_result, "activation result must match expected path") require(report.get("lastActivationError") is None, "debug activation should not record an error") require(len(selected_cards) == 1, "activation fixture must expose exactly one selected card") require(selected_cards[0].get("windowID") == expected_window_id, "selected card must match activation target") require(selected_cards[0].get("isMinimized") is expected_minimized, "selected card minimized flag must match activation target") print(json.dumps({ "commitSource": report.get("lastCommitSource"), "activationResult": report.get("lastActivationResult"), "selectedWindowID": root.get("selectedWindowID"), "quickSwitchVisible": report.get("quickSwitchVisible"), "selectedIsMinimized": selected_cards[0].get("isMinimized") }, indent=2, ensure_ascii=False)) PY } stop_current_aligner "$SCRIPT_DIR/package-app.sh" >&2 rm -f "$MINIMIZED_REPORT" "$NORMAL_REPORT" "$APP_ONLY_REPORT" "$APP/Contents/MacOS/Aligner" \ --round0-skip-permissions \ --round01-open-quick-switch \ --round01-waterfall-view-mode=vertical \ --round01-fixture-window-activation \ --round01-disable-screenshot-refresh \ --round01-debug-window-activation \ --round01-debug-key-sequence="2,down,enter" \ --round01-quick-switch-report="$MINIMIZED_REPORT" & APP_PID=$! cleanup() { kill "$APP_PID" 2>/dev/null || true wait "$APP_PID" 2>/dev/null || true stop_current_aligner } trap cleanup EXIT wait_for_activation_report "$MINIMIZED_REPORT" swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-no-quick-switch >&2 assert_activation_report "$MINIMIZED_REPORT" "keyboard" "restored" 1 40200 true kill "$APP_PID" 2>/dev/null || true wait "$APP_PID" 2>/dev/null || true stop_current_aligner "$APP/Contents/MacOS/Aligner" \ --round0-skip-permissions \ --round01-open-quick-switch \ --round01-waterfall-view-mode=vertical \ --round01-fixture-window-activation \ --round01-disable-screenshot-refresh \ --round01-debug-window-activation \ --round01-debug-mouse-sequence="click-card:0:1" \ --round01-quick-switch-report="$NORMAL_REPORT" & APP_PID=$! wait_for_activation_report "$NORMAL_REPORT" swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-no-quick-switch >&2 assert_activation_report "$NORMAL_REPORT" "mouse" "activated" 0 40100 false kill "$APP_PID" 2>/dev/null || true wait "$APP_PID" 2>/dev/null || true stop_current_aligner "$APP/Contents/MacOS/Aligner" \ --round0-skip-permissions \ --round01-open-quick-switch \ --round01-waterfall-view-mode=vertical \ --round01-fixture-window-activation \ --round01-disable-screenshot-refresh \ --round01-debug-window-activation \ --round01-debug-mouse-sequence="click-card:0:0" \ --round01-quick-switch-report="$APP_ONLY_REPORT" & APP_PID=$! wait_for_activation_report "$APP_ONLY_REPORT" swift "$SCRIPT_DIR/window-logic-qa.swift" --expect-no-quick-switch >&2 assert_activation_report "$APP_ONLY_REPORT" "mouse" "appActivatedOnly" 0 40110 false kill "$APP_PID" 2>/dev/null || true wait "$APP_PID" 2>/dev/null || true trap - EXIT stop_current_aligner