Skip to content

ci(device-tests): add a reusable workflow that runs device test apps on an emulator and simulator - #78

Merged
glennawatson merged 7 commits into
mainfrom
ci/device-tests
Sep 26, 2026
Merged

glennawatson merged 7 commits into
mainfrom
ci/device-tests

Conversation

@glennawatson

@glennawatson glennawatson commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A new reusable workflow runs a repository's device test apps on a headless Android emulator and an iOS simulator, through one script developers can also run locally.

  • workflow-common-device-tests.yml has an Android job on Linux and an iOS job on macOS. The jobs are named device-tests (android) and device-tests (ios) for branch protection. runAndroid: false or runIos: false skips a job, which still passes a required check.
  • The Android job enables KVM, installs the emulator and a system image, and uploads the TRX report, logcat and emulator log. The iOS job uploads the TRX report, the app console and the simulator log.
  • scripts/device-tests.cs does the whole device lifecycle. It boots a headless emulator or a fresh simulator, runs each test app, collects the report and logs, shuts the device down, and exits non-zero when a test fails.
  • Android runs through the .NET SDK's own device support. dotnet test --device <serial> installs the app, starts its instrumentation, streams each result and writes the TRX report on the host.
  • iOS runs through simctl. The script builds the app, installs it and launches it with its console attached. The app writes its TRX report and exit code into a results folder, which the simulator shares with the Mac.
  • The script refuses a host that cannot run the platform. iOS needs macOS with Xcode, and Android on Linux needs /dev/kvm.
  • README.md documents the workflow and the local command.

Why

Platform code under Platforms/android, apple-common and uikit-common only runs on a device, so no CI job exercised it.

Breaking changes

None. This adds a workflow and a script; no existing workflow changes.

How this was verified

The Android leg ran end to end on a local Linux host with KVM, in Debug and Release, against ReactiveUI's device test app.

  • The iOS leg is untested: no macOS host was available. The iOS app builds on Windows, and the script's host refusal and argument handling ran on Linux.

Notes for the reviewer

Start with scripts/device-tests.cs; the workflow is thin around it.

  • The workflow checks the script out at job.workflow_sha, so a caller pinned to this branch runs this branch's script. actionlint 1.7.12 does not know that documented job property yet and flags it (fix: add documented job workflow context properties rhysd/actionlint#707).
  • The script lives in a file rather than inline YAML because developers run the same file locally. A caller repository does not vendor a copy.
  • The script starts its own emulator on port 5580 with -read-only and its own AVD (rxui-device-tests, created when missing), so it does not touch a developer's running emulators.
  • The iOS job has not run yet. Expect the first run to shake out simulator runtime selection on macos-latest.

Checklist

  • I have read the Contribute guide
  • The PR title follows Conventional Commits
  • Tests cover this change, or the summary says why they do not
  • New or changed public API has XML documentation

…on an emulator and simulator

- Add scripts/device-tests.cs, a file-based app that boots a headless Android emulator or iOS simulator, runs the test app, collects the TRX report and device logs, and shuts the device down
- Run Android through the .NET SDK's `dotnet test --device` and iOS through simctl, refusing hosts that cannot run the platform
- Add workflow-common-device-tests.yml with an Android job on Linux and an iOS job on macOS, each skippable and uploading its results
- Check the script out at job.workflow_sha so a caller pinned to a branch runs that branch's script
- Document the workflow and the local command in README.md
- Default --framework to the project's first -android or -ios target framework instead of a fixed one
- Leave the workflow's framework inputs empty by default so the script chooses
Comment on lines +85 to +95
- name: Checkout device-tests script
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: reactiveui/actions-common
ref: ${{ job.workflow_sha }}
path: .device-tests-tooling
sparse-checkout: scripts
persist-credentials: false

# The hosted runner has KVM, but only root can open /dev/kvm until a udev rule opens it to the runner user.
- name: Enable KVM
Comment on lines +153 to +162
- name: Checkout device-tests script
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: reactiveui/actions-common
ref: ${{ job.workflow_sha }}
path: .device-tests-tooling
sparse-checkout: scripts
persist-credentials: false

- name: Setup .NET Environment
- Avoid the undisposed JsonDocument that fails the script's build under a caller's analyzer settings
- Choose the device type from the runtime's supported device types, since the global list includes iPhones the newest runtime cannot boot
… cannot boot

- Point avdmanager and the emulator at one ANDROID_AVD_HOME, since on hosted runners they looked in different folders and the emulator could not find the AVD it had just created
- Check the created AVD is visible to the emulator before booting it
- Refuse to start when /dev/kvm is not writable, stop waiting as soon as the emulator exits, and print emulator.log to the job log on a failed boot
- Give the emulator 4 GB and 4 cores, skip metrics, and allow a 15-minute cold boot
@glennawatson
glennawatson merged commit e7f96b0 into main Sep 26, 2026
5 checks passed
@glennawatson
glennawatson deleted the ci/device-tests branch September 26, 2026 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants