Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .github/workflows/ci-macos-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,27 @@ jobs:
needs: change-detection
if: needs.change-detection.outputs.run_compat_tests == 'true'
strategy:
fail-fast: true
# The two legs are independent OS-compatibility checks, so cancelling one
# because the other failed destroys the exact signal this job exists to
# produce. Worse, GitHub reports the cancelled leg as a *failure* to
# `gh pr checks`, so a leg that was passing when it got killed reads as
# broken. That cost real debugging time: two separate "macos-15 is failing
# everywhere" conclusions turned out to be macos-15 being cancelled
# mid-pass after macos-26 failed, only visible by reading the raw job log.
# Both legs should always run to completion and report independently.
fail-fast: false
matrix:
include:
- os: macos-15
timeout: 45
smoke: true
skip_zig: false
# This runner starves async completions partway through the serial
# suite, timing out a fixed set of tests that are not broken on
# macOS 15 -- see QUARANTINED_ON_COMPAT in scripts/ci-run-unit-tests.sh
# for the evidence. Every quarantined test still runs on the macos-26
# leg and in the main CI workflow, so no coverage is lost.
quarantine: true
- os: macos-26
timeout: 60 # macos-26 runner is ~2x slower than macos-15; 45m was too tight on a cold DerivedData cache (see cache key on project.pbxproj)
smoke: false
Expand Down Expand Up @@ -209,6 +223,7 @@ jobs:
env:
PROGRAMA_SKIP_ZIG_BUILD: ${{ matrix.skip_zig && '1' || '0' }}
PROGRAMA_UNIT_TEST_SCOPE: serial
PROGRAMA_UNIT_TEST_QUARANTINE: ${{ matrix.quarantine && '1' || '' }}
run: |
./scripts/ci-run-unit-tests.sh

Expand Down
55 changes: 55 additions & 0 deletions scripts/ci-run-unit-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,53 @@ TEST_SCOPE="${PROGRAMA_UNIT_TEST_SCOPE:-serial}"
STATEFUL_TEST_CLASS="programaTests/AppDelegateShortcutRoutingTests"
STATEFUL_TEST_SKIP="${STATEFUL_TEST_CLASS}/testCmdWClosesWindowWhenClosingLastSurfaceInLastWorkspace"

# Tests quarantined when PROGRAMA_UNIT_TEST_QUARANTINE is set (the macos-15
# compat leg). These 17 fail together, identically, on every macos-15 run --
# verified by intersecting the failure sets of independent runs -- and they are
# NOT failing because the code under test is broken on macOS 15:
#
# * There is no OS-version-conditional code anywhere in the paths they cover.
# A genuinely unguarded macOS-26-only symbol could not compile at all here,
# since Swift checks availability against MACOSX_DEPLOYMENT_TARGET (14.0).
# * Raising every wait budget 4x (ciScale) changed nothing. A 12s wait timing
# out is not a slow overlay.
# * They fail alongside raw Unix-socket and subprocess waits in the same run.
# A BSD socket accept and a DispatchQueue.main.async closure share no
# SwiftUI or AppKit code; the only thing in common is needing an async
# completion serviced promptly. That points at runner starvation.
#
# The suite runs serially in fixed order, so a runner that degrades partway
# through starves the same tests every time -- deterministic failures from a
# non-deterministic cause.
#
# IMPORTANT: this quarantine is scoped to the compat leg only. Every test below
# still runs on the macos-26 compat leg AND in the main CI workflow's unit-tests
# job on every PR, so none of this coverage is actually lost -- including the
# TerminalControllerSocketSecurityTests entries, which are security tests and
# would otherwise be the most alarming thing on this list.
#
# This is a workaround for CI infrastructure, not a fix. Revisit if the macos-15
# runner image changes or the Ghostty surface churn in these tests is reduced.
QUARANTINED_ON_COMPAT=(
"programaTests/CLINotifyProcessIntegrationTests/testSSHBootstrapStartupCommandPassesRemoteInstallScriptAsSingleSSHCommand"
"programaTests/GhosttySurfaceOverlayTests/testDropHoverOverlayAttachesToParentContainerInsteadOfHostedTerminalView"
"programaTests/GhosttySurfaceOverlayTests/testEscapeDismissingFindOverlayDoesNotLeakEscapeKeyUpToTerminal"
"programaTests/GhosttySurfaceOverlayTests/testSearchOverlayFocusesSearchFieldAfterDeferredAttach"
"programaTests/GhosttySurfaceOverlayTests/testSearchOverlayMountDoesNotRetainTerminalSurface"
"programaTests/GhosttySurfaceOverlayTests/testSearchOverlayMountsAndUnmountsWithSearchState"
"programaTests/GhosttySurfaceOverlayTests/testSearchOverlaySurvivesPortalRebindDuringSplitLikeChurn"
"programaTests/GhosttySurfaceOverlayTests/testSearchOverlaySurvivesPortalVisibilityToggleDuringWorkspaceSwitchLikeChurn"
"programaTests/NotificationDockBadgeTests/testFocusedTerminalSuppressedNotificationRunsCustomCommand"
"programaTests/TerminalControllerSocketSecurityTests/testNotificationCreateUsesExplicitSurfaceIDWhenProvided"
"programaTests/TerminalControllerSocketSecurityTests/testPasswordModeRejectsUnauthenticatedCommands"
"programaTests/TerminalControllerSocketSecurityTests/testSocketPermissionsFollowAccessMode"
"programaTests/TerminalControllerSocketSecurityTests/testSurfaceRelayRPCsAcknowledgeImmediatelyAndNoOpForUnknownSurfaceID"
"programaTests/TerminalControllerSocketSecurityTests/testSurfaceRelayRPCsAcknowledgeImmediatelyAndResolveFocusedSurfaceAsync"
"programaTests/TerminalNotificationDirectInteractionTests/testKeyDownRecoversReleasedSurfaceWhileHostedViewIsDetached"
"programaTests/TerminalNotificationDirectInteractionTests/testKeyDownRecoveryDoesNotReplayFocusAfterResponderMovesAway"
"programaTests/TerminalWindowPortalLifecycleTests/testScheduledExternalGeometrySyncRefreshesAncestorLayoutShift"
)

RESULT_BUNDLE_ROOT="${PROGRAMA_RESULT_BUNDLE_ROOT:-/tmp/programa-unit-xcresults}"

run_unit_tests() {
Expand Down Expand Up @@ -62,6 +109,14 @@ run_unit_tests() {
;;
esac

if [[ -n "${PROGRAMA_UNIT_TEST_QUARANTINE:-}" ]]; then
echo "Quarantining ${#QUARANTINED_ON_COMPAT[@]} runner-starvation-prone tests (see QUARANTINED_ON_COMPAT)" >&2
local quarantined
for quarantined in "${QUARANTINED_ON_COMPAT[@]}"; do
xcode_args+=("-skip-testing:${quarantined}")
done
fi

"${xcode_args[@]}" test 2>&1
}

Expand Down
Loading