Skip to content

Commit c9079bc

Browse files
committed
Test SauceLabs
Fix artifact download Shell Fix Fix Fix Fix Set config Fix syntax Sauce skip run Update appium Test test test apk name Test Test Test Test Test Test Test Test Test Test Test tets Test Test Poll session status Test Test Test 2 runs Test Test Try fix redirect Test Test fix id Test app status poll Test Clean up
1 parent 281af4a commit c9079bc

File tree

2 files changed

+200
-47
lines changed

2 files changed

+200
-47
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,13 @@ jobs:
200200
unreal-version: ${{ matrix.unreal }}
201201

202202
integration-test-android:
203-
needs: [test-android]
204203
name: Android UE ${{ matrix.unreal }}
205204
secrets: inherit
206205
strategy:
207206
fail-fast: false
208207
matrix:
209208
# Starting with UE 5.4-5.6 for faster iteration
210-
unreal: ['5.4', '5.5', '5.6']
209+
unreal: ['5.4']
211210
uses: ./.github/workflows/integration-test-android.yml
212211
with:
213212
unreal-version: ${{ matrix.unreal }}

.github/workflows/integration-test-android.yml

Lines changed: 199 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ jobs:
1010
name: Integration Test
1111
runs-on: ubuntu-latest
1212

13+
env:
14+
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
15+
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
16+
GITHUB_TOKEN: ${{ github.token }}
17+
SAUCE_REGION: eu-central-1
18+
1319
steps:
1420
- uses: actions/checkout@v4
15-
with:
16-
submodules: recursive
1721

18-
- name: Download sample build
19-
uses: actions/download-artifact@v4
22+
- name: Download artifact from workflow run
23+
uses: dawidd6/action-download-artifact@v6
2024
with:
21-
name: UE ${{ inputs.unreal-version }} sample build (Android)
25+
run_id: 19533023161
26+
name: "UE ${{ inputs.unreal-version }} sample build (Android)"
2227
path: sample-build
2328

2429
- name: List downloaded files
@@ -27,17 +32,6 @@ jobs:
2732
ls -lah sample-build/
2833
echo "Using x64 APK for emulator testing"
2934
30-
- name: Enable KVM group perms
31-
run: |
32-
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
33-
sudo udevadm control --reload-rules
34-
sudo udevadm trigger --name-match=kvm
35-
36-
- name: Setup Android directories
37-
run: |
38-
mkdir -p $HOME/.android/avd
39-
touch $HOME/.android/repositories.cfg
40-
4135
- name: Install Pester
4236
shell: pwsh
4337
run: Install-Module -Name Pester -Force -SkipPublisherCheck
@@ -49,37 +43,197 @@ jobs:
4943
mkdir build
5044
cmake -B build -S .
5145
52-
- name: Run Android Integration Tests
53-
uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b
54-
id: integration-test
55-
timeout-minutes: 45
56-
with:
57-
api-level: 34
58-
target: 'google_apis'
59-
arch: x86_64
60-
force-avd-creation: true
61-
disable-animations: true
62-
disable-spellchecker: true
63-
emulator-options: >
64-
-no-window
65-
-no-snapshot-save
66-
-gpu swiftshader_indirect
67-
-noaudio
68-
-no-boot-anim
69-
-camera-back none
70-
-camera-front none
71-
script: |
72-
adb wait-for-device
73-
cd integration-test && pwsh -Command "Invoke-Pester Integration.Tests.Android.ps1 -CI"
74-
env:
75-
SENTRY_UNREAL_TEST_DSN: ${{ secrets.SENTRY_UNREAL_TEST_DSN }}
76-
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_API_TOKEN }}
77-
SENTRY_UNREAL_TEST_APP_PATH: ${{ github.workspace }}/sample-build/SentryPlayground-x64.apk
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
66+
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+
}
83+
}
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
212+
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
221+
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
78230
79231
- name: Upload integration test output
80-
if: ${{ always() && steps.integration-test.outcome == 'failure' }}
232+
if: ${{ always() }}
81233
uses: actions/upload-artifact@v4
82234
with:
83-
name: UE ${{ inputs.unreal-version }} integration test output (Android)
84-
path: integration-test/output/
235+
name: UE ${{ inputs.unreal-version }} device logs (Android)
236+
path: |
237+
first_run_logs.json
238+
second_run_logs.json
85239
retention-days: 14

0 commit comments

Comments
 (0)