Skip to content

Commit f86eb7c

Browse files
extract e2e diagnostics into run-e2e.sh
1 parent c6aee2b commit f86eb7c

2 files changed

Lines changed: 104 additions & 98 deletions

File tree

.github/scripts/run-e2e.sh

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env bash
2+
# .github/scripts/run-e2e.sh
3+
#
4+
# Runs the In-App Message E2E test under ReactiveCircus/android-emulator-runner
5+
# and captures diagnostics that survive the action's emulator-kill on exit.
6+
#
7+
# Why this is an external script and not inline YAML:
8+
# The action runs each line of `script:` in a fresh `/bin/sh -c`, so cross-line
9+
# variables and shell functions don't survive. We need a single bash process for
10+
# the trap + variable + function semantics.
11+
#
12+
# Inputs (env, all set by the workflow step):
13+
# ITERABLE_API_KEY — set as buildConfigField at runtime; not echoed.
14+
# ITERABLE_SERVER_API_KEY — set as buildConfigField at runtime; not echoed.
15+
# ITERABLE_TEST_USER_EMAIL — used by tests; not echoed (length only).
16+
# GITHUB_WORKSPACE — set by the runner; root for diagnostics output.
17+
#
18+
# Outputs:
19+
# $GITHUB_WORKSPACE/integration-tests/build/diagnostics/
20+
# hierarchy.xml — UiAutomator dump at the moment of test exit
21+
# screenshot.png — device screenshot at the moment of test exit
22+
# logcat.txt — full device logcat from start of test invocation
23+
#
24+
# Exit code:
25+
# The gradle test task's exit code, propagated.
26+
#
27+
# This script writes nothing outside $GITHUB_WORKSPACE/integration-tests/build/.
28+
29+
set -uo pipefail
30+
31+
readonly TEST_CLASS="${TEST_CLASS:-com.iterable.integration.tests.InAppMessageIntegrationTest#testInAppMessageMVP}"
32+
readonly DIAG_DIR="${GITHUB_WORKSPACE:?GITHUB_WORKSPACE must be set}/integration-tests/build/diagnostics"
33+
readonly TEST_PACKAGE="com.iterable.integration.tests"
34+
35+
mkdir -p "$DIAG_DIR"
36+
37+
log() { printf '\033[1;34m[e2e]\033[0m %s\n' "$*"; }
38+
39+
log "Running E2E test: $TEST_CLASS"
40+
log "Diagnostics will be written to: $DIAG_DIR"
41+
42+
# Sanity-check env: don't echo secret values, only their lengths. The workflow's
43+
# env: block guarantees these vars exist; ${#VAR} of an empty string is 0.
44+
log "ITERABLE_API_KEY length: ${#ITERABLE_API_KEY}"
45+
log "ITERABLE_SERVER_API_KEY length: ${#ITERABLE_SERVER_API_KEY}"
46+
log "ITERABLE_TEST_USER_EMAIL length: ${#ITERABLE_TEST_USER_EMAIL}"
47+
48+
# Grant permissions; ignore failures (the package may not be installed yet,
49+
# in which case AGP will install + auto-grant during the test step).
50+
for perm in POST_NOTIFICATIONS INTERNET ACCESS_NETWORK_STATE WAKE_LOCK; do
51+
adb shell pm grant "$TEST_PACKAGE" "android.permission.$perm" >/dev/null 2>&1 || true
52+
done
53+
54+
# Stream full logcat to the workspace so the artifact upload always has it.
55+
adb logcat -c >/dev/null 2>&1 || true
56+
adb logcat > "$DIAG_DIR/logcat.txt" &
57+
LOGCAT_PID=$!
58+
59+
# Capture diagnostics that depend on a live emulator. Called from EXIT trap so
60+
# we always run, whether tests passed, failed, or the runner timed out.
61+
capture_post_test() {
62+
log "Capturing post-test diagnostics..."
63+
64+
# Stop logcat first so the file isn't being appended to mid-copy.
65+
if [[ -n "${LOGCAT_PID:-}" ]]; then
66+
kill "$LOGCAT_PID" 2>/dev/null || true
67+
wait "$LOGCAT_PID" 2>/dev/null || true
68+
fi
69+
70+
# UiAutomator hierarchy — answers "what was UiAutomator looking at?"
71+
if adb shell uiautomator dump /sdcard/hierarchy.xml >/dev/null 2>&1; then
72+
adb pull /sdcard/hierarchy.xml "$DIAG_DIR/hierarchy.xml" >/dev/null 2>&1 || true
73+
adb shell rm -f /sdcard/hierarchy.xml >/dev/null 2>&1 || true
74+
fi
75+
76+
# Screenshot — answers "what was actually on the screen?"
77+
if adb shell screencap -p /sdcard/screenshot.png >/dev/null 2>&1; then
78+
adb pull /sdcard/screenshot.png "$DIAG_DIR/screenshot.png" >/dev/null 2>&1 || true
79+
adb shell rm -f /sdcard/screenshot.png >/dev/null 2>&1 || true
80+
fi
81+
82+
log "Diagnostics captured:"
83+
ls -la "$DIAG_DIR" || true
84+
}
85+
trap capture_post_test EXIT
86+
87+
# Run the test. Don't `set -e`; we want to capture diagnostics on failure and
88+
# propagate the original exit code at the end.
89+
gradle_exit=0
90+
./gradlew :integration-tests:connectedDebugAndroidTest \
91+
-Pandroid.testInstrumentationRunnerArguments.class="$TEST_CLASS" \
92+
--stacktrace --no-daemon || gradle_exit=$?
93+
94+
if [[ "$gradle_exit" -ne 0 ]]; then
95+
log "::error::Gradle test task failed with exit code $gradle_exit — see e2e-diagnostics-api artifact"
96+
fi
97+
98+
# capture_post_test runs via EXIT trap; just propagate the exit code.
99+
exit "$gradle_exit"

