zig-mobile-runner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +484 -0
- package/CONTRIBUTING.md +42 -0
- package/FEATURES.md +112 -0
- package/LICENSE +21 -0
- package/README.md +255 -0
- package/SECURITY.md +34 -0
- package/build.zig +38 -0
- package/build.zig.zon +7 -0
- package/clients/README.md +144 -0
- package/clients/go/README.md +24 -0
- package/clients/go/examples/fake-session/main.go +93 -0
- package/clients/go/go.mod +3 -0
- package/clients/go/zmr/client.go +432 -0
- package/clients/kotlin/README.md +35 -0
- package/clients/kotlin/build.gradle.kts +35 -0
- package/clients/kotlin/settings.gradle.kts +15 -0
- package/clients/kotlin/src/main/kotlin/dev/zmr/FakeSession.kt +86 -0
- package/clients/kotlin/src/main/kotlin/dev/zmr/ZmrClient.kt +67 -0
- package/clients/python/README.md +29 -0
- package/clients/python/examples/fake_session.py +48 -0
- package/clients/python/pyproject.toml +13 -0
- package/clients/python/zmr_client.py +202 -0
- package/clients/rust/Cargo.lock +107 -0
- package/clients/rust/Cargo.toml +10 -0
- package/clients/rust/README.md +19 -0
- package/clients/rust/examples/fake_session.rs +70 -0
- package/clients/rust/src/lib.rs +461 -0
- package/clients/swift/Package.swift +16 -0
- package/clients/swift/README.md +36 -0
- package/clients/swift/Sources/ZMRClient/ZMRClient.swift +114 -0
- package/clients/swift/Sources/ZMRFakeSession/main.swift +86 -0
- package/clients/typescript/README.md +34 -0
- package/clients/typescript/examples/fake-session.mjs +36 -0
- package/clients/typescript/index.d.ts +144 -0
- package/clients/typescript/index.mjs +192 -0
- package/clients/typescript/package.json +8 -0
- package/docs/adr/0001-agent-native-runner-boundary.md +31 -0
- package/docs/adr/0002-app-local-zmr-contract.md +39 -0
- package/docs/adr/0003-ios-simulator-xctest-shim.md +41 -0
- package/docs/adr/0004-benchmark-claims-and-baseline-collection.md +37 -0
- package/docs/adr/README.md +12 -0
- package/docs/ai-agents.md +156 -0
- package/docs/app-integration.md +316 -0
- package/docs/benchmarking.md +275 -0
- package/docs/client-installation.md +141 -0
- package/docs/clients.md +98 -0
- package/docs/config.md +175 -0
- package/docs/demo.md +259 -0
- package/docs/dsl.md +57 -0
- package/docs/install.md +233 -0
- package/docs/market-positioning.md +70 -0
- package/docs/npm.md +359 -0
- package/docs/protocol-fixtures/README.md +8 -0
- package/docs/protocol-fixtures/core-session.requests.jsonl +8 -0
- package/docs/protocol-fixtures/core-session.responses.jsonl +8 -0
- package/docs/protocol-versioning.md +65 -0
- package/docs/protocol.md +560 -0
- package/docs/publication.md +77 -0
- package/docs/release-audit.md +99 -0
- package/docs/release-candidate.md +111 -0
- package/docs/release-evidence.md +188 -0
- package/docs/release-notes-template.md +58 -0
- package/docs/roadmap.md +334 -0
- package/docs/scenario-authoring.md +88 -0
- package/docs/shipping.md +170 -0
- package/docs/trace-privacy.md +88 -0
- package/docs/troubleshooting.md +256 -0
- package/examples/android-app-auth-probe.json +89 -0
- package/examples/android-app-error-state.json +13 -0
- package/examples/android-app-login-smoke.json +192 -0
- package/examples/android-app-onboarding.json +12 -0
- package/examples/android-app-referral-deep-link.json +12 -0
- package/examples/android-shim-smoke.json +19 -0
- package/examples/demo-failure.json +12 -0
- package/examples/demo-fake.json +14 -0
- package/examples/ios-dev-client-open-link.json +26 -0
- package/examples/ios-dev-client-route-snapshot.json +24 -0
- package/examples/ios-shim-smoke.json +23 -0
- package/examples/ios-smoke.json +9 -0
- package/go.work +3 -0
- package/npm/agents.mjs +183 -0
- package/npm/app-config.mjs +95 -0
- package/npm/build-zmr.mjs +21 -0
- package/npm/commands.mjs +104 -0
- package/npm/generated-files.mjs +50 -0
- package/npm/index.mjs +75 -0
- package/npm/init-app.mjs +80 -0
- package/npm/package-scripts.mjs +72 -0
- package/npm/postinstall.mjs +21 -0
- package/npm/scaffold.mjs +179 -0
- package/npm/scenarios.mjs +93 -0
- package/npm/setup.mjs +69 -0
- package/npm/wizard.mjs +117 -0
- package/npm/zmr.mjs +23 -0
- package/package.json +114 -0
- package/prebuilds/darwin-arm64/zmr +0 -0
- package/prebuilds/darwin-x64/zmr +0 -0
- package/prebuilds/linux-arm64/zmr +0 -0
- package/prebuilds/linux-x64/zmr +0 -0
- package/schemas/README.md +26 -0
- package/schemas/action-result.schema.json +27 -0
- package/schemas/capabilities-output.schema.json +98 -0
- package/schemas/devices-output.schema.json +25 -0
- package/schemas/doctor-output.schema.json +51 -0
- package/schemas/explain-output.schema.json +51 -0
- package/schemas/import-output.schema.json +23 -0
- package/schemas/init-output.schema.json +71 -0
- package/schemas/json-rpc.schema.json +55 -0
- package/schemas/release-manifest.schema.json +43 -0
- package/schemas/release-readiness-output.schema.json +127 -0
- package/schemas/run-output.schema.json +43 -0
- package/schemas/scenario.schema.json +128 -0
- package/schemas/schemas-output.schema.json +26 -0
- package/schemas/semantic-snapshot.schema.json +116 -0
- package/schemas/snapshot.schema.json +60 -0
- package/schemas/trace-event.schema.json +14 -0
- package/schemas/trace-manifest.schema.json +59 -0
- package/schemas/validate-output.schema.json +42 -0
- package/schemas/version-output.schema.json +23 -0
- package/schemas/zmr-config.schema.json +75 -0
- package/scripts/android-emulator.sh +126 -0
- package/scripts/assert-ios-physical-ready.sh +213 -0
- package/scripts/benchmark-command.sh +307 -0
- package/scripts/benchmark.sh +359 -0
- package/scripts/benchmark_gate.py +117 -0
- package/scripts/benchmark_result_row.py +88 -0
- package/scripts/compare-benchmarks.py +288 -0
- package/scripts/create-android-demo-app.sh +342 -0
- package/scripts/create-ios-demo-app.sh +261 -0
- package/scripts/demo-android-real.sh +232 -0
- package/scripts/demo-ios-real.sh +270 -0
- package/scripts/demo.sh +464 -0
- package/scripts/device-matrix.sh +338 -0
- package/scripts/ensure-ios-shim-target.rb +237 -0
- package/scripts/install-android-shim.sh +281 -0
- package/scripts/install-ios-shim.sh +589 -0
- package/scripts/pilot-gate.sh +560 -0
- package/scripts/release-readiness.py +838 -0
- package/scripts/release-readiness.sh +91 -0
- package/scripts/run-android-pilot.sh +561 -0
- package/scripts/run-ios-pilot.sh +509 -0
- package/shims/android/README.md +21 -0
- package/shims/android/ZMRShimInstrumentedTest.java +152 -0
- package/shims/android/protocol.md +18 -0
- package/shims/ios/README.md +50 -0
- package/shims/ios/ZMRShim.swift +110 -0
- package/shims/ios/ZMRShimUITestCase.swift +475 -0
- package/shims/ios/protocol.md +74 -0
- package/skills/zmr-mobile-testing/SKILL.md +127 -0
- package/src/android.zig +344 -0
- package/src/android_device_info.zig +99 -0
- package/src/android_emulator.zig +154 -0
- package/src/android_screen_recording.zig +112 -0
- package/src/android_shell.zig +112 -0
- package/src/bundle.zig +124 -0
- package/src/bundle_redaction.zig +272 -0
- package/src/bundle_tar.zig +123 -0
- package/src/cli_devices.zig +97 -0
- package/src/cli_doctor.zig +114 -0
- package/src/cli_import.zig +70 -0
- package/src/cli_info.zig +39 -0
- package/src/cli_init.zig +72 -0
- package/src/cli_output.zig +467 -0
- package/src/cli_run.zig +259 -0
- package/src/cli_serve.zig +287 -0
- package/src/cli_trace.zig +111 -0
- package/src/cli_validate.zig +41 -0
- package/src/command.zig +211 -0
- package/src/config.zig +305 -0
- package/src/config_diagnostics.zig +212 -0
- package/src/config_paths.zig +49 -0
- package/src/device_registry.zig +37 -0
- package/src/doctor.zig +412 -0
- package/src/doctor_hints.zig +52 -0
- package/src/errors.zig +55 -0
- package/src/fake_device.zig +163 -0
- package/src/health.zig +28 -0
- package/src/importer.zig +343 -0
- package/src/importer_json.zig +100 -0
- package/src/importer_model.zig +103 -0
- package/src/ios.zig +399 -0
- package/src/ios_devices.zig +219 -0
- package/src/ios_lifecycle.zig +72 -0
- package/src/ios_shim.zig +242 -0
- package/src/ios_snapshot.zig +20 -0
- package/src/json_fields.zig +80 -0
- package/src/json_rpc.zig +150 -0
- package/src/json_rpc_methods.zig +318 -0
- package/src/json_rpc_observation.zig +31 -0
- package/src/json_rpc_params.zig +52 -0
- package/src/json_rpc_protocol.zig +110 -0
- package/src/json_rpc_trace.zig +73 -0
- package/src/main.zig +135 -0
- package/src/mcp.zig +234 -0
- package/src/mcp_protocol.zig +64 -0
- package/src/mcp_trace.zig +83 -0
- package/src/report.zig +346 -0
- package/src/report_html.zig +63 -0
- package/src/report_values.zig +27 -0
- package/src/run_options.zig +152 -0
- package/src/runner.zig +280 -0
- package/src/runner_actions.zig +109 -0
- package/src/runner_config.zig +6 -0
- package/src/runner_diagnostics.zig +268 -0
- package/src/runner_events.zig +170 -0
- package/src/runner_native.zig +88 -0
- package/src/runner_waits.zig +300 -0
- package/src/scaffold.zig +472 -0
- package/src/scenario.zig +346 -0
- package/src/scenario_fields.zig +50 -0
- package/src/schema_registry.zig +53 -0
- package/src/selector.zig +84 -0
- package/src/semantic.zig +171 -0
- package/src/trace.zig +315 -0
- package/src/trace_json.zig +340 -0
- package/src/trace_summary.zig +218 -0
- package/src/trace_summary_diagnostic.zig +202 -0
- package/src/types.zig +120 -0
- package/src/uiautomator.zig +164 -0
- package/src/validation.zig +187 -0
- package/src/version.zig +22 -0
- package/viewer/app.js +373 -0
- package/viewer/index.html +126 -0
- package/viewer/parser.js +233 -0
- package/viewer/styles.css +585 -0
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
5
|
+
while [[ -h "$SOURCE" ]]; do
|
|
6
|
+
SOURCE_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
7
|
+
SOURCE="$(readlink "$SOURCE")"
|
|
8
|
+
if [[ "$SOURCE" != /* ]]; then
|
|
9
|
+
SOURCE="$SOURCE_DIR/$SOURCE"
|
|
10
|
+
fi
|
|
11
|
+
done
|
|
12
|
+
|
|
13
|
+
ROOT="$(cd -P "$(dirname "$SOURCE")/.." && pwd)"
|
|
14
|
+
APP_ROOT=""
|
|
15
|
+
SCHEME=""
|
|
16
|
+
TEST_TARGET=""
|
|
17
|
+
APP_TARGET=""
|
|
18
|
+
BUNDLE_ID=""
|
|
19
|
+
TEST_BUNDLE_ID=""
|
|
20
|
+
DEVICE="booted"
|
|
21
|
+
DEVICE_TYPE="simulator"
|
|
22
|
+
CONFIGURATION="Debug"
|
|
23
|
+
WORKSPACE=""
|
|
24
|
+
PROJECT=""
|
|
25
|
+
DERIVED_DATA_PATH=""
|
|
26
|
+
DEPLOYMENT_TARGET="15.0"
|
|
27
|
+
PATCH_XCODEPROJ=0
|
|
28
|
+
|
|
29
|
+
usage() {
|
|
30
|
+
cat <<'USAGE'
|
|
31
|
+
Usage:
|
|
32
|
+
scripts/install-ios-shim.sh --app-root <dir> --scheme <UITestScheme> --bundle-id <id> [options]
|
|
33
|
+
|
|
34
|
+
Writes an app-local .zmr/ios-shim command and XCTest source file.
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--app-root <dir> App repository root. Required.
|
|
38
|
+
--scheme <scheme> Xcode UI test scheme that includes ZMRShimUITestCase. Required.
|
|
39
|
+
--bundle-id <id> App bundle id under test. Required.
|
|
40
|
+
--app-target <name> App target name. Enables generated Xcode target helper.
|
|
41
|
+
--test-target <name> UI test target name. Default: scheme.
|
|
42
|
+
--test-bundle-id <id> UI test bundle id. Default: <bundle-id>.zmr-uitests.
|
|
43
|
+
--workspace <path> Workspace path relative to app root.
|
|
44
|
+
--project <path> Xcode project path relative to app root.
|
|
45
|
+
--derived-data-path <path>
|
|
46
|
+
Derived data path relative to app root.
|
|
47
|
+
--device <udid|booted> Simulator or physical-device destination id. Default: booted.
|
|
48
|
+
--device-type <type> simulator or physical. Default: simulator.
|
|
49
|
+
--configuration <name> Xcode build configuration. Default: Debug.
|
|
50
|
+
--deployment-target <version>
|
|
51
|
+
iOS deployment target for generated UI test target. Default: 15.0.
|
|
52
|
+
--patch-xcodeproj Run the generated Xcodeproj helper immediately.
|
|
53
|
+
-h, --help Show this help.
|
|
54
|
+
|
|
55
|
+
After running, use .zmr/ensure-ios-shim-target.sh to create/update the UI test
|
|
56
|
+
target when --project or --workspace and --app-target are available, then pass
|
|
57
|
+
--ios-shim ./.zmr/ios-shim to zmr.
|
|
58
|
+
USAGE
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
die() {
|
|
62
|
+
echo "error: $*" >&2
|
|
63
|
+
exit 2
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
require_value() {
|
|
67
|
+
local flag="$1"
|
|
68
|
+
local value="${2-}"
|
|
69
|
+
if [[ -z "$value" || "$value" == --* ]]; then
|
|
70
|
+
die "$flag requires a value"
|
|
71
|
+
fi
|
|
72
|
+
printf '%s\n' "$value"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
while [[ $# -gt 0 ]]; do
|
|
76
|
+
case "$1" in
|
|
77
|
+
--app-root)
|
|
78
|
+
APP_ROOT="$(require_value "$1" "${2-}")"
|
|
79
|
+
shift 2
|
|
80
|
+
;;
|
|
81
|
+
--scheme)
|
|
82
|
+
SCHEME="$(require_value "$1" "${2-}")"
|
|
83
|
+
shift 2
|
|
84
|
+
;;
|
|
85
|
+
--bundle-id)
|
|
86
|
+
BUNDLE_ID="$(require_value "$1" "${2-}")"
|
|
87
|
+
shift 2
|
|
88
|
+
;;
|
|
89
|
+
--app-target)
|
|
90
|
+
APP_TARGET="$(require_value "$1" "${2-}")"
|
|
91
|
+
shift 2
|
|
92
|
+
;;
|
|
93
|
+
--test-bundle-id)
|
|
94
|
+
TEST_BUNDLE_ID="$(require_value "$1" "${2-}")"
|
|
95
|
+
shift 2
|
|
96
|
+
;;
|
|
97
|
+
--workspace)
|
|
98
|
+
WORKSPACE="$(require_value "$1" "${2-}")"
|
|
99
|
+
shift 2
|
|
100
|
+
;;
|
|
101
|
+
--project)
|
|
102
|
+
PROJECT="$(require_value "$1" "${2-}")"
|
|
103
|
+
shift 2
|
|
104
|
+
;;
|
|
105
|
+
--derived-data-path)
|
|
106
|
+
DERIVED_DATA_PATH="$(require_value "$1" "${2-}")"
|
|
107
|
+
shift 2
|
|
108
|
+
;;
|
|
109
|
+
--test-target)
|
|
110
|
+
TEST_TARGET="$(require_value "$1" "${2-}")"
|
|
111
|
+
shift 2
|
|
112
|
+
;;
|
|
113
|
+
--device)
|
|
114
|
+
DEVICE="$(require_value "$1" "${2-}")"
|
|
115
|
+
shift 2
|
|
116
|
+
;;
|
|
117
|
+
--device-type)
|
|
118
|
+
DEVICE_TYPE="$(require_value "$1" "${2-}")"
|
|
119
|
+
shift 2
|
|
120
|
+
;;
|
|
121
|
+
--configuration)
|
|
122
|
+
CONFIGURATION="$(require_value "$1" "${2-}")"
|
|
123
|
+
shift 2
|
|
124
|
+
;;
|
|
125
|
+
--deployment-target)
|
|
126
|
+
DEPLOYMENT_TARGET="$(require_value "$1" "${2-}")"
|
|
127
|
+
shift 2
|
|
128
|
+
;;
|
|
129
|
+
--patch-xcodeproj)
|
|
130
|
+
PATCH_XCODEPROJ=1
|
|
131
|
+
shift
|
|
132
|
+
;;
|
|
133
|
+
-h|--help)
|
|
134
|
+
usage
|
|
135
|
+
exit 0
|
|
136
|
+
;;
|
|
137
|
+
*)
|
|
138
|
+
die "unknown argument: $1"
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
done
|
|
142
|
+
|
|
143
|
+
[[ -n "$APP_ROOT" ]] || die "--app-root is required"
|
|
144
|
+
[[ -n "$SCHEME" ]] || die "--scheme is required"
|
|
145
|
+
[[ -n "$BUNDLE_ID" ]] || die "--bundle-id is required"
|
|
146
|
+
if [[ -n "$WORKSPACE" && -n "$PROJECT" ]]; then
|
|
147
|
+
die "--workspace and --project are mutually exclusive"
|
|
148
|
+
fi
|
|
149
|
+
if [[ -z "$TEST_TARGET" ]]; then
|
|
150
|
+
TEST_TARGET="$SCHEME"
|
|
151
|
+
fi
|
|
152
|
+
if [[ -z "$TEST_BUNDLE_ID" ]]; then
|
|
153
|
+
TEST_BUNDLE_ID="$BUNDLE_ID.zmr-uitests"
|
|
154
|
+
fi
|
|
155
|
+
if [[ "$PATCH_XCODEPROJ" -eq 1 ]]; then
|
|
156
|
+
[[ -n "$PROJECT" || -n "$WORKSPACE" ]] || die "--patch-xcodeproj requires --project or --workspace"
|
|
157
|
+
[[ -n "$APP_TARGET" ]] || die "--patch-xcodeproj requires --app-target"
|
|
158
|
+
fi
|
|
159
|
+
if [[ "$DEVICE_TYPE" != "simulator" && "$DEVICE_TYPE" != "physical" ]]; then
|
|
160
|
+
die "--device-type must be simulator or physical"
|
|
161
|
+
fi
|
|
162
|
+
if [[ "$DEVICE_TYPE" == "physical" && "$DEVICE" == "booted" ]]; then
|
|
163
|
+
die "--device-type physical requires --device <physical-device-id>"
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
mkdir -p "$APP_ROOT"
|
|
167
|
+
APP_ROOT="$(cd "$APP_ROOT" && pwd)"
|
|
168
|
+
mkdir -p "$APP_ROOT/.zmr" "$APP_ROOT/.zmr/shims/ios"
|
|
169
|
+
rm -f "$APP_ROOT/.zmr/ios-shim-state/build-for-testing.ready"
|
|
170
|
+
cp "$ROOT/shims/ios/ZMRShim.swift" "$APP_ROOT/.zmr/shims/ios/ZMRShim.swift"
|
|
171
|
+
cp "$ROOT/shims/ios/ZMRShimUITestCase.swift" "$APP_ROOT/.zmr/ZMRShimUITestCase.swift"
|
|
172
|
+
cp "$ROOT/scripts/ensure-ios-shim-target.rb" "$APP_ROOT/.zmr/ensure-ios-shim-target.rb"
|
|
173
|
+
|
|
174
|
+
cat > "$APP_ROOT/.zmr/ZMRShimUITests-Info.plist" <<'EOF'
|
|
175
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
176
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
177
|
+
<plist version="1.0">
|
|
178
|
+
<dict>
|
|
179
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
180
|
+
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
181
|
+
<key>CFBundleExecutable</key>
|
|
182
|
+
<string>$(EXECUTABLE_NAME)</string>
|
|
183
|
+
<key>CFBundleIdentifier</key>
|
|
184
|
+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
185
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
186
|
+
<string>6.0</string>
|
|
187
|
+
<key>CFBundleName</key>
|
|
188
|
+
<string>$(PRODUCT_NAME)</string>
|
|
189
|
+
<key>CFBundlePackageType</key>
|
|
190
|
+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
191
|
+
<key>CFBundleShortVersionString</key>
|
|
192
|
+
<string>1.0</string>
|
|
193
|
+
<key>CFBundleVersion</key>
|
|
194
|
+
<string>1</string>
|
|
195
|
+
<key>ZMR_APP_BUNDLE_ID</key>
|
|
196
|
+
<string>$(ZMR_APP_BUNDLE_ID)</string>
|
|
197
|
+
<key>ZMR_SHIM_REQUEST_FILE</key>
|
|
198
|
+
<string>$(ZMR_SHIM_REQUEST_FILE)</string>
|
|
199
|
+
<key>ZMR_SHIM_RESPONSE_FILE</key>
|
|
200
|
+
<string>$(ZMR_SHIM_RESPONSE_FILE)</string>
|
|
201
|
+
<key>ZMR_SHIM_MODE</key>
|
|
202
|
+
<string>$(ZMR_SHIM_MODE)</string>
|
|
203
|
+
<key>ZMR_SHIM_SERVER_DIR</key>
|
|
204
|
+
<string>$(ZMR_SHIM_SERVER_DIR)</string>
|
|
205
|
+
</dict>
|
|
206
|
+
</plist>
|
|
207
|
+
EOF
|
|
208
|
+
|
|
209
|
+
cat > "$APP_ROOT/.zmr/ios-shim" <<EOF
|
|
210
|
+
#!/usr/bin/env bash
|
|
211
|
+
set -euo pipefail
|
|
212
|
+
|
|
213
|
+
cd "$APP_ROOT"
|
|
214
|
+
|
|
215
|
+
STATE_DIR="$APP_ROOT/.zmr/ios-shim-state"
|
|
216
|
+
SERVER_DIR="\$STATE_DIR/server"
|
|
217
|
+
PID_FILE="\$STATE_DIR/xcodebuild.pid"
|
|
218
|
+
READY_FILE="\$SERVER_DIR/ready"
|
|
219
|
+
BUILD_READY_FILE="\$STATE_DIR/build-for-testing.ready"
|
|
220
|
+
LOG_FILE="\$STATE_DIR/xcodebuild.log"
|
|
221
|
+
STDIN_FILE="\$(mktemp)"
|
|
222
|
+
trap 'rm -f "\$STDIN_FILE"' EXIT
|
|
223
|
+
|
|
224
|
+
mkdir -p "\$STATE_DIR" "\$SERVER_DIR"
|
|
225
|
+
|
|
226
|
+
cat > "\$STDIN_FILE"
|
|
227
|
+
|
|
228
|
+
XCODEBUILD_ARGS=()
|
|
229
|
+
if [[ -n "$WORKSPACE" ]]; then
|
|
230
|
+
XCODEBUILD_ARGS+=(-workspace "$WORKSPACE")
|
|
231
|
+
elif [[ -n "$PROJECT" ]]; then
|
|
232
|
+
XCODEBUILD_ARGS+=(-project "$PROJECT")
|
|
233
|
+
fi
|
|
234
|
+
if [[ -n "$DERIVED_DATA_PATH" ]]; then
|
|
235
|
+
XCODEBUILD_ARGS+=(-derivedDataPath "$DERIVED_DATA_PATH")
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
tail_log() {
|
|
239
|
+
if [[ -f "\$LOG_FILE" ]]; then
|
|
240
|
+
tail -120 "\$LOG_FILE" >&2
|
|
241
|
+
fi
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
resolve_destination() {
|
|
245
|
+
local destination_id="$DEVICE"
|
|
246
|
+
if [[ "\$destination_id" == "booted" ]]; then
|
|
247
|
+
if [[ "$DEVICE_TYPE" == "physical" ]]; then
|
|
248
|
+
echo "physical iOS shim requires an explicit --device id" >&2
|
|
249
|
+
exit 2
|
|
250
|
+
fi
|
|
251
|
+
destination_id="\$(xcrun simctl list devices booted | sed -n 's/.*(\([0-9A-Fa-f-][0-9A-Fa-f-]*\)) (Booted).*/\1/p' | head -n 1)"
|
|
252
|
+
fi
|
|
253
|
+
if [[ -z "\$destination_id" ]]; then
|
|
254
|
+
echo "no booted iOS simulator found" >&2
|
|
255
|
+
exit 2
|
|
256
|
+
fi
|
|
257
|
+
printf '%s' "\$destination_id"
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
destination_spec() {
|
|
261
|
+
local destination_id platform_name
|
|
262
|
+
destination_id="\$(resolve_destination)"
|
|
263
|
+
if [[ "$DEVICE_TYPE" == "physical" ]]; then
|
|
264
|
+
platform_name="iOS"
|
|
265
|
+
else
|
|
266
|
+
platform_name="iOS Simulator"
|
|
267
|
+
fi
|
|
268
|
+
printf 'platform=%s,id=%s' "\$platform_name" "\$destination_id"
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
is_server_running() {
|
|
272
|
+
if [[ ! -f "\$PID_FILE" ]]; then
|
|
273
|
+
return 1
|
|
274
|
+
fi
|
|
275
|
+
local pid
|
|
276
|
+
pid="\$(cat "\$PID_FILE" 2>/dev/null || true)"
|
|
277
|
+
[[ -n "\$pid" ]] && kill -0 "\$pid" 2>/dev/null
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
run_oneshot() {
|
|
281
|
+
local request_file response_file oneshot_log destination_id
|
|
282
|
+
request_file="\$(mktemp "\$STATE_DIR/request.XXXXXX")"
|
|
283
|
+
response_file="\$(mktemp "\$STATE_DIR/response.XXXXXX")"
|
|
284
|
+
oneshot_log="\$(mktemp "\$STATE_DIR/xcodebuild.oneshot.XXXXXX.log")"
|
|
285
|
+
cp "\$STDIN_FILE" "\$request_file"
|
|
286
|
+
destination_id="\$(destination_spec)"
|
|
287
|
+
|
|
288
|
+
if ! xcodebuild test \\
|
|
289
|
+
"\${XCODEBUILD_ARGS[@]}" \\
|
|
290
|
+
-scheme "$SCHEME" \\
|
|
291
|
+
-configuration "$CONFIGURATION" \\
|
|
292
|
+
-destination "\$destination_id" \\
|
|
293
|
+
-only-testing:"$TEST_TARGET/ZMRShimUITestCase/testRunZMRCommand" \\
|
|
294
|
+
ZMR_SHIM_MODE="oneshot" \\
|
|
295
|
+
ZMR_SHIM_REQUEST_FILE="\$request_file" \\
|
|
296
|
+
ZMR_SHIM_RESPONSE_FILE="\$response_file" \\
|
|
297
|
+
ZMR_APP_BUNDLE_ID="$BUNDLE_ID" \\
|
|
298
|
+
>"\$oneshot_log" 2>&1; then
|
|
299
|
+
tail -120 "\$oneshot_log" >&2
|
|
300
|
+
exit 1
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
cat "\$response_file"
|
|
304
|
+
printf '\\n'
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
wait_for_ready() {
|
|
308
|
+
local deadline
|
|
309
|
+
deadline=\$((SECONDS + \${ZMR_IOS_SHIM_START_TIMEOUT_SECONDS:-180}))
|
|
310
|
+
while (( SECONDS < deadline )); do
|
|
311
|
+
if [[ -f "\$READY_FILE" ]]; then
|
|
312
|
+
return 0
|
|
313
|
+
fi
|
|
314
|
+
if ! is_server_running; then
|
|
315
|
+
echo "iOS shim server exited before it became ready" >&2
|
|
316
|
+
tail_log
|
|
317
|
+
exit 1
|
|
318
|
+
fi
|
|
319
|
+
sleep 0.2
|
|
320
|
+
done
|
|
321
|
+
echo "timed out waiting for iOS shim server readiness" >&2
|
|
322
|
+
tail_log
|
|
323
|
+
exit 1
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
build_for_testing() {
|
|
327
|
+
if [[ "\${ZMR_IOS_SHIM_FORCE_REBUILD:-}" != "1" && -f "\$BUILD_READY_FILE" ]]; then
|
|
328
|
+
return 0
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
local destination_id build_log
|
|
332
|
+
destination_id="\$(destination_spec)"
|
|
333
|
+
build_log="\$STATE_DIR/xcodebuild.build.log"
|
|
334
|
+
|
|
335
|
+
if ! xcodebuild build-for-testing \\
|
|
336
|
+
"\${XCODEBUILD_ARGS[@]}" \\
|
|
337
|
+
-scheme "$SCHEME" \\
|
|
338
|
+
-configuration "$CONFIGURATION" \\
|
|
339
|
+
-destination "\$destination_id" \\
|
|
340
|
+
ZMR_SHIM_MODE="server" \\
|
|
341
|
+
ZMR_SHIM_SERVER_DIR="\$SERVER_DIR" \\
|
|
342
|
+
ZMR_APP_BUNDLE_ID="$BUNDLE_ID" \\
|
|
343
|
+
>"\$build_log" 2>&1; then
|
|
344
|
+
tail -120 "\$build_log" >&2
|
|
345
|
+
exit 1
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
touch "\$BUILD_READY_FILE"
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
start_server() {
|
|
352
|
+
if is_server_running; then
|
|
353
|
+
wait_for_ready
|
|
354
|
+
return 0
|
|
355
|
+
fi
|
|
356
|
+
|
|
357
|
+
rm -f "\$READY_FILE" "\$SERVER_DIR"/request-*.json "\$SERVER_DIR"/response-*.json "\$SERVER_DIR/stop"
|
|
358
|
+
: > "\$LOG_FILE"
|
|
359
|
+
build_for_testing
|
|
360
|
+
|
|
361
|
+
local destination_id
|
|
362
|
+
destination_id="\$(destination_spec)"
|
|
363
|
+
nohup xcodebuild test-without-building \\
|
|
364
|
+
"\${XCODEBUILD_ARGS[@]}" \\
|
|
365
|
+
-scheme "$SCHEME" \\
|
|
366
|
+
-configuration "$CONFIGURATION" \\
|
|
367
|
+
-destination "\$destination_id" \\
|
|
368
|
+
-only-testing:"$TEST_TARGET/ZMRShimUITestCase/testRunZMRCommand" \\
|
|
369
|
+
ZMR_SHIM_MODE="server" \\
|
|
370
|
+
ZMR_SHIM_SERVER_DIR="\$SERVER_DIR" \\
|
|
371
|
+
ZMR_APP_BUNDLE_ID="$BUNDLE_ID" \\
|
|
372
|
+
>"\$LOG_FILE" 2>&1 < /dev/null &
|
|
373
|
+
|
|
374
|
+
echo "\$!" > "\$PID_FILE"
|
|
375
|
+
wait_for_ready
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
send_request() {
|
|
379
|
+
start_server
|
|
380
|
+
|
|
381
|
+
local REQUEST_ID request_file response_file tmp_request deadline
|
|
382
|
+
REQUEST_ID="\$(date +%s%N)-\$\$"
|
|
383
|
+
request_file="\$SERVER_DIR/request-\$REQUEST_ID.json"
|
|
384
|
+
response_file="\$SERVER_DIR/response-\$REQUEST_ID.json"
|
|
385
|
+
tmp_request="\$request_file.tmp"
|
|
386
|
+
cp "\$STDIN_FILE" "\$tmp_request"
|
|
387
|
+
mv "\$tmp_request" "\$request_file"
|
|
388
|
+
|
|
389
|
+
deadline=\$((SECONDS + \${ZMR_IOS_SHIM_RESPONSE_TIMEOUT_SECONDS:-180}))
|
|
390
|
+
while (( SECONDS < deadline )); do
|
|
391
|
+
if [[ -f "\$response_file" ]]; then
|
|
392
|
+
cat "\$response_file"
|
|
393
|
+
printf '\\n'
|
|
394
|
+
rm -f "\$response_file"
|
|
395
|
+
return 0
|
|
396
|
+
fi
|
|
397
|
+
if ! is_server_running; then
|
|
398
|
+
echo "iOS shim server exited while waiting for response \$REQUEST_ID" >&2
|
|
399
|
+
tail_log
|
|
400
|
+
exit 1
|
|
401
|
+
fi
|
|
402
|
+
sleep 0.05
|
|
403
|
+
done
|
|
404
|
+
|
|
405
|
+
echo "timed out waiting for iOS shim response \$REQUEST_ID" >&2
|
|
406
|
+
tail_log
|
|
407
|
+
exit 1
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if [[ "\${ZMR_IOS_SHIM_ONESHOT:-}" == "1" ]]; then
|
|
411
|
+
run_oneshot
|
|
412
|
+
else
|
|
413
|
+
send_request
|
|
414
|
+
fi
|
|
415
|
+
EOF
|
|
416
|
+
|
|
417
|
+
chmod +x "$APP_ROOT/.zmr/ios-shim"
|
|
418
|
+
|
|
419
|
+
shell_quote() {
|
|
420
|
+
printf '%q' "$1"
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
patch_zmr_config() {
|
|
424
|
+
local config_file="$APP_ROOT/.zmr/config.json"
|
|
425
|
+
ruby -rjson -e '
|
|
426
|
+
path = ARGV.fetch(0)
|
|
427
|
+
app_id = ARGV.fetch(1)
|
|
428
|
+
config = if File.exist?(path)
|
|
429
|
+
JSON.parse(File.read(path))
|
|
430
|
+
else
|
|
431
|
+
{ "schemaVersion" => 1, "appId" => app_id }
|
|
432
|
+
end
|
|
433
|
+
abort "error: .zmr/config.json must be a JSON object" unless config.is_a?(Hash)
|
|
434
|
+
config["schemaVersion"] ||= 1
|
|
435
|
+
config["appId"] ||= app_id unless app_id.empty?
|
|
436
|
+
tools = config["tools"]
|
|
437
|
+
abort "error: .zmr/config.json tools must be a JSON object" if tools && !tools.is_a?(Hash)
|
|
438
|
+
tools ||= {}
|
|
439
|
+
tools["iosShimPath"] = "./.zmr/ios-shim"
|
|
440
|
+
config["tools"] = tools
|
|
441
|
+
File.write(path, "#{JSON.pretty_generate(config)}\n")
|
|
442
|
+
' "$config_file" "$BUNDLE_ID"
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
patch_zmr_config
|
|
446
|
+
|
|
447
|
+
cat > "$APP_ROOT/.zmr/ensure-ios-shim-target.sh" <<EOF
|
|
448
|
+
#!/usr/bin/env bash
|
|
449
|
+
set -euo pipefail
|
|
450
|
+
|
|
451
|
+
APP_ROOT=$(shell_quote "$APP_ROOT")
|
|
452
|
+
PROJECT=$(shell_quote "$PROJECT")
|
|
453
|
+
WORKSPACE=$(shell_quote "$WORKSPACE")
|
|
454
|
+
APP_TARGET=$(shell_quote "$APP_TARGET")
|
|
455
|
+
TEST_TARGET=$(shell_quote "$TEST_TARGET")
|
|
456
|
+
SCHEME=$(shell_quote "$SCHEME")
|
|
457
|
+
BUNDLE_ID=$(shell_quote "$BUNDLE_ID")
|
|
458
|
+
TEST_BUNDLE_ID=$(shell_quote "$TEST_BUNDLE_ID")
|
|
459
|
+
DEPLOYMENT_TARGET=$(shell_quote "$DEPLOYMENT_TARGET")
|
|
460
|
+
|
|
461
|
+
cd "\$APP_ROOT"
|
|
462
|
+
|
|
463
|
+
if [[ -z "\$APP_TARGET" ]]; then
|
|
464
|
+
echo "error: rerun install-ios-shim.sh with --app-target to enable Xcode target patching" >&2
|
|
465
|
+
exit 2
|
|
466
|
+
fi
|
|
467
|
+
|
|
468
|
+
if [[ -z "\$PROJECT" ]]; then
|
|
469
|
+
if [[ -z "\$WORKSPACE" ]]; then
|
|
470
|
+
echo "error: rerun install-ios-shim.sh with --project or --workspace to enable Xcode target patching" >&2
|
|
471
|
+
exit 2
|
|
472
|
+
fi
|
|
473
|
+
PROJECT="\$(ruby -rrexml/document -rpathname -e '
|
|
474
|
+
app_root = Pathname.new(ARGV[0]).expand_path
|
|
475
|
+
workspace = app_root.join(ARGV[1]).expand_path
|
|
476
|
+
app_target = ARGV[2].to_s
|
|
477
|
+
bundle_id = ARGV[3].to_s
|
|
478
|
+
data = workspace.join("contents.xcworkspacedata")
|
|
479
|
+
abort "error: workspace metadata not found at #{data}" unless data.file?
|
|
480
|
+
doc = REXML::Document.new(data.read)
|
|
481
|
+
projects = []
|
|
482
|
+
doc.elements.each("//FileRef") do |element|
|
|
483
|
+
location = element.attributes["location"].to_s
|
|
484
|
+
next unless location.end_with?(".xcodeproj")
|
|
485
|
+
project_path = location.sub(/\A(?:group|container|self):/, "")
|
|
486
|
+
project = Pathname.new(project_path)
|
|
487
|
+
project = workspace.dirname.join(project).expand_path unless project.absolute?
|
|
488
|
+
projects << project
|
|
489
|
+
end
|
|
490
|
+
projects.uniq!
|
|
491
|
+
abort "error: workspace #{ARGV[1]} does not reference an .xcodeproj; rerun with --project" if projects.empty?
|
|
492
|
+
if projects.length > 1
|
|
493
|
+
begin
|
|
494
|
+
require "xcodeproj"
|
|
495
|
+
rescue LoadError
|
|
496
|
+
abort "error: workspace #{ARGV[1]} references multiple .xcodeproj files; install the xcodeproj gem or rerun with --project"
|
|
497
|
+
end
|
|
498
|
+
projects = projects.select do |project_path|
|
|
499
|
+
begin
|
|
500
|
+
Xcodeproj::Project.open(project_path.to_s).targets.any? { |target| target.name == app_target }
|
|
501
|
+
rescue StandardError
|
|
502
|
+
false
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
abort "error: workspace #{ARGV[1]} references multiple .xcodeproj files, and none contain target #{app_target}; rerun with --project" if projects.empty?
|
|
506
|
+
if projects.length > 1 && !bundle_id.empty?
|
|
507
|
+
bundle_matches = projects.select do |project_path|
|
|
508
|
+
begin
|
|
509
|
+
Xcodeproj::Project.open(project_path.to_s).targets.any? do |target|
|
|
510
|
+
target.name == app_target &&
|
|
511
|
+
target.build_configurations.any? do |configuration|
|
|
512
|
+
configuration.build_settings["PRODUCT_BUNDLE_IDENTIFIER"].to_s == bundle_id
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
rescue StandardError
|
|
516
|
+
false
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
projects = bundle_matches unless bundle_matches.empty?
|
|
520
|
+
end
|
|
521
|
+
abort "error: workspace #{ARGV[1]} references multiple .xcodeproj files containing target #{app_target}; bundle id #{bundle_id} did not disambiguate; rerun with --project" if projects.length > 1
|
|
522
|
+
end
|
|
523
|
+
puts projects.first.relative_path_from(app_root).to_s
|
|
524
|
+
' "\$APP_ROOT" "\$WORKSPACE" "\$APP_TARGET" "\$BUNDLE_ID")"
|
|
525
|
+
fi
|
|
526
|
+
|
|
527
|
+
ruby "\$APP_ROOT/.zmr/ensure-ios-shim-target.rb" \\
|
|
528
|
+
--project "\$PROJECT" \\
|
|
529
|
+
--app-target "\$APP_TARGET" \\
|
|
530
|
+
--test-target "\$TEST_TARGET" \\
|
|
531
|
+
--scheme "\$SCHEME" \\
|
|
532
|
+
--bundle-id "\$BUNDLE_ID" \\
|
|
533
|
+
--test-bundle-id "\$TEST_BUNDLE_ID" \\
|
|
534
|
+
--deployment-target "\$DEPLOYMENT_TARGET" \\
|
|
535
|
+
--source ".zmr/ZMRShimUITestCase.swift" \\
|
|
536
|
+
--source ".zmr/shims/ios/ZMRShim.swift" \\
|
|
537
|
+
--info-plist ".zmr/ZMRShimUITests-Info.plist"
|
|
538
|
+
EOF
|
|
539
|
+
|
|
540
|
+
chmod +x "$APP_ROOT/.zmr/ensure-ios-shim-target.sh"
|
|
541
|
+
|
|
542
|
+
if [[ "$PATCH_XCODEPROJ" -eq 1 ]]; then
|
|
543
|
+
"$APP_ROOT/.zmr/ensure-ios-shim-target.sh"
|
|
544
|
+
fi
|
|
545
|
+
|
|
546
|
+
cat > "$APP_ROOT/.zmr/ios-shim.README.md" <<EOF
|
|
547
|
+
# ZMR iOS Shim
|
|
548
|
+
|
|
549
|
+
Generated for scheme \`$SCHEME\`, UI test target \`$TEST_TARGET\`, and bundle id \`$BUNDLE_ID\`.
|
|
550
|
+
|
|
551
|
+
When the app has an Xcode project or workspace, run:
|
|
552
|
+
|
|
553
|
+
\`\`\`bash
|
|
554
|
+
.zmr/ensure-ios-shim-target.sh
|
|
555
|
+
\`\`\`
|
|
556
|
+
|
|
557
|
+
The helper uses the Ruby \`xcodeproj\` gem to create/update the UI test target,
|
|
558
|
+
add the shim sources, set the test bundle Info.plist, and write a shared scheme.
|
|
559
|
+
For workspaces, it resolves the project automatically when there is one project,
|
|
560
|
+
or when exactly one project contains \`--app-target\`, or when \`--bundle-id\`
|
|
561
|
+
disambiguates matching app targets. Rerun the installer with \`--project\` for
|
|
562
|
+
still-ambiguous workspaces. Install the gem with
|
|
563
|
+
\`gem install xcodeproj\` or your app's Bundler setup.
|
|
564
|
+
|
|
565
|
+
If you do not want ZMR to patch the Xcode project, add these files to the app's
|
|
566
|
+
UI test target manually:
|
|
567
|
+
|
|
568
|
+
- \`.zmr/ZMRShimUITestCase.swift\`
|
|
569
|
+
- \`.zmr/shims/ios/ZMRShim.swift\`
|
|
570
|
+
- \`.zmr/ZMRShimUITests-Info.plist\`
|
|
571
|
+
|
|
572
|
+
Run ZMR with:
|
|
573
|
+
|
|
574
|
+
\`\`\`bash
|
|
575
|
+
zmr run .zmr/ios-smoke.json --platform ios --ios-shim ./.zmr/ios-shim
|
|
576
|
+
\`\`\`
|
|
577
|
+
|
|
578
|
+
The command caches \`build-for-testing\` output under \`.zmr/ios-shim-state/\`
|
|
579
|
+
and uses \`test-without-building\` for selector commands. Set
|
|
580
|
+
\`ZMR_IOS_SHIM_FORCE_REBUILD=1\` after app-side target changes, or
|
|
581
|
+
\`ZMR_IOS_SHIM_ONESHOT=1\` to force a cold XCTest run for debugging Xcode target
|
|
582
|
+
wiring.
|
|
583
|
+
EOF
|
|
584
|
+
|
|
585
|
+
echo "wrote $APP_ROOT/.zmr/ios-shim"
|
|
586
|
+
echo "wrote $APP_ROOT/.zmr/ZMRShimUITestCase.swift"
|
|
587
|
+
echo "wrote $APP_ROOT/.zmr/shims/ios/ZMRShim.swift"
|
|
588
|
+
echo "wrote $APP_ROOT/.zmr/ZMRShimUITests-Info.plist"
|
|
589
|
+
echo "wrote $APP_ROOT/.zmr/ensure-ios-shim-target.sh"
|