-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
309 lines (288 loc) · 14 KB
/
Copy pathtest-android-game.yml
File metadata and controls
309 lines (288 loc) · 14 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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
name: Test Android Game (Vulkan)
run-name: >-
${{ github.event.inputs.commit-sha != '' && format('Test Android Game (Vulkan) @ {0}', github.event.inputs.commit-sha) || '' }}
on:
workflow_dispatch:
inputs:
build-type:
description: Build Configuration
default: Debug
type: choice
options:
- Debug
- Release
test-filter:
description: 'vstest --filter expression forwarded to the on-device runner (blank = all tests)'
type: string
default: ''
project:
description: 'Single test .csproj to build/run (blank = all Android suites)'
type: string
default: ''
commit-sha:
description: Commit SHA to test (default = HEAD of selected branch)
type: string
default: ''
upload-binlog:
description: Capture and upload build binlog (off by default; adds some I/O overhead)
default: false
type: boolean
use-combined:
description: 'Combine all test suites into one APK (faster build/install/run); false = per-suite APKs'
default: true
type: boolean
repeat:
description: 'Rerun the filtered test set up to N times in-process, stop on first failure (flake hunting). 1 = single run (default).'
type: number
default: 1
workflow_call:
inputs:
build-type:
default: Debug
type: string
test-filter:
default: ''
type: string
project:
default: ''
type: string
commit-sha:
default: ''
type: string
# Internal (callers only, e.g. test-gold-gen): tolerate test failures so generating new gold
# doesn't fail the run. Build failures still fail. Not exposed in the dispatch UI.
tolerate-test-failures:
default: false
type: boolean
upload-binlog:
default: false
type: boolean
use-combined:
default: true
type: boolean
repeat:
type: number
default: 1
schedule:
- cron: '37 3 * * *'
concurrency:
group: test-android-game-${{ github.event.pull_request.number || github.ref }}-${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}
cancel-in-progress: true
jobs:
Android-Tests-Game:
name: Android (Vulkan, ${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }})
# Linux host: KVM-accelerated x86_64 Android emulator is the only path that runs in a
# reasonable amount of time on a hosted runner. macOS-14+ Apple Silicon runners don't
# support nested virt for AOSP emulator; older macOS-13 Intel HAXM is being retired.
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
STRIDE_GRAPHICS_SOFTWARE_RENDERING: "1"
STRIDE_MAX_PARALLELISM: "8"
DOTNET_NUGET_SIGNATURE_VERIFICATION: "false"
steps:
# Hosted runners vary per VM (free space, CPU model, noisy neighbors); record the
# initial state to diagnose "No space left on device" failures and timing variance.
- name: Report runner resources
run: |
df -h /
nproc
lscpu | grep -E 'Model name|MHz' || true
free -h
# Bootstrap .github so the local composite action below is resolvable pre-checkout.
- uses: actions/checkout@v7
with:
sparse-checkout: .github
- uses: ./.github/actions/stride-checkout
with:
ref: ${{ (github.event.inputs.commit-sha != '' && github.event.inputs.commit-sha) || (inputs.commit-sha != '' && inputs.commit-sha) || github.ref }}
exclude: samples
- name: Install Android Workload
uses: ./.github/actions/stride-workload
with:
workloads: android
- name: Setup Android SDK
# NDK: not installed/pinned — the runner image preinstalls NDKs and each project's
# Android workload targets resolve _AndroidNdkDirectory locally (Stride.Build.Sdk
# strips it from ProjectReference dispatches so the value never fragments build
# cache keys; Stride.Native.targets enforces a minimum version at compile time).
uses: android-actions/setup-android@v4
with:
cmdline-tools-version: 12266719
accept-android-sdk-licenses: true
packages: tools platform-tools build-tools;36.0.0 platforms;android-34 system-images;android-34;google_apis;x86_64
- name: Install Linux host runtime deps (for asset compilation)
run: |
# Asset compilation runs on the Linux host with Vulkan via Lavapipe. Mirrors the
# set used by test-linux-game.yml.
sudo apt-get update
sudo apt-get install -y libvulkan1 libvulkan-dev xvfb lld vulkan-validationlayers libva2 libva-drm2 libva-x11-2
- name: Start virtual display (asset compiler Vulkan)
run: |
Xvfb :99 -screen 0 1920x1080x24 &
echo "DISPLAY=:99" >> $GITHUB_ENV
- name: Register Lavapipe ICD
run: |
# Use the no-dma_buf Lavapipe variant (runtimes/linux-x64-nodmabuf/native/): gfxstream
# aborts headless on an ICD advertising VK_EXT_external_memory_dma_buf. AssetCompiler
# needs a Vulkan device at build time (Skybox/IBL etc.) and is indifferent to dma_buf,
# so the same ICD serves both; it then drives the emulator's Vulkan path via -gpu host
# -feature ForceGpuHost (gfxstream forwards Vulkan to the host loader, honouring
# VK_DRIVER_FILES).
dotnet restore build/Stride.Tests.Game.Runtime.slnf \
-p:StridePlatforms=Linux \
-p:StrideGraphicsApis=Vulkan
LIB=$(find ~/.nuget/packages/stride.dependencies.lavapipe -path "*/runtimes/linux-x64-nodmabuf/native/libvulkan_lvp.so" | head -1)
if [ -z "$LIB" ]; then
echo "::error::Lavapipe libvulkan_lvp.so (no-dma_buf variant) not found in NuGet cache"
exit 1
fi
LIB_ABS=$(readlink -f "$LIB")
ICD_JSON="$PWD/lvp_icd.json"
echo "{\"file_format_version\":\"1.0.0\",\"ICD\":{\"library_path\":\"$LIB_ABS\",\"api_version\":\"1.3.0\"}}" > "$ICD_JSON"
echo "VK_DRIVER_FILES=$ICD_JSON" >> $GITHUB_ENV
- name: Build Android emulator helper Vulkan layer
# Stamps the host OS into the device name so gold images bucket by emulator host
# (Lavapipe renders slightly differently per host RID, and the guest can't otherwise
# tell which host it's on).
run: pwsh tests/android/stride_android_emu_helper_layer/build-and-install.ps1
- name: Resolve build target / suite
run: |
# `project` set (gold-gen: one suite only) → that .csproj's APK + run-suites.sh.
# Unset → use-combined=true (default): Combined.csproj — all suites in one APK,
# one install, one process run (faster build/testing).
# use-combined=false → slnf build + per-suite APKs + per-suite install/run.
PROJ="${{ github.event.inputs.project || inputs.project }}"
USE_COMBINED="${{ github.event.inputs.use-combined || inputs.use-combined || 'true' }}"
if [ -n "$PROJ" ]; then
echo "ANDROID_BUILD_TARGET=$PROJ" >> "$GITHUB_ENV"
echo "ANDROID_TEST_SUITES=$(basename "$PROJ" .csproj)" >> "$GITHUB_ENV"
echo "ANDROID_RUN_SCRIPT=tests/android/run-suites.sh" >> "$GITHUB_ENV"
elif [ "$USE_COMBINED" = "true" ]; then
echo "ANDROID_BUILD_TARGET=sources/tests/Stride.Tests.Combined/Stride.Tests.Combined.csproj" >> "$GITHUB_ENV"
echo "ANDROID_RUN_SCRIPT=tests/android/run-combined.sh" >> "$GITHUB_ENV"
else
# slnf path: one build evaluates the graph once; per-suite APKs land under
# bin/Tests/<Suite>/Android-Vulkan/<Config>/. Run step iterates ANDROID_TEST_SUITES.
echo "ANDROID_BUILD_TARGET=build/Stride.Tests.Game.Android.slnf" >> "$GITHUB_ENV"
echo "ANDROID_RUN_SCRIPT=tests/android/run-suites.sh" >> "$GITHUB_ENV"
{
echo "ANDROID_TEST_SUITES<<EOF"
echo "Stride.Engine.NoAssets.Tests"
echo "Stride.Input.Tests"
echo "Stride.Audio.Tests"
echo "Stride.Particles.Tests"
echo "Stride.UI.Tests"
echo "Stride.Navigation.Tests"
echo "Stride.Physics.Tests"
echo "Stride.Engine.Tests"
echo "Stride.Graphics.Tests"
echo "Stride.Graphics.Tests.10_0"
echo "Stride.Graphics.Tests.11_0"
echo "EOF"
} >> "$GITHUB_ENV"
fi
- name: Reclaim disk before build
# The combined APK build embeds compiled assets for all suites and dies on the
# runner's stock ~20 GB free. Drop preinstalled trees this job never touches.
# Keep dotnet (drives the workload), the Android SDK/NDK, and pwsh (helper layer).
# large-packages stays off (slow apt removals); swift/CodeQL have no action toggle.
run: |
df -h /
sudo rm -rf /usr/share/swift /opt/hostedtoolcache/CodeQL
- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1
with:
android: false
dotnet: false
tool-cache: false
large-packages: false
haskell: true
docker-images: true
- name: Build Android test APK(s)
run: |
# Embed assemblies into the APK so the test runner has everything (no Fast Deployment).
dotnet build "$ANDROID_BUILD_TARGET" \
-nr:false -v:m -p:WarningLevel=0 \
-p:StrideSkipAutoPack=true \
${{ inputs.upload-binlog && '-bl:build.binlog' || '' }} \
-p:Configuration=${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }} \
-p:StridePlatforms=Android \
-p:EmbedAssembliesIntoApk=true \
-p:AndroidUseSharedRuntime=false
- name: Upload build binlog
# failure() so we still get the binlog when Build dies (most useful case)
if: failure() || inputs.upload-binlog
uses: actions/upload-artifact@v7
with:
name: build-binlog-android-game
path: build.binlog
if-no-files-found: ignore
- name: Enable KVM
# Hosted ubuntu runners expose /dev/kvm but the device node has restrictive perms
# by default. This udev rule grants 0666 so the emulator can use KVM acceleration.
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Run tests on Android emulator
continue-on-error: ${{ inputs.tolerate-test-failures == true }}
# Action handles emulator boot/teardown; we run the per-suite pwsh driver inside.
# API 34 / google_apis / x86_64 — API 36+ (Android 16) crashes Mono JIT on the
# emulator (W^X enforcement), so we pin to 34.
env:
# Empty bypasses gfxstream's built-in ICD overrides (initIcdPaths/setIcdPaths)
# which would clobber VK_DRIVER_FILES with bundled paths.
ANDROID_EMU_VK_ICD: ""
# Activates the host-OS-stamp implicit Vulkan layer installed above.
STRIDE_EMU_LAYER: "1"
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 34
target: google_apis
arch: x86_64
force-avd-creation: false
# -no-audio omitted: Stride.Audio.Tests' AudioTrack init segfaults in native code
# if the emulator has no audio backend. Default backend on Linux is none/null
# which is enough to keep AudioTrack from crashing.
# -gpu host + -feature ForceGpuHost route Vulkan through the host loader so it
# honours VK_DRIVER_FILES (set in "Register Lavapipe ICD") and picks our Stride
# Lavapipe. The Linux emulator build has no -use-host-vulkan flag, so this
# combination is its equivalent.
# -partition-size 8192 (MB): default userdata is too small for several ~90 MB test APKs (INSUFFICIENT_STORAGE).
emulator-options: -no-snapshot-load -no-window -no-boot-anim -gpu host -feature ForceGpuHost -partition-size 8192
disable-animations: true
# android-emulator-runner runs each `script:` line as its own `sh -c`, so multi-line
# shell blocks break with "unexpected end of file". The driver lives in a standalone
# script — Combined or per-suite — chosen by the Resolve step above.
script: bash "$ANDROID_RUN_SCRIPT" "${{ github.event.inputs.build-type || inputs.build-type || 'Debug' }}" "${{ github.event.inputs.test-filter || inputs.test-filter }}" "${{ github.event.inputs.repeat || inputs.repeat || '1' }}"
- name: Publish Test Report
if: always()
uses: phoenix-actions/test-reporting@v16
with:
name: 'Test Report: Android Game (Vulkan)'
path: TestResults/*.trx
reporter: dotnet-trx
output-to: step-summary
list-tests: 'failed'
# Tolerated (gold-gen) test failures shouldn't red the run via the reporter; the Build
# and Test steps still fail normally. Defaults to true for direct / PR-gate runs.
fail-on-error: ${{ inputs.tolerate-test-failures != true }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
with:
name: test-results-android-vulkan
path: TestResults/
if-no-files-found: ignore
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: test-artifacts-android-vulkan
# Single path so upload-artifact uses tests/local/ as the common ancestor: per-suite
# outputs land as Stride.<Suite>.Tests/... at the artifact root, matching what
# comparegold and the per-suite (master) flow expect. The driver moves the logcat
# into ResultsDir after the pre-pull wipe is past so it ships with this artifact.
path: tests/local/
if-no-files-found: ignore