-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathxcode-test
More file actions
executable file
·208 lines (178 loc) · 5.81 KB
/
xcode-test
File metadata and controls
executable file
·208 lines (178 loc) · 5.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/bin/bash
# xcode-test: Run Xcode tests with detailed failure reporting
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
show_help() {
cat <<EOF
Usage: xcode-test <scheme> [options] [-- extra xcodebuild flags]
Run Xcode tests with concise, detailed failure reporting.
Arguments:
scheme The Xcode scheme to test (required)
Options:
-h, --help Show this help message
-destination DEST Simulator destination for iOS (auto-selected if not provided)
--result-path PATH Path for .xcresult bundle (default: \$TMPDIR/<scheme>Test.xcresult)
Examples:
xcode-test MyMacApp
xcode-test MyApp -destination "platform=iOS Simulator,name=iPhone 17"
xcode-test MyApp --result-path ./results.xcresult
xcode-test MyApp -- -only-testing:MyAppTests/SomeTest
EOF
}
# Parse options
RESULT_PATH=""
DESTINATION=""
EXTRA_ARGS=()
POSITIONAL=()
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
show_help
exit 0
;;
-destination)
DESTINATION="$2"
shift 2
;;
--result-path)
RESULT_PATH="$2"
shift 2
;;
--)
shift
EXTRA_ARGS=("$@")
break
;;
-*)
echo "Unknown option: $1" >&2
echo "Use --help for usage information" >&2
exit 1
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
# Source common functions
# shellcheck source=lib/common.sh
source "$SCRIPT_DIR/lib/common.sh"
# Validate required arguments
if [[ ${#POSITIONAL[@]} -lt 1 ]]; then
echo "Error: scheme is required" >&2
echo "Use --help for usage information" >&2
exit 1
fi
# Check dependencies
check_dependencies
SCHEME="${POSITIONAL[0]}"
# Set result path if not specified
if [[ -z "$RESULT_PATH" ]]; then
RESULT_PATH="${TMPDIR:-/tmp}/${SCHEME}Test.xcresult"
fi
# Auto-select iOS simulator if destination not provided and scheme is iOS-only
if [[ -z "$DESTINATION" ]]; then
DESTINATIONS=$(xcodebuild -showdestinations -scheme "$SCHEME" 2>/dev/null || true)
# If destinations include "iOS Simulator" but no "macOS", it's an iOS project
if echo "$DESTINATIONS" | grep -q "iOS Simulator" && ! echo "$DESTINATIONS" | grep -q "platform:macOS"; then
DESTINATION=$(get_ios_simulator)
fi
fi
# Build destination flag if set
DEST_ARGS=()
if [[ -n "$DESTINATION" ]]; then
DEST_ARGS=(-destination "$DESTINATION")
fi
# Retry with database lock handling and whole-module fallback
# Sets TEST_OUTPUT and exits 0 on success, returns 1 on failure
retry_with_fallback() {
MAX_LOCK_RETRIES=5
lock_retries=0
while check_database_lock "$TEST_OUTPUT"; do
lock_retries=$((lock_retries + 1))
if [[ $lock_retries -gt $MAX_LOCK_RETRIES ]]; then
echo "✗ Build database still locked after $MAX_LOCK_RETRIES retries."
break
fi
echo "✗ Build database locked. Retrying ($lock_retries/$MAX_LOCK_RETRIES)..."
sleep 1
rm -rf "$RESULT_PATH"
TEST_OUTPUT=$(xcodebuild test -scheme "$SCHEME" ${DEST_ARGS[@]+"${DEST_ARGS[@]}"} \
-resultBundlePath "$RESULT_PATH" "${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}" 2>&1) || true
if echo "$TEST_OUTPUT" | grep -q "TEST SUCCEEDED"; then
echo "✓ Tests passed (after retry)"
exit 0
fi
if echo "$TEST_OUTPUT" | grep -q "BUILD SUCCEEDED"; then
echo "✓ Build succeeded (after retry)"
exit 0
fi
done
# No errors found - rebuild with whole-module mode
echo "✗ Build failed with no error details. Rebuilding with whole-module mode..."
echo ""
rm -rf "$RESULT_PATH"
TEST_OUTPUT=$(xcodebuild test -scheme "$SCHEME" ${DEST_ARGS[@]+"${DEST_ARGS[@]}"} \
SWIFT_COMPILATION_MODE=wholemodule \
-resultBundlePath "$RESULT_PATH" "${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}" 2>&1) || true
if echo "$TEST_OUTPUT" | grep -q "TEST SUCCEEDED"; then
echo "✓ Tests passed (after retry)"
exit 0
fi
if echo "$TEST_OUTPUT" | grep -q "BUILD SUCCEEDED"; then
echo "✓ Build succeeded (after retry)"
exit 0
fi
ERRORS=$(extract_errors "$TEST_OUTPUT")
if [[ -n "$ERRORS" ]]; then
echo "$ERRORS"
else
show_fallback_errors "$TEST_OUTPUT"
fi
return 1
}
# Remove existing result bundle
rm -rf "$RESULT_PATH"
# Run tests
TEST_OUTPUT=$(xcodebuild test -scheme "$SCHEME" ${DEST_ARGS[@]+"${DEST_ARGS[@]}"} \
-resultBundlePath "$RESULT_PATH" \
"${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}" 2>&1) || true
if echo "$TEST_OUTPUT" | grep -q "TEST SUCCEEDED"; then
echo "✓ Tests passed"
elif echo "$TEST_OUTPUT" | grep -q "TEST FAILED"; then
# Check for build errors first
BUILD_ERRORS=$(extract_errors "$TEST_OUTPUT")
if [[ -n "$BUILD_ERRORS" ]]; then
echo "✗ Build failed:"
echo ""
echo "$BUILD_ERRORS"
exit 1
fi
# Check for test failures in result bundle
TEST_FAILURES=$(xcrun xcresulttool get test-results tests --path "$RESULT_PATH" 2>/dev/null | jq -r '
.. |
objects |
select(.result == "Failed" and .nodeIdentifier != null and (.children | length > 0)) |
.nodeIdentifier as $test |
"\($test):\n" + ([.children[]? | select(.nodeType == "Failure Message") | .name] | join("\n")) + "\n"
' || true)
if [[ -n "$TEST_FAILURES" ]]; then
echo "✗ Tests failed:"
echo ""
echo "$TEST_FAILURES"
exit 1
fi
retry_with_fallback
exit 1
else
# Build error or other failure
ERRORS=$(extract_errors "$TEST_OUTPUT")
if [[ -n "$ERRORS" ]]; then
echo "✗ Build failed:"
echo ""
echo "$ERRORS"
else
retry_with_fallback
fi
exit 1
fi