3030 run : |
3131 echo "Downloaded build contents:"
3232 ls -lah sample-build/
33- echo "Using x64 APK for emulator testing"
33+ echo "Using APK for SauceLabs real device testing"
3434
3535 - name : Install Pester
3636 shell : pwsh
@@ -43,197 +43,31 @@ jobs:
4343 mkdir build
4444 cmake -B build -S .
4545
46- - name : Upload APK to SauceLabs Storage
47- id : upload_apk
48- run : |
49- sudo apt-get install -y jq
50-
51- APK=$(ls sample-build/*.apk | head -n1)
52- echo "Uploading: $APK"
53-
54- RESPONSE=$(curl -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
55- -X POST "https://api.${SAUCE_REGION}.saucelabs.com/v1/storage/upload" \
56- -F "payload=@${APK}" \
57- -F "name=$(basename "$APK")")
58-
59- echo "Upload Response: $RESPONSE"
60- STORAGE_ID=$(echo "$RESPONSE" | jq -r '.item.id')
61-
62- echo "storage_id=$STORAGE_ID" >> $GITHUB_OUTPUT
63-
64- - name : Start UE5 test on real device
65- id : start_session
46+ - name : Run integration tests
47+ id : run-integration-tests
48+ shell : pwsh
49+ env :
50+ SENTRY_UNREAL_TEST_DSN : ${{ secrets.SENTRY_UNREAL_TEST_DSN }}
51+ SENTRY_AUTH_TOKEN : ${{ secrets.SENTRY_API_TOKEN }}
52+ UNREAL_VERSION : ${{ inputs.unreal-version }}
6653 run : |
67- REQUEST=$(cat <<EOF
68- {
69- "capabilities": {
70- "alwaysMatch": {
71- "platformName": "Android",
72- "appium:app": "storage:${{ steps.upload_apk.outputs.storage_id }}",
73- "appium:deviceName": "Samsung_Galaxy_S23_FE_free",
74- "appium:automationName": "UiAutomator2",
75- "appium:noReset": true,
76- "appium:autoLaunch": false,
77- "sauce:options": {
78- "name": "${{ inputs.unreal-version }} Android Integration Test",
79- "appiumVersion": "latest"
80- }
81- }
82- }
54+ # Find the APK file
55+ $apkPath = Get-ChildItem -Path "${{ github.workspace }}/sample-build" -Filter "*.apk" | Select-Object -First 1
56+ if (-not $apkPath) {
57+ throw "No APK file found in sample-build directory"
8358 }
84- EOF
85- )
86-
87- echo "Sending Appium session request..."
88- echo "$REQUEST"
89-
90- RESPONSE=$(curl -sS -w "\nHTTP_CODE:%{http_code}" \
91- -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
92- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session" \
93- -H "Content-Type: application/json" \
94- -d "$REQUEST")
95-
96- echo "Session Response: $RESPONSE"
97-
98- SESSION_ID=$(echo "$RESPONSE" | grep -v "HTTP_CODE" | jq -r '.value.sessionId // .sessionId')
99- echo "Extracted session_id: $SESSION_ID"
100- echo "session_id=$SESSION_ID" >> $GITHUB_OUTPUT
101-
102- - name : First launch (crash capture)
103- run : |
104- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
105-
106- RESPONSE=$(curl -sSL -w "\nHTTP:%{http_code}" \
107- -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
108- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID/appium/device/start_activity" \
109- -H "Content-Type: application/json" \
110- -d '{
111- "appPackage": "io.sentry.unreal.sample",
112- "appActivity": "com.epicgames.unreal.GameActivity",
113- "appWaitActivity": "*",
114- "intentAction": "android.intent.action.MAIN",
115- "intentCategory": "android.intent.category.LAUNCHER",
116- "optionalIntentArguments": "-e cmdline '-crash-capture'"
117- }')
118- echo "Launch response: $RESPONSE"
119-
120- - name : Wait for app to finish (first run)
121- run : |
122- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
123-
124- echo "Waiting for app to finish..."
125- MAX_WAIT=20 # Max wait time in seconds
126- POLL_INTERVAL=2
127- ELAPSED=0
128-
129- while [ $ELAPSED -lt $MAX_WAIT ]; do
130- STATE=$(curl -sSL \
131- -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
132- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID/execute/sync" \
133- -H "Content-Type: application/json" \
134- -d '{"script": "mobile: queryAppState", "args": [{"appId": "io.sentry.unreal.sample"}]}' \
135- | jq -r '.value')
136-
137- echo "App state: $STATE (elapsed: ${ELAPSED}s)"
138-
139- # State 1 = not running, 0 = not installed
140- if [ "$STATE" = "1" ] || [ "$STATE" = "0" ]; then
141- echo "App finished/crashed"
142- break
143- fi
144-
145- sleep $POLL_INTERVAL
146- ELAPSED=$((ELAPSED + POLL_INTERVAL))
147- done
148-
149- if [ $ELAPSED -ge $MAX_WAIT ]; then
150- echo "Timeout waiting for app to finish"
151- fi
152-
153- - name : Get first run logs
154- run : |
155- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
156- curl -sS -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
157- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID/log" \
158- -H "Content-Type: application/json" \
159- -d '{"type": "logcat"}' \
160- -o first_run_logs.json
161-
162- - name : Second launch (message capture)
163- run : |
164- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
165-
166- RESPONSE=$(curl -sSL -w "\nHTTP:%{http_code}" \
167- -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
168- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID/appium/device/start_activity" \
169- -H "Content-Type: application/json" \
170- -d '{
171- "appPackage": "io.sentry.unreal.sample",
172- "appActivity": "com.epicgames.unreal.GameActivity",
173- "appWaitActivity": "*",
174- "intentAction": "android.intent.action.MAIN",
175- "intentCategory": "android.intent.category.LAUNCHER",
176- "optionalIntentArguments": "-e cmdline '-message-capture'"
177- }')
178- echo "Launch response: $RESPONSE"
179-
180- - name : Wait for app to finish (second run)
181- run : |
182- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
183-
184- echo "Waiting for app to finish..."
185- MAX_WAIT=20 # Max wait time in seconds
186- POLL_INTERVAL=2
187- ELAPSED=0
188-
189- while [ $ELAPSED -lt $MAX_WAIT ]; do
190- STATE=$(curl -sSL \
191- -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
192- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID/execute/sync" \
193- -H "Content-Type: application/json" \
194- -d '{"script": "mobile: queryAppState", "args": [{"appId": "io.sentry.unreal.sample"}]}' \
195- | jq -r '.value')
196-
197- echo "App state: $STATE (elapsed: ${ELAPSED}s)"
198-
199- # State 1 = not running, 0 = not installed
200- if [ "$STATE" = "1" ] || [ "$STATE" = "0" ]; then
201- echo "App finished/crashed"
202- break
203- fi
204-
205- sleep $POLL_INTERVAL
206- ELAPSED=$((ELAPSED + POLL_INTERVAL))
207- done
208-
209- if [ $ELAPSED -ge $MAX_WAIT ]; then
210- echo "Timeout waiting for app to finish"
211- fi
21259
213- - name : Get second run logs
214- run : |
215- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
216- curl -sS -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
217- -X POST "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID/log" \
218- -H "Content-Type: application/json" \
219- -d '{"type": "logcat"}' \
220- -o second_run_logs.json
60+ Write-Host "Found APK: $($apkPath.Name)"
61+ $env:SENTRY_UNREAL_TEST_APP_PATH = $apkPath.FullName
22162
222- - name : End session
223- if : always()
224- run : |
225- SESSION_ID="${{ steps.start_session.outputs.session_id }}"
226- if [ -n "$SESSION_ID" ] && [ "$SESSION_ID" != "null" ]; then
227- curl -sS -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \
228- -X DELETE "https://ondemand.eu-central-1.saucelabs.com/wd/hub/session/$SESSION_ID"
229- fi
63+ cd integration-test
64+ Invoke-Pester Integration.Tests.Android.SauceLabs.ps1 -CI
23065
23166 - name : Upload integration test output
23267 if : ${{ always() }}
23368 uses : actions/upload-artifact@v4
23469 with :
235- name : UE ${{ inputs.unreal-version }} device logs (Android)
70+ name : UE ${{ inputs.unreal-version }} integration test output (Android)
23671 path : |
237- first_run_logs.json
238- second_run_logs.json
72+ integration-test/output/
23973 retention-days : 14
0 commit comments