274 lines
8.2 KiB
Bash
Executable File
274 lines
8.2 KiB
Bash
Executable File
#!/bin/sh
|
|
set -eu
|
|
|
|
ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
ANDROID_ENV="$ROOT/.env"
|
|
TEST_ENV="$ROOT/.env.android-test"
|
|
run_id=$(date -u +%Y%m%d%H%M%S)-$$
|
|
android_api=${WNH_ANDROID_TEST_API:-37.0}
|
|
# 1G is measured against the complete API 30/34/37 suite. It can still be
|
|
# overridden for a future test that intentionally stores more device data.
|
|
android_data_partition_size=${WNH_ANDROID_TEST_DATA_PARTITION_SIZE:-1G}
|
|
|
|
if ! printf '%s\n' "$android_data_partition_size" |
|
|
grep -Eq '^[1-9][0-9]*[GKM]$'; then
|
|
echo "WNH_ANDROID_TEST_DATA_PARTITION_SIZE must be a positive integer followed by G, M, or K." >&2
|
|
exit 1
|
|
fi
|
|
|
|
case "$android_api" in
|
|
30)
|
|
android_system_image="system-images/android-30/google_apis/x86_64"
|
|
;;
|
|
34)
|
|
android_system_image="system-images/android-34/google_apis/x86_64"
|
|
;;
|
|
37.0)
|
|
android_system_image="system-images/android-37.0/google_apis_ps16k/x86_64"
|
|
;;
|
|
*)
|
|
echo "WNH_ANDROID_TEST_API must be one of: 30, 34, 37.0." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
api_label=$(printf '%s' "$android_api" | tr '.' '-')
|
|
image="who-need-help-android:instrumentation-api${api_label}-$run_id"
|
|
container="who-need-help-android-instrumentation-api${api_label}-$run_id"
|
|
output="$ROOT/output/android-instrumentation/api${api_label}/$run_id"
|
|
geo_pid=""
|
|
death_runner_pid=""
|
|
|
|
if [ ! -e /dev/kvm ]; then
|
|
echo "/dev/kvm is required for the containerized Android emulator." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f "$ANDROID_ENV" ]; then
|
|
echo "Missing $ANDROID_ENV. Copy .env.example to .env and configure Android timings." >&2
|
|
exit 1
|
|
fi
|
|
|
|
"$ROOT/scripts/ensure-local-android-test-env.sh"
|
|
|
|
set -a
|
|
# shellcheck source=/dev/null
|
|
. "$ANDROID_ENV"
|
|
# shellcheck source=/dev/null
|
|
. "$TEST_ENV"
|
|
set +a
|
|
|
|
: "${WNH_ANDROID_TEST_BASE_URL:?Set WNH_ANDROID_TEST_BASE_URL in .env.android-test}"
|
|
: "${WNH_TRACKING_MIN_TIME_MS:?Set WNH_TRACKING_MIN_TIME_MS in .env}"
|
|
: "${WNH_TRACKING_HTTP_TIMEOUT_MS:?Set WNH_TRACKING_HTTP_TIMEOUT_MS in .env}"
|
|
|
|
android_test_port=${WNH_ANDROID_TEST_BASE_URL#http://127.0.0.1:}
|
|
|
|
if [ "$android_test_port" = "$WNH_ANDROID_TEST_BASE_URL" ]; then
|
|
echo "WNH_ANDROID_TEST_BASE_URL must be an http://127.0.0.1:PORT origin." >&2
|
|
exit 1
|
|
fi
|
|
|
|
case "$android_test_port" in
|
|
""|*[!0-9]*)
|
|
echo "WNH_ANDROID_TEST_BASE_URL must contain only a numeric port." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [ "$android_test_port" -lt 1 ] || [ "$android_test_port" -gt 65535 ]; then
|
|
echo "WNH_ANDROID_TEST_BASE_URL port must be between 1 and 65535." >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$output"
|
|
|
|
cleanup() {
|
|
status=$?
|
|
|
|
if [ -n "$geo_pid" ]; then
|
|
kill "$geo_pid" >/dev/null 2>&1 || true
|
|
wait "$geo_pid" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
if [ -n "$death_runner_pid" ]; then
|
|
kill "$death_runner_pid" >/dev/null 2>&1 || true
|
|
wait "$death_runner_pid" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
if [ "$status" -ne 0 ] && docker inspect "$container" >/dev/null 2>&1; then
|
|
docker exec "$container" adb logcat -d > "$output/logcat.txt" 2>&1 || true
|
|
docker exec "$container" adb shell dumpsys activity services \
|
|
org.whoneedhelp.mobile.debug > "$output/services.txt" 2>&1 || true
|
|
docker logs "$container" > "$output/emulator.log" 2>&1 || true
|
|
fi
|
|
|
|
docker rm -f "$container" >/dev/null 2>&1 || true
|
|
docker image rm "$image" >/dev/null 2>&1 || true
|
|
exit "$status"
|
|
}
|
|
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
docker build \
|
|
--build-arg "WNH_DEBUG_BASE_URL=$WNH_ANDROID_TEST_BASE_URL" \
|
|
--build-arg "WNH_TRACKING_MIN_TIME_MS=$WNH_TRACKING_MIN_TIME_MS" \
|
|
--build-arg "WNH_TRACKING_HTTP_TIMEOUT_MS=$WNH_TRACKING_HTTP_TIMEOUT_MS" \
|
|
--build-arg "ANDROID_EMULATOR_SYSTEM_IMAGE=$android_system_image" \
|
|
--build-arg "ANDROID_EMULATOR_DATA_PARTITION_SIZE=$android_data_partition_size" \
|
|
--target emulator \
|
|
-t "$image" \
|
|
"$ROOT/android"
|
|
|
|
docker run -d \
|
|
--name "$container" \
|
|
--device /dev/kvm \
|
|
--network none \
|
|
"$image" > "$output/container-id.txt"
|
|
|
|
docker exec "$container" adb wait-for-device
|
|
|
|
booted=""
|
|
attempt=0
|
|
while [ "$attempt" -lt 90 ]; do
|
|
booted=$(docker exec "$container" adb shell getprop sys.boot_completed 2>/dev/null \
|
|
| tr -d '\r')
|
|
|
|
if [ "$booted" = "1" ]; then
|
|
break
|
|
fi
|
|
|
|
attempt=$((attempt + 1))
|
|
sleep 2
|
|
done
|
|
|
|
if [ "$booted" != "1" ]; then
|
|
echo "Android emulator did not finish booting." >&2
|
|
exit 1
|
|
fi
|
|
|
|
docker exec "$container" adb shell input keyevent 82
|
|
docker exec "$container" adb shell settings put global window_animation_scale 0
|
|
docker exec "$container" adb shell settings put global transition_animation_scale 0
|
|
docker exec "$container" adb shell settings put global animator_duration_scale 0
|
|
docker exec "$container" adb shell cmd location set-location-enabled true
|
|
docker exec "$container" adb emu geo fix -122.084000 37.422000
|
|
docker exec "$container" adb install -r \
|
|
/opt/who-need-help/who-need-help-debug.apk
|
|
docker exec "$container" adb install -r \
|
|
/opt/who-need-help/who-need-help-debug-androidTest.apk
|
|
|
|
docker exec "$container" adb shell pm list instrumentation \
|
|
> "$output/instrumentation.txt"
|
|
|
|
(
|
|
while docker inspect "$container" >/dev/null 2>&1; do
|
|
docker exec "$container" adb emu geo fix -122.084000 37.422000 \
|
|
>/dev/null 2>&1 || true
|
|
sleep 2
|
|
done
|
|
) &
|
|
geo_pid=$!
|
|
|
|
set +e
|
|
docker exec "$container" adb shell am instrument -w -r \
|
|
-e class org.whoneedhelp.mobile.AndroidClientInstrumentedTest \
|
|
org.whoneedhelp.mobile.debug.test/androidx.test.runner.AndroidJUnitRunner \
|
|
> "$output/results.txt" 2>&1
|
|
instrumentation_status=$?
|
|
set -e
|
|
cat "$output/results.txt"
|
|
|
|
if [ "$instrumentation_status" -ne 0 ] \
|
|
|| ! grep -Eq '^OK \([0-9]+ tests?\)$' "$output/results.txt" \
|
|
|| grep -Eq 'FAILURES!!!|INSTRUMENTATION_FAILED|Process crashed' "$output/results.txt"; then
|
|
echo "Android instrumentation failed; diagnostics are retained in $output." >&2
|
|
exit 1
|
|
fi
|
|
|
|
docker exec "$container" adb shell am force-stop org.whoneedhelp.mobile.debug
|
|
docker exec "$container" adb root >"$output/adb-root.txt"
|
|
docker exec "$container" adb wait-for-device
|
|
docker exec "$container" adb shell am instrument -w -r \
|
|
-e class org.whoneedhelp.mobile.TrackingProcessDeathProbeTest \
|
|
org.whoneedhelp.mobile.debug.test/androidx.test.runner.AndroidJUnitRunner \
|
|
>"$output/process-death-results.txt" 2>&1 &
|
|
death_runner_pid=$!
|
|
|
|
service_observed=""
|
|
attempt=0
|
|
while [ "$attempt" -lt 20 ]; do
|
|
if docker exec "$container" adb shell dumpsys activity services \
|
|
org.whoneedhelp.mobile.debug |
|
|
grep -q 'org.whoneedhelp.mobile.TrackingService'; then
|
|
service_observed=1
|
|
break
|
|
fi
|
|
|
|
attempt=$((attempt + 1))
|
|
sleep 1
|
|
done
|
|
|
|
if [ -z "$service_observed" ]; then
|
|
echo "Process-death probe never observed the foreground service." >&2
|
|
exit 1
|
|
fi
|
|
|
|
app_pid=$(docker exec "$container" adb shell pidof org.whoneedhelp.mobile.debug | tr -d '\r')
|
|
|
|
case "$app_pid" in
|
|
""|*[!0-9]*)
|
|
echo "Process-death probe could not identify one target process." >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
docker exec "$container" adb logcat -c
|
|
docker exec "$container" adb shell kill -9 "$app_pid"
|
|
|
|
set +e
|
|
wait "$death_runner_pid"
|
|
death_status=$?
|
|
set -e
|
|
death_runner_pid=""
|
|
cat "$output/process-death-results.txt"
|
|
docker exec "$container" adb logcat -d >"$output/process-death-logcat.txt"
|
|
|
|
if ! grep -Eq \
|
|
'Process org\.whoneedhelp\.mobile\.debug .* has died:.*FGS|Crash of app org\.whoneedhelp\.mobile\.debug running instrumentation|Process crashed' \
|
|
"$output/process-death-logcat.txt"; then
|
|
echo "The expected instrumentation process death was not observed." >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf 'Expected killed instrumentation exit status: %s\n' "$death_status" \
|
|
>"$output/process-death-status.txt"
|
|
|
|
attempt=0
|
|
while [ "$attempt" -lt 10 ]; do
|
|
if ! docker exec "$container" adb shell dumpsys activity services \
|
|
org.whoneedhelp.mobile.debug |
|
|
grep -q 'org.whoneedhelp.mobile.TrackingService'; then
|
|
break
|
|
fi
|
|
|
|
attempt=$((attempt + 1))
|
|
sleep 1
|
|
done
|
|
|
|
if docker exec "$container" adb shell dumpsys activity services \
|
|
org.whoneedhelp.mobile.debug |
|
|
grep -q 'org.whoneedhelp.mobile.TrackingService'; then
|
|
echo "START_NOT_STICKY tracking restarted after process death." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if docker exec "$container" adb shell dumpsys notification --noredact |
|
|
grep -q 'Sharing live location'; then
|
|
echo "The tracking notification remained after process death." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Android API $android_api instrumentation and process-death probes passed."
|
|
echo "Evidence: $output"
|