google / google/ground-android
[Code health] Update integration test config to reduce flakiness
- Dominant language
- Kotlin
- Stars
- 293
- Forks
- 149
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 42
Description
From GitHub job analysis in failing test https://github.com/google/ground-android/actions/runs/28511787637/job/84533015752?pr=3806
## Problem Analysis
The job is failing during Android instrumented tests with a critical error:
```
Failed to install split APK(s): [/home/runner/work/ground-android/ground-android/app/build/outputs/apk/local/debug/app-local-debug.apk]
Failed to install-write all apks
com.android.ddmlib.InstallException: Failed to install-write all apks
```
The root cause is an **Android emulator connectivity/stability issue**. The emulator becomes unresponsive during APK installation, as evidenced by:
1. **Shell command timeout**: `ShellCommandUnresponsiveException` when trying to install the APK
2. **Emulator device going offline**: `adb: device offline` appears in the logs
3. **Console connection failures**: `[EmulatorConsole]: Failed to start Emulator console for 5554`
## Solution
The issue stems from insufficient resource allocation or emulator instability in the CI environment. Here are the recommended fixes:
### 1. **Increase Emulator Stability Timeout**
The emulator needs more time to stabilize before APK installation. Modify `.github/workflows/test-e2e.yml` to add retry logic and extended timeouts:
```yaml
- name: Wait for emulator to be ready
run: |
MAX_TRIES=120 # Increased from current value
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_TRIES ]; do
if adb -s emulator-5554 shell getprop sys.boot_completed | grep -q '1'; then
echo "Emulator fully booted"
sleep 10 # Additional stabilization time
break
fi
echo "Waiting for emulator... ($((ATTEMPT+1))/$MAX_TRIES)"
sleep 2
((ATTEMPT++))
done
if [ $ATTEMPT -eq $MAX_TRIES ]; then
echo "Emulator failed to boot"
exit 1
fi
```
### 2. **Add APK Installation Retry Logic**
Wrap the test execution with retry logic in your Gradle configuration:
```gradle
// In your build.gradle or build.gradle.kts
android {
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
unitTests {
includeAndroidResources = true
}
}
}
```
### 3. **Reduce Test APK Size**
The split APK installation is failing. Consider:
- Disabling multi-APK splits for CI builds
- Reducing dex method count using R8/ProGuard optimization
```gradle
android {
buildTypes {
debug {
// Optimize for CI environment
minifyEnabled true
useProguard false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
```
### 4. **Increase CI Runner Resources** (if available)
Update `.github/workflows/test-e2e.yml` to use more robust hardware:
```yaml
runs-on: ubuntu-latest-xl # Use larger runner if available
```
### 5. **Add Emulator Health Checks Before Testing**
Insert a verification step before running tests:
```yaml
- name: Verify emulator health before testing
run: |
adb -s emulator-5554 shell getprop ro.bootloader
adb -s emulator-5554 shell getprop ro.hardware
adb -s emulator-5554 shell dumpsys meminfo | head -20
```
## Immediate Action
The most effective short-term solution is implementing **APK installation retry logic** with exponential backoff, combined with **extended emulator boot validation** to ensure full stability before attempting installation.
Contributor guide
Research direction
Start with .github/workflows/test-e2e.yml and the linked failing GitHub Actions job, focusing on emulator readiness and the APK installation step. Verify the failure mode locally in the workflow logs, then make the emulator wait or installation handling more resilient and confirm the instrumented tests complete without the reported offline or timeout errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, github-actions, kotlin
- Domain
- ci-cd, mobile-dev, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100