Skip to content

Commit e21bf43

Browse files
author
Cameron Cooke
authored
feat(testing): Support reusable prepared test artifacts (#475)
* feat(xcodebuild): Support prepared test artifacts Extend existing build and test tools to prepare, return, and consume reusable test artifacts while preserving normal build behavior. Fixes #450 * feat(manifest): Support conditional next steps Move conditional next-step selection into tool manifests and preserve the behavior across MCP, CLI, daemon, and snapshot outputs. Refs #450 * fix(testing): Stabilize prepared test execution Keep source-only xcodebuild arguments out of the generated test-without-building phase and sort final test failures independently of emission order. * test(snapshot): Align device test schema versions * fix(test): Extract failures from managed result bundles * fix(test): Preserve explicit test destinations * test(snapshot): isolate snapshot preflight state Share per-test environment setup and cleanup across snapshot suites, cache suite-level simulator builds, and normalize volatile output so fixtures remain stable. Update non-environmental fixtures and add coverage for cleanup and normalization behavior. * fix(testing): address prepared test review findings Prevent session defaults and selectors from leaking into prepared test runs, remove a useless simulator state initializer, and align volatile snapshot fixtures across CLI and MCP. * fix(testing): preserve prepared test selectors Keep only-testing and skip-testing arguments in the prepared test phase while retaining session-default conflict filtering. * fix(testing): only mark successful test products complete * fix: stabilize prepared test snapshot fixtures * fix: honor test product retention options * fix: reject device destinations without prepared tests * fix: isolate session destinations for device builds * fix: mark failed prepared builds complete * fix: remove unstable snapshot fixture churn * fix(snapshot): Tolerate simulator lifecycle races Retry app cleanup after a concurrent simulator shutdown and tolerate a boot that completes between the state check and boot command. * test(snapshot): Align prepared test fixtures Update the discovery schema expectation and keep device and macOS fixtures aligned with the generated artifacts. * fix(snapshot): Handle teardown shutdown races Treat an already-shut-down simulator as successful cleanup and align compiler-failure fixtures with the emitted result shape. * fix: Align prepared test review fixes * fix(snapshot): Stabilize isolated fixture output * fix(test): Sanitize managed test product paths * fix: preserve test product retention cap
1 parent 60cfdc3 commit e21bf43

363 files changed

Lines changed: 8223 additions & 2258 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66

77
- Added `xcodebuildmcp purge` to report and explicitly clean XcodeBuildMCP-managed workspace storage, including opt-in DerivedData cleanup with dry-run and confirmation safeguards.
88
- Added `extraArgs` as a first-class session-default value. Repo config or runtime defaults can now carry common `xcodebuild` flags (for example `-skipPackagePluginValidation` or `-disableAutomaticPackageResolution`) so they don't need repeating on every build or test call. Per-call `extraArgs` replace matching configured flags or build settings and append after non-matching defaults, while an explicit empty array (`extraArgs: []`) clears the defaults for a single call. The session management tools show, set, sync, and clear `extraArgs` alongside the other defaults.
9+
- Added reusable test preparation to the existing simulator, device, and macOS build tools. Builds can now return a portable `.xctestproducts` artifact, and test tools can run that artifact—or an advanced `.xctestrun` input—without rebuilding before producing a new `.xcresult` ([#450](https://github.com/getsentry/XcodeBuildMCP/issues/450)).
10+
11+
### Changed
12+
13+
- Tool manifests can now conditionally expose static next steps using handler-activated condition keys, while runtime-generated parameters continue to flow through the existing next-step parameter contract.
914

1015
### Fixed
1116

@@ -694,4 +699,3 @@ Please note that the UI automation features are an early preview and currently i
694699
## [v1.0.1] - 2025-04-02
695700
- Initial release of XcodeBuildMCP
696701
- Basic support for building iOS and macOS applications
697-

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ xcodebuildmcp tools
8585

8686
# Build for simulator
8787
xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj
88+
89+
# Prepare portable test products without running tests
90+
xcodebuildmcp simulator build --scheme MyApp --project-path ./MyApp.xcodeproj --simulator-name "iPhone 17" --build-for-testing --test-products-path ./MyApp.xctestproducts
91+
92+
# Run previously prepared test products
93+
xcodebuildmcp simulator test --test-products-path ./MyApp.xctestproducts --simulator-name "iPhone 17"
8894
```
8995

9096
Check for updates and upgrade in place:

‎example_projects/iOS_Calculator/CalculatorApp/CalculatorApp.swift‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@ import OSLog
33
import CalculatorAppFeature
44

55
private let logger = Logger(subsystem: "io.sentry.calculatorapp", category: "lifecycle")
6+
private let snapshotScrollSurfaceArgument = "--snapshot-scroll-surface"
67

78
@main
89
struct CalculatorApp: App {
910
@Environment(\.scenePhase) private var scenePhase
1011

1112
var body: some Scene {
1213
WindowGroup {
13-
ContentView()
14+
if ProcessInfo.processInfo.arguments.contains(snapshotScrollSurfaceArgument) {
15+
SnapshotScrollSurface()
16+
} else {
17+
ContentView()
18+
}
1419
}
1520
.onChange(of: scenePhase) { _, newPhase in
1621
switch newPhase {
@@ -25,6 +30,21 @@ struct CalculatorApp: App {
2530
}
2631
}
2732

33+
private struct SnapshotScrollSurface: View {
34+
var body: some View {
35+
ScrollView {
36+
LazyVStack(spacing: 16) {
37+
ForEach(0..<30, id: \.self) { index in
38+
Text("Snapshot row \(index)")
39+
.frame(maxWidth: .infinity, minHeight: 44)
40+
}
41+
}
42+
.padding()
43+
}
44+
.accessibilityIdentifier("snapshot-scroll-surface")
45+
}
46+
}
47+
2848
#Preview {
2949
ContentView()
3050
}

‎manifests/tools/batch.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,8 @@ annotations:
1515
readOnlyHint: true
1616
destructiveHint: false
1717
openWorldHint: false
18+
nextSteps:
19+
- label: Refresh after UI action
20+
toolId: snapshot_ui
21+
when: success
22+
condition: ui_action_needs_refresh

‎manifests/tools/build_device.yaml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ names:
66
description: Build for device.
77
outputSchema:
88
schema: xcodebuildmcp.output.build-result
9-
version: "2"
9+
version: '3'
1010
predicates:
1111
- hideWhenXcodeAgentMode
1212
annotations:
@@ -19,3 +19,8 @@ nextSteps:
1919
toolId: get_device_app_path
2020
priority: 1
2121
when: success
22+
condition: app_build_succeeded
23+
- label: Run prepared tests
24+
toolId: test_device
25+
when: success
26+
condition: prepared_tests_available

‎manifests/tools/build_macos.yaml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ names:
66
description: Build macOS app.
77
outputSchema:
88
schema: xcodebuildmcp.output.build-result
9-
version: "2"
9+
version: '3'
1010
predicates:
1111
- hideWhenXcodeAgentMode
1212
annotations:
@@ -19,3 +19,8 @@ nextSteps:
1919
toolId: get_mac_app_path
2020
priority: 1
2121
when: success
22+
condition: app_build_succeeded
23+
- label: Run prepared tests
24+
toolId: test_macos
25+
when: success
26+
condition: prepared_tests_available

‎manifests/tools/build_sim.yaml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ names:
66
description: Build for iOS sim (compile-only, no launch).
77
outputSchema:
88
schema: xcodebuildmcp.output.build-result
9-
version: "2"
9+
version: '3'
1010
predicates:
1111
- hideWhenXcodeAgentMode
1212
annotations:
@@ -19,3 +19,8 @@ nextSteps:
1919
toolId: get_sim_app_path
2020
priority: 1
2121
when: success
22+
condition: app_build_succeeded
23+
- label: Run prepared tests
24+
toolId: test_sim
25+
when: success
26+
condition: prepared_tests_available

‎manifests/tools/button.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@ annotations:
1414
readOnlyHint: true
1515
destructiveHint: false
1616
openWorldHint: false
17+
nextSteps:
18+
- label: Refresh after UI action
19+
toolId: snapshot_ui
20+
when: success
21+
condition: ui_action_needs_refresh

‎manifests/tools/drag.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,8 @@ annotations:
1515
readOnlyHint: true
1616
destructiveHint: false
1717
openWorldHint: false
18+
nextSteps:
19+
- label: Refresh after UI action
20+
toolId: snapshot_ui
21+
when: success
22+
condition: ui_action_needs_refresh

‎manifests/tools/gesture.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@ annotations:
1414
readOnlyHint: true
1515
destructiveHint: false
1616
openWorldHint: false
17+
nextSteps:
18+
- label: Refresh after UI action
19+
toolId: snapshot_ui
20+
when: success
21+
condition: ui_action_needs_refresh

0 commit comments

Comments
 (0)