| | |
| | | REPORT_HORIZONTAL_DARK="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-horizontal-dark-report.json" |
| | | REPORT_COMMIT="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-commit-report.json" |
| | | REPORT_INVALID="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-invalid-report.json" |
| | | REPORT_SINGLE_WINDOW="$BUILD_REPORT_ROOT/round01-window-index-progressive-filter-single-window-report.json" |
| | | SUMMARY_DIR="$BUILD_TMP_ROOT/round01-window-index-progressive-filter-fixture-qa" |
| | | |
| | | APP_PID="" |
| | |
| | | echo "app:$APP_SYMBOL,windowInvalid:${APP_SYMBOL}Z" |
| | | } |
| | | |
| | | single_window_expected_sequence() { |
| | | echo "app:$APP_SYMBOL,windowSingle:$APP_SYMBOL" |
| | | } |
| | | |
| | | run_fixture() { |
| | | local report="$1" |
| | | local theme="$2" |
| | | local mode="$3" |
| | | local key_sequence="$4" |
| | | local windows_per_app="${5:-$FIXTURE_WINDOWS_PER_APP}" |
| | | |
| | | stop_current_aligner |
| | | set_theme "$theme" |
| | |
| | | --round0-skip-permissions \ |
| | | --round01-open-quick-switch \ |
| | | --round01-fixture-app-count="$FIXTURE_APP_COUNT" \ |
| | | --round01-fixture-windows-per-app="$FIXTURE_WINDOWS_PER_APP" \ |
| | | --round01-fixture-windows-per-app="$windows_per_app" \ |
| | | --round01-disable-screenshot-refresh \ |
| | | --round01-waterfall-view-mode="$mode" \ |
| | | --round01-debug-key-sequence="$key_sequence" \ |
| | |
| | | if last_report is not None: |
| | | print(json.dumps(last_report, indent=2, ensure_ascii=False), file=sys.stderr) |
| | | print(f"invalid report did not reach invalid state within {timeout:.1f}s", file=sys.stderr) |
| | | sys.exit(1) |
| | | PY |
| | | } |
| | | |
| | | wait_for_single_window_report() { |
| | | local report="$1" |
| | | local expected_mode="$2" |
| | | local expected_sequence="$3" |
| | | |
| | | /usr/bin/python3 - "$report" "$REPORT_WAIT" "$expected_mode" "$expected_sequence" "$APP_SYMBOL" <<'PY' |
| | | import json |
| | | import sys |
| | | import time |
| | | |
| | | path = sys.argv[1] |
| | | timeout = float(sys.argv[2]) |
| | | expected_mode = sys.argv[3] |
| | | expected_sequence = [part for part in sys.argv[4].split(",") if part] |
| | | app_symbol = sys.argv[5] |
| | | 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 |
| | | root = report.get("rootView", {}) |
| | | if ( |
| | | report.get("snapshotLoaded") is True |
| | | and root.get("waterfallViewMode") == expected_mode |
| | | and root.get("keyboardCommandsApplied") == expected_sequence |
| | | and root.get("lastWindowIndexKeyCommand") == f"windowSingle:{app_symbol}" |
| | | and root.get("lastCommitSource") == "keyboard" |
| | | ): |
| | | 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"single-window report did not reach direct commit within {timeout:.1f}s", file=sys.stderr) |
| | | sys.exit(1) |
| | | PY |
| | | } |
| | |
| | | root = report.get("rootView", {}) |
| | | columns = root.get("waterfallColumns", []) |
| | | cards = [card for column in columns for card in column.get("cards", [])] |
| | | target_column = next((column for column in columns if column.get("appGroupIndex") == target_app_index), None) |
| | | all_window_ids = sorted(int(card.get("windowID")) for card in cards) |
| | | committed_window_id = root.get("lastCommittedWindowID") |
| | | require(isinstance(committed_window_id, int), "commit must report a committed window ID") |
| | | target_window_id = int(committed_window_id) |
| | | |
| | | require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true") |
| | | require(report.get("appCount") == fixture_app_count, "fixture appCount must match") |
| | | require(report.get("windowCount") == fixture_app_count * fixture_windows_per_app, "fixture windowCount must match") |
| | | require(report.get("appCount") == fixture_app_count, "multi-window fixture must keep target App visible after suppressing one window") |
| | | require(report.get("windowCount") == fixture_app_count * fixture_windows_per_app - 1, "commit run must suppress the activated target window from visible cards") |
| | | require(root.get("waterfallViewMode") == expected_mode, "commit run must use horizontal Waterfall") |
| | | require(root.get("keyboardCommandsApplied") == expected_sequence, "commit key sequence must be applied") |
| | | require(root.get("appShelfIndexKeyDownCount") == 2, "commit must exercise two physical keyDown events") |
| | |
| | | require(root.get("windowIndexKeyPendingAppSymbol") is None, "commit must clear legacy pending App symbol") |
| | | require(root.get("lastCommitSource") == "keyboard", "valid shortcut commit must be keyboard sourced") |
| | | |
| | | require(target_column is not None, "target App column must exist") |
| | | target_cards = target_column.get("cards", []) |
| | | require(target_window_index < len(target_cards), "target window index must exist") |
| | | target_card = target_cards[target_window_index] |
| | | target_window_id = int(target_card.get("windowID")) |
| | | all_window_ids = sorted(int(card.get("windowID")) for card in cards) |
| | | dimmed_ids = sorted(window_id for window_id in all_window_ids if window_id != target_window_id) |
| | | |
| | | require(target_card.get("windowShortcutCode") == target_code, "target card shortcut code must match committed code") |
| | | require(root.get("lastCommittedAppGroupIndex") == target_app_index, "commit must target expected App") |
| | | require(root.get("lastCommittedWindowIndex") == target_window_index, "commit must target expected window index") |
| | | require(root.get("lastCommittedWindowID") == target_window_id, "commit must target expected window ID") |
| | | require(root.get("selectedAppGroupIndex") == target_app_index, "selection must land on target App") |
| | | require(root.get("selectedWindowIndex") == target_window_index, "selection must land on target window") |
| | | require(root.get("selectedWindowID") == target_window_id, "selectedWindowID must match target window") |
| | | require(report.get("suppressedActivationWindowIDs") == [target_window_id], "commit must suppress the activated target window") |
| | | require(report.get("suppressedActivationWindowCount") == 1, "commit must report one suppressed activation window") |
| | | require(target_window_id not in all_window_ids, "committed target window must not remain in visible cards after activation") |
| | | |
| | | snapshot = root.get("lastWindowShortcutPreCommitFilterSnapshot") |
| | | require(isinstance(snapshot, dict), "pre-commit filter snapshot must be an object") |
| | | require(snapshot.get("phase") == "targetPreCommit", "pre-commit snapshot phase must be targetPreCommit") |
| | | require(snapshot.get("targetCode") == target_code, "pre-commit snapshot targetCode must match committed shortcut") |
| | | require(int(snapshot.get("targetWindowID")) == target_window_id, "pre-commit snapshot targetWindowID must match target card") |
| | | require(int(snapshot.get("targetWindowID")) == target_window_id, "pre-commit snapshot targetWindowID must match committed window") |
| | | require(sorted_ints(snapshot.get("matchedWindowIDs")) == [target_window_id], "pre-commit snapshot must match only the target window") |
| | | require(sorted_ints(snapshot.get("dimmedWindowIDs")) == dimmed_ids, "pre-commit snapshot must dim all non-target Waterfall windows") |
| | | |
| | | states = target_card.get("visualStates", []) |
| | | opacity = target_card.get("windowShortcutFilterOpacity") |
| | | require(target_card.get("windowShortcutFilterState") == "target", "target card must report target filter state") |
| | | require(is_number(opacity), "target card must expose numeric filter opacity") |
| | | require(0 <= opacity <= 1.01, "target card opacity must be normalized") |
| | | require("shortcutTarget" in states, "target card must expose shortcutTarget visual state") |
| | | require(sorted_ints(snapshot.get("dimmedWindowIDs")) == all_window_ids, "pre-commit snapshot must dim all non-target visible Waterfall windows") |
| | | require(root.get("windowShortcutFilterActive") is True, "commit must preserve target pre-commit filter state for QA evidence") |
| | | require(root.get("windowShortcutFilterPhase") == "targetPreCommit", "commit must preserve target pre-commit filter phase") |
| | | require(root.get("windowShortcutFilterPrefix") == target_code, "commit filter prefix must match target code") |
| | | require(sorted_ints(root.get("windowShortcutFilterMatchedWindowIDs")) == [target_window_id], "commit filter must keep target as matched evidence") |
| | | require(sorted_ints(root.get("windowShortcutFilterDimmedWindowIDs")) == all_window_ids, "commit filter must dim all non-target visible windows") |
| | | require(root.get("windowShortcutFilterMatchedCount") == 1, "commit filter must report one matched window") |
| | | require(root.get("windowShortcutFilterDimmedCount") == len(all_window_ids), "commit filter dimmed count must match visible non-target windows") |
| | | |
| | | for card in cards: |
| | | if int(card.get("windowID")) == target_window_id: |
| | | continue |
| | | card_states = card.get("visualStates", []) |
| | | card_opacity = card.get("windowShortcutFilterOpacity") |
| | | require(card.get("windowShortcutFilterState") == "dimmed", "non-target cards must remain dimmed in target pre-commit state") |
| | | require(card.get("windowShortcutFilterState") == "dimmed", "visible non-target cards must remain dimmed in target pre-commit state") |
| | | require(is_number(card_opacity), "non-target cards must expose numeric filter opacity") |
| | | require("shortcutDimmed" in card_states, "non-target cards must expose shortcutDimmed in target pre-commit state") |
| | | require(0 <= float(card_opacity) <= 1.01, "non-target card opacity must be normalized") |
| | | require("shortcutDimmed" in card_states, "visible non-target cards must expose shortcutDimmed in target pre-commit state") |
| | | require("shortcutTarget" not in card_states, "non-target cards must not expose shortcutTarget") |
| | | |
| | | for node in root.get("appShelfItems", []) + root.get("spaceLaneSegments", []): |
| | |
| | | "dimmedCount": len(snapshot.get("dimmedWindowIDs", [])), |
| | | "lastCommitSource": root.get("lastCommitSource"), |
| | | "lastWindowIndexKeyCommitCode": root.get("lastWindowIndexKeyCommitCode"), |
| | | "targetCardState": target_card.get("windowShortcutFilterState"), |
| | | "suppressedActivationWindowIDs": report.get("suppressedActivationWindowIDs"), |
| | | } |
| | | with open(summary_file, "w", encoding="utf-8") as file: |
| | | json.dump(summary, file, indent=2, ensure_ascii=False) |
| | |
| | | SUMMARY_FILES+=("$summary_file") |
| | | } |
| | | |
| | | assert_single_window_report() { |
| | | local report="$1" |
| | | local case_name="single-window-first-key-commit-horizontal" |
| | | local summary_file="$SUMMARY_DIR/$case_name.json" |
| | | |
| | | /usr/bin/python3 - \ |
| | | "$report" \ |
| | | "$case_name" \ |
| | | "horizontalMasonry" \ |
| | | "$(single_window_expected_sequence)" \ |
| | | "$FIXTURE_APP_COUNT" \ |
| | | "$TARGET_APP_INDEX" \ |
| | | "$APP_SYMBOL" \ |
| | | "$summary_file" <<'PY' |
| | | import json |
| | | import numbers |
| | | import sys |
| | | |
| | | path = sys.argv[1] |
| | | case_name = sys.argv[2] |
| | | expected_mode = sys.argv[3] |
| | | expected_sequence = [part for part in sys.argv[4].split(",") if part] |
| | | fixture_app_count = int(sys.argv[5]) |
| | | target_app_index = int(sys.argv[6]) |
| | | app_symbol = sys.argv[7] |
| | | summary_file = sys.argv[8] |
| | | |
| | | with open(path, "r", encoding="utf-8") as file: |
| | | report = json.load(file) |
| | | |
| | | def require(condition, message): |
| | | if not condition: |
| | | print(f"{case_name}: {message}", file=sys.stderr) |
| | | print(json.dumps(report, indent=2, ensure_ascii=False), file=sys.stderr) |
| | | sys.exit(1) |
| | | |
| | | def is_number(value): |
| | | return isinstance(value, numbers.Real) and not isinstance(value, bool) |
| | | |
| | | root = report.get("rootView", {}) |
| | | columns = root.get("waterfallColumns", []) |
| | | cards = [card for column in columns for card in column.get("cards", [])] |
| | | all_window_ids = sorted(int(card.get("windowID")) for card in cards) |
| | | committed_window_id = root.get("lastCommittedWindowID") |
| | | require(isinstance(committed_window_id, int), "direct commit must report a committed window ID") |
| | | target_window_id = int(committed_window_id) |
| | | |
| | | require(report.get("snapshotLoaded") is True, "snapshotLoaded must be true") |
| | | require(report.get("appCount") == fixture_app_count - 1, "single-window fixture must hide the committed target App after activation") |
| | | require(report.get("windowCount") == fixture_app_count - 1, "single-window fixture must hide the committed target window after activation") |
| | | require(root.get("waterfallViewMode") == expected_mode, "single-window run must use horizontal Waterfall") |
| | | require(root.get("keyboardCommandsApplied") == expected_sequence, "single-window key sequence must direct-commit") |
| | | require(root.get("appShelfIndexKeyDownCount") == 1, "single-window direct commit must use one physical keyDown") |
| | | require(root.get("lastAppShelfIndexKeySymbol") == app_symbol, "last physical symbol must be the App key") |
| | | require(root.get("lastWindowIndexKeyCommand") == f"windowSingle:{app_symbol}", "single-window direct command must be reported") |
| | | require(root.get("lastWindowIndexKeyCommitCode") == app_symbol, "single-window commit code must be the one-key shortcut") |
| | | require(root.get("lastCommitSource") == "keyboard", "single-window direct commit must be keyboard sourced") |
| | | require(root.get("windowIndexKeyPendingAppGroupIndex") is None, "single-window direct commit must not leave pending App group") |
| | | require(root.get("windowIndexKeyPendingAppSymbol") is None, "single-window direct commit must not leave pending App symbol") |
| | | require(root.get("windowShortcutFilterActive") is False, "single-window direct commit must not enter progressive filter") |
| | | require(root.get("windowShortcutFilterPhase") == "none", "single-window direct commit must keep filter phase none") |
| | | require(root.get("windowShortcutFilterMatchedCount") == 0, "single-window direct commit must not leave matched filter count") |
| | | require(root.get("windowShortcutFilterDimmedCount") == 0, "single-window direct commit must not leave dimmed filter count") |
| | | require(root.get("lastWindowShortcutPreCommitFilterSnapshot") is None, "single-window direct commit must not expose two-key pre-commit snapshot") |
| | | |
| | | require(root.get("lastCommittedAppGroupIndex") == target_app_index, "direct commit must target expected App") |
| | | require(root.get("lastCommittedWindowIndex") == 0, "direct commit must target the only window") |
| | | require(root.get("lastCommittedWindowID") == target_window_id, "direct commit must target expected window ID") |
| | | require(report.get("suppressedActivationWindowIDs") == [target_window_id], "direct commit must suppress the activated target window") |
| | | require(report.get("suppressedActivationWindowCount") == 1, "direct commit must report one suppressed activation window") |
| | | require(target_window_id not in all_window_ids, "direct-committed target window must not remain in visible cards after activation") |
| | | require(target_app_index not in [column.get("appGroupIndex") for column in columns], "single-window target App must not remain visible after activation") |
| | | |
| | | for card in cards: |
| | | opacity = card.get("windowShortcutFilterOpacity") |
| | | states = card.get("visualStates", []) |
| | | require(card.get("windowShortcutFilterState") == "none", "single-window direct commit must not leave card filter state") |
| | | require(is_number(opacity), "cards must expose numeric filter opacity") |
| | | require(float(opacity) >= 0.99, "single-window direct commit must not dim cards") |
| | | require("shortcutDimmed" not in states, "single-window direct commit must not leave shortcutDimmed state") |
| | | require("shortcutMatched" not in states, "single-window direct commit must not leave shortcutMatched state") |
| | | require("shortcutTarget" not in states, "single-window direct commit must not leave shortcutTarget state") |
| | | |
| | | summary = { |
| | | "case": case_name, |
| | | "status": "passed", |
| | | "mode": root.get("waterfallViewMode"), |
| | | "report": path, |
| | | "commands": root.get("keyboardCommandsApplied"), |
| | | "targetWindowID": target_window_id, |
| | | "lastCommitSource": root.get("lastCommitSource"), |
| | | "lastWindowIndexKeyCommitCode": root.get("lastWindowIndexKeyCommitCode"), |
| | | "suppressedActivationWindowIDs": report.get("suppressedActivationWindowIDs"), |
| | | "cardCount": len(cards) |
| | | } |
| | | with open(summary_file, "w", encoding="utf-8") as file: |
| | | json.dump(summary, file, indent=2, ensure_ascii=False) |
| | | PY |
| | | |
| | | SUMMARY_FILES+=("$summary_file") |
| | | } |
| | | |
| | | assert_pending_summaries_consistent() { |
| | | local baseline="$1" |
| | | local candidate="$2" |
| | |
| | | trap cleanup EXIT |
| | | stop_current_aligner |
| | | |
| | | run_fixture "$REPORT_SINGLE_WINDOW" "light" "horizontal" "$(pending_key_sequence)" 1 |
| | | wait_for_single_window_report "$REPORT_SINGLE_WINDOW" "$(mode_report_value horizontal)" "$(single_window_expected_sequence)" |
| | | assert_single_window_report "$REPORT_SINGLE_WINDOW" |
| | | finish_case |
| | | |
| | | run_fixture "$REPORT_HORIZONTAL_LIGHT" "light" "horizontal" "$(pending_key_sequence)" |
| | | wait_for_pending_report "$REPORT_HORIZONTAL_LIGHT" "$(mode_report_value horizontal)" "$(pending_expected_sequence)" "pending-horizontal-light" |
| | | assert_pending_report "$REPORT_HORIZONTAL_LIGHT" "pending-horizontal-light" "light" "$(mode_report_value horizontal)" |