Skip to content

Commit 1a9eca2

Browse files
committed
test(e2e, android): use modern android emulator startup style
ported from AnkiDroid, hopefully more reliable
1 parent c0ec732 commit 1a9eca2

File tree

1 file changed

+97
-71
lines changed

1 file changed

+97
-71
lines changed

.github/workflows/tests_e2e_android.yml

Lines changed: 97 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,18 @@ jobs:
2424
android:
2525
name: Android
2626
runs-on: macos-12
27-
timeout-minutes: 70
27+
timeout-minutes: 90
28+
strategy:
29+
fail-fast: false
30+
matrix:
31+
# Refactor to make these dynamic with a low/high bracket only on schedule, not push
32+
# For now this is just the fastest combo (api/arch/target/snapshot-warm-time) based on testing
33+
api-level: [30]
34+
arch: [x86_64]
35+
target: [google_apis]
36+
first-boot-delay: [600]
37+
# This is useful for benchmarking, do 0, 1, 2, etc (up to 256 max job-per-matrix limit) for averages
38+
iteration: [0]
2839
env:
2940
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
3041
EMULATOR_COMMAND: "-avd TestingAVD -noaudio -gpu swiftshader_indirect -camera-back none -no-snapshot -no-window -no-boot-anim -nojni -memory 2048 -timezone 'Europe/London' -cores 2"
@@ -93,7 +104,7 @@ jobs:
93104
- name: Build Android App
94105
uses: nick-invision/retry@v2
95106
with:
96-
timeout_minutes: 15
107+
timeout_minutes: 25
97108
retry_wait_seconds: 60
98109
max_attempts: 3
99110
command: yarn tests:android:build
@@ -118,77 +129,92 @@ jobs:
118129
curl --output /dev/null --silent --head --fail "http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&inlineSourceMap=true"
119130
echo "...javascript bundle ready."
120131
121-
- name: Download Emulator Image
122-
# This can fail on network request, wrap with retry
123-
uses: nick-invision/retry@v2
132+
# This appears to be 'Cache Size: ~1230 MB (1290026823 B)' based on watching action logs
133+
# Repo limit is 10GB; branch caches are independent; branches may read default branch cache.
134+
# We don't want branches to evict main branch snapshot, so save on main, read-only all else
135+
- name: AVD cache
136+
uses: actions/cache@v3
137+
id: avd-cache
124138
with:
125-
timeout_minutes: 10
126-
retry_wait_seconds: 60
127-
max_attempts: 3
128-
command: echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "system-images;android-30;google_apis;x86_64"
129-
130-
- name: Create Emulator
131-
run: echo "no" | $ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd --force --name TestingAVD --device "Nexus 5X" -k 'system-images;android-30;google_apis;x86_64' -g google_apis
132-
133-
# These Emulator start steps are the current best practice to do retries on multi-line commands with persistent (nohup) processes
134-
- name: Start Android Emulator
135-
id: emu1
136-
timeout-minutes: 5
137-
continue-on-error: true
138-
run: |
139-
echo "Starting emulator"
140-
nohup $ANDROID_HOME/emulator/emulator $EMULATOR_COMMAND &
141-
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done'
142-
143-
- name: Start Android Emulator Retry 1
144-
id: emu2
145-
if: steps.emu1.outcome=='failure'
146-
timeout-minutes: 5
147-
continue-on-error: true
148-
run: |
149-
echo "Starting emulator, second attempt"
150-
$ANDROID_HOME/platform-tools/adb devices
151-
sudo killall -9 $EMULATOR_EXECUTABLE || true
152-
sleep 2
153-
nohup $ANDROID_HOME/emulator/emulator $EMULATOR_COMMAND &
154-
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done'
155-
156-
- name: Start Android Emulator Retry 2
157-
id: emu3
158-
if: steps.emu2.outcome=='failure'
159-
timeout-minutes: 5
160-
continue-on-error: true
161-
run: |
162-
echo "Starting emulator, third attempt"
163-
$ANDROID_HOME/platform-tools/adb devices
164-
sudo killall -9 $EMULATOR_EXECUTABLE || true
165-
sleep 2
166-
nohup $ANDROID_HOME/emulator/emulator $EMULATOR_COMMAND &
167-
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done'
168-
169-
- name: Emulator Status
170-
if: always()
171-
run: |
172-
if ${{ steps.emu1.outcome=='success' || steps.emu2.outcome=='success' || steps.emu3.outcome=='success' }}; then
173-
echo "Emulator Started"
174-
else
175-
exit 1
176-
fi
177-
178-
- name: Detox Test
179-
# Detox uses Espresso to choreograph steps in reaction to UI events, so we need to send a stream of taps.
180-
timeout-minutes: 40
139+
path: |
140+
~/.android/avd/*
141+
~/.android/adb*
142+
key: avd-${{ matrix.api-level }}-${{ matrix.arch }}-${{matrix.target}}-v1-${{ hashFiles('~/.android/avd/**/snapshots/**') }}
143+
restore-keys: |
144+
avd-${{ matrix.api-level }}-${{ matrix.arch }}-${{matrix.target}}-v1
145+
146+
- name: AVD Boot and Snapshot Creation
147+
# Only generate a snapshot with a cache miss
148+
# Comment the if out to generate snapshots on branch for performance testing
149+
if: steps.avd-cache.outputs.cache-hit != 'true'
150+
uses: reactivecircus/android-emulator-runner@v2
151+
with:
152+
api-level: ${{ matrix.api-level }}
153+
avd-name: TestingAVD
154+
force-avd-creation: false
155+
target: ${{ matrix.target }}
156+
arch: ${{ matrix.arch }}
157+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
158+
sdcard-path-or-size: 100M
159+
disable-animations: true
160+
# Give the emulator a little time to run and do first boot stuff before taking snapshot
161+
script: |
162+
$ANDROID_HOME/platform-tools/adb logcat '*:D' -d > adb-snapshot-log.txt
163+
$ANDROID_HOME/platform-tools/adb logcat --clear
164+
echo "Generated AVD snapshot for caching."
165+
166+
# This step is separate so pure install time may be calculated as a step
167+
- name: Emulator Snapshot After Firstboot Warmup
168+
# Only generate a snapshot for saving with a cache miss
169+
# Switch the if statements via comment if generating snapshots for performance testing
170+
# if: matrix.first-boot-delay != '0'
171+
if: steps.avd-cache.outputs.cache-hit != 'true'
172+
env:
173+
FIRST_BOOT_DELAY: ${{ matrix.first-boot-delay }}
174+
uses: reactivecircus/android-emulator-runner@v2
175+
with:
176+
api-level: ${{ matrix.api-level }}
177+
avd-name: TestingAVD
178+
force-avd-creation: false
179+
target: ${{ matrix.target }}
180+
arch: ${{ matrix.arch }}
181+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
182+
sdcard-path-or-size: 100M
183+
disable-animations: true
184+
# Give the emulator a little time to run and do first boot stuff before taking snapshot
185+
# The zygote restart makes sure zygote has correct heap size as a workaround for android emulator init bug
186+
script: |
187+
$ANDROID_HOME/platform-tools/adb shell su root "setprop ctl.restart zygote"
188+
sleep $FIRST_BOOT_DELAY
189+
$ANDROID_HOME/platform-tools/adb logcat '*:D' -d > adb-warmup-log.txt
190+
$ANDROID_HOME/platform-tools/adb logcat --clear
191+
echo "First boot warmup completed."
192+
193+
- name: Test Tapper
194+
# Run this outside the emulator runner so the emulator runner does not wait on it for cleanup
181195
run: |
182-
$ANDROID_HOME/platform-tools/adb devices
183-
$ANDROID_HOME/platform-tools/adb shell settings put global window_animation_scale 0.0
184-
$ANDROID_HOME/platform-tools/adb shell settings put global transition_animation_scale 0.0
185-
$ANDROID_HOME/platform-tools/adb shell settings put global animator_duration_scale 0.0
186196
nohup sh -c "until false; do $ANDROID_HOME/platform-tools/adb shell input tap 100 800; sleep 0.2; done" &
187-
nohup sh -c "$ANDROID_HOME/platform-tools/adb logcat '*:D' > adb-log.txt" &
188-
yarn tests:android:test-cover
189-
yarn tests:android:test:jacoco-report
190197
shell: bash
191198

199+
- name: Detox Tests
200+
uses: reactivecircus/android-emulator-runner@v2
201+
timeout-minutes: 40
202+
with:
203+
api-level: ${{ matrix.api-level }}
204+
avd-name: TestingAVD
205+
force-avd-creation: false
206+
target: ${{ matrix.target }}
207+
arch: ${{ matrix.arch }}
208+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
209+
sdcard-path-or-size: 100M
210+
disable-animations: true
211+
# Detox uses Espresso to choreograph steps in reaction to UI events, so we need to send a stream of taps.
212+
script: |
213+
$ANDROID_HOME/platform-tools/adb devices
214+
nohup sh -c "$ANDROID_HOME/platform-tools/adb logcat '*:D' > adb-log.txt" &
215+
yarn tests:android:test-cover
216+
yarn tests:android:test:jacoco-report
217+
192218
- name: Submit Coverage
193219
# This can fail on timeouts etc, wrap with retry
194220
uses: nick-invision/retry@v2
@@ -200,12 +226,12 @@ jobs:
200226

201227
- name: Compress Emulator Log
202228
if: always()
203-
run: gzip -9 adb-log.txt
229+
run: gzip -9 adb-*.txt
204230
shell: bash
205231

206232
- name: Upload Emulator Log
207-
uses: actions/upload-artifact@v2
233+
uses: actions/upload-artifact@v3
208234
if: always()
209235
with:
210236
name: adb_logs
211-
path: adb-log.txt.gz
237+
path: adb-*.gz

0 commit comments

Comments
 (0)