diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 39adbe03d3a0..55febe392da2 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -2822,6 +2822,9 @@ jobs: env: SELECTED_PACKAGES: ${{ steps.select.outputs.selected_packages }} SELECTED_COUNT: ${{ steps.select.outputs.selected_count }} + # macOS 15 disables Swift's crash backtracer by default. Preserve + # a bounded symbolic stack in the log when a package runner dies. + SWIFT_BACKTRACE: "enable=yes,interactive=no,timeout=15s,symbolicate=fast,color=no" run: | set -euo pipefail # The cmux-unit scheme only runs the cmuxTests app-host suite; it does @@ -2927,6 +2930,30 @@ jobs: echo "::endgroup::" done < "$selected" + - name: Diagnose Settings UI package crash + if: ${{ failure() }} + continue-on-error: true + timeout-minutes: 3 + run: | + set -euo pipefail + package="$PWD/Packages/macOS/CmuxSettingsUI" + bundle="$(find "$package/.build" -path '*/CmuxSettingsUIPackageTests.xctest/Contents/MacOS/CmuxSettingsUIPackageTests' -type f -print -quit)" + [ -n "$bundle" ] || exit 0 + toolchain_usr="$(dirname "$(dirname "$(xcrun --find swift)")")" + helper="$RUNNER_TEMP/cmux-settings-testing-helper" + cp "$toolchain_usr/libexec/swift/pm/swiftpm-testing-helper" "$helper" + cat > "$RUNNER_TEMP/cmux-settings-debug.plist" <<'PLIST' + + + com.apple.security.get-task-allow + PLIST + codesign --force --sign - --entitlements "$RUNNER_TEMP/cmux-settings-debug.plist" "$helper" + python3 scripts/ci/run_with_timeout.py --timeout-seconds 120 -- \ + xcrun lldb --batch \ + -o 'settings set target.disable-aslr false' \ + -o run -o 'thread backtrace all' \ + -- "$helper" --test-bundle-path "$bundle" --testing-library swift-testing + # A package test runner that dies on a signal prints one line from # SwiftPM ("Exited with unexpected signal code N") and nothing else: no # faulting address, no thread, no frames. That is why the only tool this diff --git a/Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/GlobalSearchShortcutSettingsModelTests.swift b/Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/GlobalSearchShortcutSettingsModelTests.swift index d063f00e6d60..951d7dd649ba 100644 --- a/Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/GlobalSearchShortcutSettingsModelTests.swift +++ b/Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/GlobalSearchShortcutSettingsModelTests.swift @@ -7,6 +7,7 @@ import Testing @Suite(.serialized) final class GlobalSearchShortcutSettingsModelTests { @Test func distinctChordSuffixesCanShareAPrefix() async throws { + FileHandle.standardError.write(Data("global-search-chord: creating fixture\n".utf8)) let directory = try makeTemporaryDirectory() defer { try? FileManager.default.removeItem(at: directory) } @@ -24,6 +25,7 @@ final class GlobalSearchShortcutSettingsModelTests { option: true, control: true ) + FileHandle.standardError.write(Data("global-search-chord: fixture ready\n".utf8)) let settingsChord = StoredShortcut( first: prefix, second: ShortcutStroke(key: "p") @@ -34,12 +36,15 @@ final class GlobalSearchShortcutSettingsModelTests { ) await model.assignChord(settingsChord, to: .openSettings) + FileHandle.standardError.write(Data("global-search-chord: settings chord assigned\n".utf8)) await model.assignChord(searchChord, to: .globalSearch) + FileHandle.standardError.write(Data("global-search-chord: search chord assigned\n".utf8)) let bindings = await store.value(for: catalog.shortcuts.bindings) #expect(bindings[ShortcutAction.openSettings.rawValue] == settingsChord) #expect(bindings[ShortcutAction.globalSearch.rawValue] == searchChord) #expect(model.conflictRejections[ShortcutAction.globalSearch.rawValue] == nil) + FileHandle.standardError.write(Data("global-search-chord: assertions complete\n".utf8)) } @Test func persistedMediaKeyGlobalSearchBindingIsNotEffective() async throws {