.github/workflows/inapp-e2e-tests.yml

Lines changed: 5 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -87,104 +87,11 @@ jobs:
8787
# Clean + start adb after platform-tools exist (avoids tcp:5037 noise)
8888
adb kill-server >/dev/null 2>&1 || true
8989
adb start-server
90-
script: |
91-
# SDK-170 OBSERVABILITY: capture diagnostics that survive the action exit.
92-
# The android-emulator-runner kills the emulator on its way out, so any
93-
# adb-dependent capture MUST happen before this script returns. We write
94-
# everything under $GITHUB_WORKSPACE so a later step can upload it.
95-
DIAG_DIR="$GITHUB_WORKSPACE/integration-tests/build/diagnostics"
96-
mkdir -p "$DIAG_DIR"
97-
98-
capture_diag() {
99-
local label="$1"
100-
local prefix="$2"
101-
echo "=== diag capture: $label ==="
102-
{
103-
echo "# label: $label"
104-
echo "# t: $(date -u +%FT%TZ)"
105-
echo
106-
echo "## adb devices"
107-
adb devices 2>&1
108-
echo
109-
echo "## top activities"
110-
adb shell dumpsys activity activities 2>&1 | head -200
111-
echo
112-
echo "## window state (focused app + visible windows)"
113-
adb shell dumpsys window 2>&1 | head -200
114-
echo
115-
echo "## connectivity (active default network, link addresses, dns)"
116-
adb shell dumpsys connectivity 2>&1 | head -120
117-
echo
118-
echo "## meminfo (system_server + integration tests)"
119-
adb shell dumpsys meminfo 2>&1 | head -80
120-
echo
121-
echo "## installed iterable packages"
122-
adb shell pm list packages 2>&1 | grep iterable || true
123-
echo
124-
echo "## boot props"
125-
adb shell getprop sys.boot_completed dev.bootcomplete init.svc.netd net.dns1 ro.build.version.release ro.build.version.sdk 2>&1 | head -10
126-
} > "$DIAG_DIR/${prefix}-state.txt" 2>&1
127-
128-
# UiAutomator hierarchy and screenshot — the smoking gun for "button not found"
129-
adb shell uiautomator dump /sdcard/hierarchy.xml >/dev/null 2>&1 || true
130-
adb pull /sdcard/hierarchy.xml "$DIAG_DIR/${prefix}-hierarchy.xml" >/dev/null 2>&1 || echo "(no hierarchy)"
131-
adb shell screencap -p /sdcard/screenshot.png >/dev/null 2>&1 || true
132-
adb pull /sdcard/screenshot.png "$DIAG_DIR/${prefix}-screenshot.png" >/dev/null 2>&1 || echo "(no screenshot)"
133-
}
134-
135-
echo "Emulator is ready! Running tests..."
136-
echo "Setting up permissions..."
137-
adb shell pm grant com.iterable.integration.tests android.permission.POST_NOTIFICATIONS || true
138-
adb shell pm grant com.iterable.integration.tests android.permission.INTERNET || true
139-
adb shell pm grant com.iterable.integration.tests android.permission.ACCESS_NETWORK_STATE || true
140-
adb shell pm grant com.iterable.integration.tests android.permission.WAKE_LOCK || true
141-
142-
echo "Running In-App Message MVP test..."
143-
echo "Debug: Checking if APKs are ready..."
144-
ls -la integration-tests/build/outputs/apk/ || echo "APK directory not found"
145-
146-
echo "Debug: Verifying API keys are set..."
147-
echo "ITERABLE_API_KEY length: ${#ITERABLE_API_KEY}"
148-
echo "ITERABLE_SERVER_API_KEY length: ${#ITERABLE_SERVER_API_KEY}"
149-
echo "ITERABLE_TEST_USER_EMAIL: $ITERABLE_TEST_USER_EMAIL"
150-
151-
# Snapshot pre-test device state. If tests fail because the activity never
152-
# foregrounded, this captures what was on screen *before* we even ran.
153-
capture_diag "pre-test" "00-pre-test"
154-
155-
# Stream full logcat to the workspace so we get an artifact even on success.
156-
adb logcat -c >/dev/null 2>&1 || true
157-
adb logcat > "$DIAG_DIR/10-logcat-full.txt" &
158-
LOGCAT_PID=$!
159-
160-
GRADLE_EXIT=0
161-
./gradlew :integration-tests:connectedDebugAndroidTest \
162-
-Pandroid.testInstrumentationRunnerArguments.class=com.iterable.integration.tests.InAppMessageIntegrationTest#testInAppMessageMVP \
163-
--stacktrace --no-daemon || GRADLE_EXIT=$?
164-
165-
# Stop logcat ASAP so post-test capture isn't entangled with it.
166-
kill $LOGCAT_PID 2>/dev/null || true
167-
wait $LOGCAT_PID 2>/dev/null || true
168-
169-
# Always capture post-test state — useful on success too (proves the test
170-
# really did exercise the SDK and surface campaigns).
171-
capture_diag "post-test" "20-post-test"
172-
173-
# Filtered logcat for fast triage; full logcat is also uploaded.
174-
grep -E 'IterableApi|IterableRequest|IterableInApp|IterableEmbedded|IntegrationMainActivity|BaseIntegrationTest|InAppMessageIntegrationTest|PushNotificationIntegrationTest|EmbeddedMessageIntegrationTest|DeepLinkIntegrationTest|FATAL|AndroidRuntime|ANR' \
175-
"$DIAG_DIR/10-logcat-full.txt" > "$DIAG_DIR/11-logcat-filtered.txt" 2>/dev/null || true
176-
177-
echo
178-
echo "=== diagnostics summary ==="
179-
ls -la "$DIAG_DIR"
180-
echo
181-
echo "Last 50 logcat lines (full file uploaded as artifact):"
182-
tail -50 "$DIAG_DIR/10-logcat-full.txt" || true
183-
184-
if [ "$GRADLE_EXIT" != "0" ]; then
185-
echo "::error::Test gradle task failed with exit code $GRADLE_EXIT — see e2e-diagnostics-api-${{ matrix.api-level }} artifact"
186-
exit "$GRADLE_EXIT"
187-
fi
90+
# The android-emulator-runner action runs each line of an inline `script:`
91+
# in a fresh `/bin/sh -c`, so cross-line variables and bash functions don't
92+
# survive. Externalise the whole thing to a single bash file that runs in
93+
# one process — see .github/scripts/run-e2e.sh for the actual logic.
94+
script: bash "$GITHUB_WORKSPACE/.github/scripts/run-e2e.sh"
18895
env:
18996
ITERABLE_API_KEY: ${{ secrets.BCIT_ITERABLE_API_KEY }}
19097
ITERABLE_SERVER_API_KEY: ${{ secrets.BCIT_ITERABLE_SERVER_API_KEY }}

0 commit comments

Comments
 (0)