| | |
| | | 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 |
| | | } |
| | |
| | | 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", [])] |
| | | target_column = next((column for column in columns if column.get("appGroupIndex") == target_app_index), None) |
| | | |
| | | 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, "single-window fixture must have one window per App") |
| | | 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(target_column is not None, "target App column must exist") |
| | | target_cards = target_column.get("cards", []) |
| | | require(len(target_cards) == 1, "target App must have exactly one visible window") |
| | | target_card = target_cards[0] |
| | | target_window_id = int(target_card.get("windowID")) |
| | | 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(root.get("selectedAppGroupIndex") == target_app_index, "selection must land on target App") |
| | | require(root.get("selectedWindowIndex") == 0, "selection must land on the only window") |
| | | require(root.get("selectedWindowID") == target_window_id, "selectedWindowID must match target window") |
| | | |
| | | 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"), |
| | | "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)" |