Skip to content

Fix iOS crash and workaround debug hang #1512

Fix iOS crash and workaround debug hang

Fix iOS crash and workaround debug hang #1512

Workflow file for this run

name: Run tests
env:
# The name of the main module repository
main_project_module: app
# The name in the Play Store
playstore_name: Soundscape
on:
pull_request:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
lint:
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
# Oboe is vendored as prebuilts rather than resolved by Gradle (so that prefab
# can stay off - see app/src/main/cpp/CMakeLists.txt), which means nothing
# normally verifies that the committed binaries still match the version pinned
# in the catalogue. This does. Needs no JDK, so it runs before the setup step.
- name: Check vendored Oboe matches the pinned version
run: ./scripts/update-oboe.sh --check
- name: Setup JDK + Gradle
uses: ./.github/actions/setup-jdk-gradle
- name: Run lint over the repo
run: ./gradlew lint
# The unit tests and the instrumentation tests are independent of each other,
# so they run as separate parallel jobs rather than one long serial job. Both
# need the offline map fixtures (the unit tests read them out of the test
# resources, the instrumentation job pushes one onto the emulator), but that
# step is cached so fetching it twice costs little.
unit-test:
name: Run unit tests
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup offline maps for tests
uses: ./.github/actions/setup-offline-maps
with:
output_dir: ${{ env.main_project_module }}/src/test/res/org/scottishtecharmy/soundscape
- name: Setup JDK + Gradle
uses: ./.github/actions/setup-jdk-gradle
# Setting up the tile and search providers from secrets won't work for runners which are
# triggered from forks e.g. to test a pull request. The tests that require tiles check if the
# tile provider exists, so we leave in this code so that if it's triggered by "Run workflow"
# the tile provider will be available.
- name: Setup tile and search providers
uses: ./.github/actions/write-provider-config
with:
platform: android
tile_provider_url: ${{ secrets.TILE_PROVIDER_URL }}
tile_provider_api_key: ${{ secrets.TILE_PROVIDER_API_KEY }}
search_provider_url: ${{ secrets.SEARCH_PROVIDER_URL }}
search_provider_api_key: ${{ secrets.SEARCH_PROVIDER_API_KEY }}
extract_provider_url: ${{ secrets.EXTRACT_PROVIDER_URL }}
- name: Run tests
run: ./gradlew test :shared:testAndroidHostTest
instrumentation-test:
name: Run instrumentation tests
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup offline maps for tests
uses: ./.github/actions/setup-offline-maps
with:
output_dir: ${{ env.main_project_module }}/src/test/res/org/scottishtecharmy/soundscape
- name: Setup JDK + Gradle
uses: ./.github/actions/setup-jdk-gradle
# See the note in the unit-test job: provider secrets are unavailable to
# fork-triggered runs, and the tests that need them check at runtime.
- name: Setup tile and search providers
uses: ./.github/actions/write-provider-config
with:
platform: android
tile_provider_url: ${{ secrets.TILE_PROVIDER_URL }}
tile_provider_api_key: ${{ secrets.TILE_PROVIDER_API_KEY }}
search_provider_url: ${{ secrets.SEARCH_PROVIDER_URL }}
search_provider_api_key: ${{ secrets.SEARCH_PROVIDER_API_KEY }}
extract_provider_url: ${{ secrets.EXTRACT_PROVIDER_URL }}
# Build the APK before the emulator boots so the slow Gradle work doesn't
# happen with an emulator sitting open waiting on it.
- name: Build debug version
run: ./gradlew assembleDebug
- name: Enable KVM
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: Instrumentation Tests
id: instrumentation-tests
uses: reactivecircus/android-emulator-runner@v2
continue-on-error: true # IMPORTANT: allow pipeline to continue to Android Test Report step
with:
api-level: 35
target: google_apis
arch: x86_64
script: |
adb root
adb shell mkdir -p /storage/emulated/0/Android/data/org.scottishtecharmy.soundscape/files/Download
adb push ${{ env.main_project_module }}/src/test/res/org/scottishtecharmy/soundscape/glasgow-gb.pmtiles /storage/emulated/0/Android/data/org.scottishtecharmy.soundscape/files/Download/
adb logcat -c # clear logs
touch app/emulator.log # create log file
chmod 777 app/emulator.log # allow writing to log file
adb logcat >> app/emulator.log & # pipe all logcat messages into log file as a background process
# NightlyOnly-annotated tests (real-time audio soak tests, live-network map fetches)
# are excluded here and run separately in nightly.yaml instead.
./gradlew connectedCheck -Pandroid.testInstrumentationRunnerArguments.notAnnotation=org.scottishtecharmy.soundscape.NightlyOnly
- name: Upload Failing Test Report Log
if: steps.instrumentation-tests.outcome != 'success' # upload the generated log on failure of the tests job
uses: actions/upload-artifact@v7
with:
name: logs
path: app/emulator.log # path to where the log is
- name: Raise error on test fail # set a red tick on the workflow run if the tests failed
if: steps.instrumentation-tests.outcome != 'success'
run: exit 1
ios-test:
name: Build and test iOS app
runs-on: [self-hosted, macOS, ARM64]
environment: development
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup iOS toolchain
uses: ./.github/actions/setup-ios-toolchain
# As with Android, secrets won't be available on fork-triggered PRs.
# iOS tests that require provider URLs check at runtime, so an empty
# Local.xcconfig is acceptable.
- name: Setup tile and search providers
uses: ./.github/actions/write-provider-config
with:
platform: ios
tile_provider_url: ${{ secrets.TILE_PROVIDER_URL }}
search_provider_url: ${{ secrets.SEARCH_PROVIDER_URL }}
extract_provider_url: ${{ secrets.EXTRACT_PROVIDER_URL }}
- name: Generate Xcode project
working-directory: iosApp
run: xcodegen generate
# We can't link the iOS test binary because maplibre-compose's published
# .klib has linker flags pointing at MapLibre.framework that our build
# has no way to provide outside the Xcode/SPM toolchain. Compile the
# test sources to catch Kotlin/iOS errors; the XCTest step below
# exercises the linked code via xcodebuild.
- name: Compile shared Kotlin/Native tests for iOS simulator
run: ./gradlew --configure-on-demand :shared:compileTestKotlinIosSimulatorArm64 -PincludeIos=true
# The runner image does not always pre-register a concrete simulator
# device (xcodebuild only sees the "Any iOS Simulator" placeholder), so
# pin the destination to a real UDID at runtime.
- name: Pick iOS simulator
run: |
xcrun simctl list runtimes
xcrun simctl list devices available
UDID=$(xcrun simctl list devices available --json \
| jq -r '[.devices | to_entries[] | select(.key | test("iOS")) | .value[] | select(.isAvailable and (.name | startswith("iPhone")))][0].udid // empty')
if [ -z "$UDID" ]; then
echo "No iPhone simulator pre-registered; creating one."
RUNTIME=$(xcrun simctl list runtimes available -j \
| jq -r '[.runtimes[] | select(.isAvailable and .platform == "iOS")] | sort_by(.version) | reverse | .[0].identifier')
DEVICETYPE=$(xcrun simctl list devicetypes -j \
| jq -r '[.devicetypes[] | select(.productFamily == "iPhone")] | sort_by(.name) | reverse | .[0].identifier')
UDID=$(xcrun simctl create "CI iPhone" "$DEVICETYPE" "$RUNTIME")
fi
echo "Using simulator $UDID"
echo "SIMULATOR_DESTINATION=platform=iOS Simulator,id=$UDID" >> "$GITHUB_ENV"
- name: Build iOS app for simulator
run: |
xcodebuild build-for-testing \
-project iosApp/iosApp.xcodeproj \
-scheme iosApp \
-configuration Debug \
-sdk iphonesimulator \
-destination "$SIMULATOR_DESTINATION" \
-derivedDataPath iosApp/build/DerivedData \
CODE_SIGNING_ALLOWED=NO
- name: Run XCTest suite
id: xctest
continue-on-error: true
run: |
xcodebuild test-without-building \
-project iosApp/iosApp.xcodeproj \
-scheme iosApp \
-configuration Debug \
-sdk iphonesimulator \
-destination "$SIMULATOR_DESTINATION" \
-derivedDataPath iosApp/build/DerivedData \
CODE_SIGNING_ALLOWED=NO
- name: Upload xcresult bundle
if: steps.xctest.outcome != 'success'
uses: actions/upload-artifact@v7
with:
name: ios-xcresult
path: iosApp/build/DerivedData/Logs/Test/*.xcresult
- name: Raise error on test fail
if: steps.xctest.outcome != 'success'
run: exit 1