From 55e11ec781abc27586f41b1191f1b169605e8bc8 Mon Sep 17 00:00:00 2001 From: Fran Aguilera Date: Thu, 9 Apr 2026 20:12:38 +0200 Subject: [PATCH] Add options for native/jvm crash --- ...gradle_test_app_random_native_crashes.yaml | 50 +++------- ...n_gradle_test_app_random_native_crashes.sh | 99 +++++++++++++++++++ tools/maestro/jvm-crash-loop.yaml | 3 +- tools/maestro/native-crash-loop.yaml | 1 + 4 files changed, 113 insertions(+), 40 deletions(-) create mode 100644 ci/run_gradle_test_app_random_native_crashes.sh diff --git a/.github/workflows/gradle_test_app_random_native_crashes.yaml b/.github/workflows/gradle_test_app_random_native_crashes.yaml index 66f163d05..22e500441 100644 --- a/.github/workflows/gradle_test_app_random_native_crashes.yaml +++ b/.github/workflows/gradle_test_app_random_native_crashes.yaml @@ -1,4 +1,4 @@ -name: Gradle Test App Random Native Crashes +name: Trigger Android Crash Test Loop E2E test on: workflow_dispatch: @@ -11,8 +11,10 @@ on: jobs: run_random_native_crashes: + name: Android gradle-test-app crash loop test runs-on: ubuntu-latest-8-cores env: + CRASH_LOOP_API_KEY: ${{ secrets.CRASH_LOOP_API_KEY }} SKIP_PROTO_GEN: 1 steps: - name: Checkout project sources @@ -23,53 +25,23 @@ jobs: with: setup-emulator: 'true' - - name: AVD cache - uses: actions/cache@v4 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: ${{ runner.os }}-avd-api-23-random-native-crashes-v1 - - - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - timeout-minutes: 30 - uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b - with: - channel: stable - force-avd-creation: false - api-level: 23 - target: default - ram-size: 2048M - arch: x86_64 - disk-size: 6144M - profile: Nexus 6 - disable-animations: true - emulator-options: -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - script: echo "Generated AVD snapshot for caching." + - name: Build gradle-test-app debug APK + timeout-minutes: 45 + run: ./tools/android_sdk_wrapper.sh platform/jvm/gradlew -Prust-target=x86_64 -PskipGradleTestAppLintOnAssemble=true -p platform/jvm gradle-test-app:assembleDebug --stacktrace - name: Run random native crashes timeout-minutes: 360 uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b with: + avd-name: android-gradle-test-app-crash-loop-test channel: stable force-avd-creation: false - api-level: 23 - target: default + api-level: 31 + target: google_apis ram-size: 2048M arch: x86_64 disk-size: 6144M profile: Nexus 6 disable-animations: true - emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - script: | - ITERATIONS="${{ github.event.inputs.iterations || '5000' }}" - if ! [[ "$ITERATIONS" =~ ^[0-9]+$ ]] || [[ "$ITERATIONS" -lt 1 ]]; then - echo "iterations must be a positive integer" - exit 1 - fi - curl -Ls "https://get.maestro.mobile.dev" | bash - export PATH="$PATH:$HOME/.maestro/bin" - ./tools/android_sdk_wrapper.sh platform/jvm/gradlew -Prust-target=x86_64 -p platform/jvm gradle-test-app:assembleDebug gradle-test-app:installDebug --stacktrace - ITERATIONS="$ITERATIONS" maestro test tools/maestro/native-crash-loop.yaml + emulator-options: -no-snapshot-save -no-window -accel on -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-metrics + script: bash ./ci/run_gradle_test_app_random_native_crashes.sh "${{ github.event.inputs.iterations }}" diff --git a/ci/run_gradle_test_app_random_native_crashes.sh b/ci/run_gradle_test_app_random_native_crashes.sh new file mode 100644 index 000000000..9de5f8efe --- /dev/null +++ b/ci/run_gradle_test_app_random_native_crashes.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -euo pipefail + +readonly iterations="${1:-5000}" +readonly emulator_serial="${ANDROID_SERIAL:-emulator-5554}" +readonly crash_loop_api_key="${CRASH_LOOP_API_KEY:-}" +readonly shared_prefs_path="shared_prefs/io.bitdrift.gradletestapp_preferences.xml" +readonly apk_path="platform/jvm/gradle-test-app/build/outputs/apk/debug/gradle-test-app-debug.apk" +readonly maestro_flow="tools/maestro/native-crash-loop.yaml" +readonly maestro_retries=3 + +if ! [[ "$iterations" =~ ^[0-9]+$ ]] || [[ "$iterations" -lt 1 ]]; then + echo "iterations must be a positive integer" + exit 1 +fi + +if [[ -z "$crash_loop_api_key" ]]; then + echo "CRASH_LOOP_API_KEY must be set" + exit 1 +fi + +if [[ ! -f "$apk_path" ]]; then + echo "Expected APK not found at $apk_path" + exit 1 +fi + +wait_for_emulator_ready() { + adb -s "$emulator_serial" wait-for-device + + for _ in $(seq 1 45); do + local sys_boot_completed + local dev_boot_completed + local boot_anim + + sys_boot_completed="$(adb -s "$emulator_serial" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" + dev_boot_completed="$(adb -s "$emulator_serial" shell getprop dev.bootcomplete 2>/dev/null | tr -d '\r')" + boot_anim="$(adb -s "$emulator_serial" shell getprop init.svc.bootanim 2>/dev/null | tr -d '\r')" + + if [[ "$sys_boot_completed" == "1" ]] && + [[ "$dev_boot_completed" == "1" ]] && + [[ "$boot_anim" == "stopped" ]] && + adb -s "$emulator_serial" shell cmd package list packages >/dev/null 2>&1; then + return 0 + fi + + adb reconnect offline >/dev/null 2>&1 || true + sleep 2 + done + + echo "Timed out waiting for emulator package manager readiness" + return 1 +} + +seed_gradle_test_app_settings() { + local escaped_api_key + local prefs_xml + + escaped_api_key="$(printf '%s' "$crash_loop_api_key" | sed -e 's/&/\&/g' -e 's/"/\"/g' -e "s/'/\\'/g" -e 's//\>/g')" + prefs_xml="$(cat < + + https://api.bitdrift.dev + $escaped_api_key + +EOF +)" + + printf '%s' "$prefs_xml" | adb -s "$emulator_serial" shell "run-as io.bitdrift.gradletestapp sh -c 'mkdir -p shared_prefs && cat > $shared_prefs_path'" +} + +curl -Ls "https://get.maestro.mobile.dev" | bash +export PATH="$PATH:$HOME/.maestro/bin" +export MAESTRO_CLI_NO_ANALYTICS=1 +export ANDROID_SERIAL="$emulator_serial" + +adb start-server +wait_for_emulator_ready +adb -s "$emulator_serial" install -r "$apk_path" +adb -s "$emulator_serial" shell input keyevent 82 >/dev/null 2>&1 || true +seed_gradle_test_app_settings + +for attempt in $(seq 1 "$maestro_retries"); do + echo "Running Maestro attempt $attempt/$maestro_retries" + + if maestro test -e ITERATIONS="$iterations" "$maestro_flow"; then + exit 0 + fi + + if [[ "$attempt" -eq "$maestro_retries" ]]; then + exit 1 + fi + + echo "Maestro failed, restarting adb before retry" + adb kill-server || true + adb start-server + wait_for_emulator_ready + seed_gradle_test_app_settings +done diff --git a/tools/maestro/jvm-crash-loop.yaml b/tools/maestro/jvm-crash-loop.yaml index 1fbc36448..0fc54c5a3 100644 --- a/tools/maestro/jvm-crash-loop.yaml +++ b/tools/maestro/jvm-crash-loop.yaml @@ -1,8 +1,9 @@ appId: io.bitdrift.gradletestapp --- - repeat: - times: 50 + times: ${ITERATIONS} commands: + - back - launchApp - tapOn: "App Exits" - tapOn: "JVM crash" diff --git a/tools/maestro/native-crash-loop.yaml b/tools/maestro/native-crash-loop.yaml index 846c15ff8..58aa0d231 100644 --- a/tools/maestro/native-crash-loop.yaml +++ b/tools/maestro/native-crash-loop.yaml @@ -3,6 +3,7 @@ appId: io.bitdrift.gradletestapp - repeat: times: ${ITERATIONS} commands: + - back - launchApp - tapOn: "App Exits" - tapOn: "Native Crash"