Skip to content

Commit 7dcaa4d

Browse files
committed
Add watchdog and logging for Android UI tests
1 parent c9f4160 commit 7dcaa4d

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

scripts/build-android-app.sh

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,43 @@ elif [ -x "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" ]; then
311311
yes | "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null 2>&1 || true
312312
fi
313313

314+
UI_TEST_TIMEOUT_SECONDS="${UI_TEST_TIMEOUT_SECONDS:-600}"
315+
if ! [[ "$UI_TEST_TIMEOUT_SECONDS" =~ ^[0-9]+$ ]] || [ "$UI_TEST_TIMEOUT_SECONDS" -le 0 ]; then
316+
ba_log "Invalid UI_TEST_TIMEOUT_SECONDS=$UI_TEST_TIMEOUT_SECONDS provided; falling back to 600"
317+
UI_TEST_TIMEOUT_SECONDS=600
318+
fi
319+
320+
GRADLE_TEST_CMD=("./gradlew" "--no-daemon" "test")
321+
if command -v timeout >/dev/null 2>&1; then
322+
ba_log "Running Gradle UI tests with external timeout of ${UI_TEST_TIMEOUT_SECONDS}s"
323+
GRADLE_TEST_WRAPPER=("timeout" "$UI_TEST_TIMEOUT_SECONDS" "${GRADLE_TEST_CMD[@]}")
324+
else
325+
ba_log "timeout command not found; running Gradle UI tests without external watchdog"
326+
GRADLE_TEST_WRAPPER=("${GRADLE_TEST_CMD[@]}")
327+
fi
328+
314329
set +e
315330
(
316331
cd "$GRADLE_PROJECT_DIR"
317-
./gradlew --no-daemon test
332+
"${GRADLE_TEST_WRAPPER[@]}"
318333
)
319334
TEST_EXIT_CODE=$?
320335
set -e
321336

337+
if [ "$TEST_EXIT_CODE" -eq 124 ]; then
338+
ba_log "Gradle UI tests exceeded ${UI_TEST_TIMEOUT_SECONDS}s timeout and were terminated"
339+
fi
340+
if [ "$TEST_EXIT_CODE" -ne 0 ]; then
341+
ba_log "Gradle UI tests exited with status $TEST_EXIT_CODE"
342+
TEST_RESULTS_DIR="$APP_MODULE_DIR/build/test-results/testDebugUnitTest"
343+
if [ -d "$TEST_RESULTS_DIR" ]; then
344+
ba_log "Available XML results under $TEST_RESULTS_DIR:"
345+
find "$TEST_RESULTS_DIR" -maxdepth 1 -type f -name '*.xml' -print | sed 's#^.*/##' | sed 's/^/[build-android-app] /'
346+
else
347+
ba_log "No test result XML directory found at $TEST_RESULTS_DIR"
348+
fi
349+
fi
350+
322351
if [ "$TEST_EXIT_CODE" -eq 0 ]; then
323352
(
324353
cd "$GRADLE_PROJECT_DIR"

scripts/templates/HelloCodenameOneUiTest.java.tmpl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,23 @@ public class @MAIN_NAME@UiTest {
2929

3030
@Test
3131
public void mainFormScreenshotContainsRenderedContent() throws Exception {
32+
log("Starting Robolectric activity for screenshot test");
3233
ActivityController<@MAIN_NAME@Stub> controller = Robolectric.buildActivity(@[email protected]).setup().visible();
3334
try {
35+
log("Waiting for Codename One main form to be displayed");
3436
Form displayed = waitForDisplayedForm(5000L);
3537
assertNotNull("Codename One form should be displayed", displayed);
3638

3739
@MAIN_NAME@Stub activity = controller.get();
40+
log("Captured Codename One form; capturing decor view screenshot");
3841
Bitmap screenshot = captureScreenshot(activity);
3942
assertNotNull("Screenshot capture should succeed", screenshot);
4043
assertTrue("Screenshot width should be positive", screenshot.getWidth() > 0);
4144
assertTrue("Screenshot height should be positive", screenshot.getHeight() > 0);
4245
assertTrue("Screenshot should contain rendered content beyond the background", hasRenderableContent(screenshot));
4346

4447
File screenshotFile = saveScreenshot(screenshot);
48+
log("Screenshot stored at " + screenshotFile.getAbsolutePath());
4549
assertTrue("Screenshot file should exist", screenshotFile.isFile());
4650
assertTrue("Screenshot file should not be empty", screenshotFile.length() > 0L);
4751
} finally {
@@ -51,6 +55,7 @@ public class @MAIN_NAME@UiTest {
5155

5256
private static Form waitForDisplayedForm(long timeoutMillis) throws InterruptedException {
5357
long deadline = SystemClock.uptimeMillis() + timeoutMillis;
58+
long nextLog = SystemClock.uptimeMillis();
5459
while (SystemClock.uptimeMillis() < deadline) {
5560
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
5661
if (Display.isInitialized()) {
@@ -60,6 +65,11 @@ public class @MAIN_NAME@UiTest {
6065
}
6166
}
6267
Thread.sleep(50L);
68+
long now = SystemClock.uptimeMillis();
69+
if (now >= nextLog) {
70+
log("Still waiting for Codename One form; displayInitialized=" + Display.isInitialized());
71+
nextLog = now + 1000L;
72+
}
6373
}
6474
throw new AssertionError("Timed out waiting for Codename One form to be displayed");
6575
}
@@ -117,6 +127,7 @@ public class @MAIN_NAME@UiTest {
117127

118128
private static View waitForDecorViewWithDimensions(@MAIN_NAME@Stub activity, long timeoutMillis) throws InterruptedException {
119129
long deadline = SystemClock.uptimeMillis() + timeoutMillis;
130+
long nextLog = SystemClock.uptimeMillis();
120131
while (SystemClock.uptimeMillis() < deadline) {
121132
View decorView = activity.getWindow().getDecorView();
122133
if (decorView != null) {
@@ -127,6 +138,12 @@ public class @MAIN_NAME@UiTest {
127138
}
128139
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
129140
Thread.sleep(50L);
141+
long now = SystemClock.uptimeMillis();
142+
if (now >= nextLog) {
143+
log("Waiting for decor view layout; width=" + (decorView != null ? decorView.getWidth() : 0)
144+
+ " height=" + (decorView != null ? decorView.getHeight() : 0));
145+
nextLog = now + 1000L;
146+
}
130147
}
131148
throw new AssertionError("Timed out waiting for decor view layout");
132149
}
@@ -141,4 +158,8 @@ public class @MAIN_NAME@UiTest {
141158
view.measure(widthSpec, heightSpec);
142159
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
143160
}
161+
162+
private static void log(String message) {
163+
System.out.println("[@MAIN_NAME@UiTest] " + message);
164+
}
144165
}

0 commit comments

Comments
 (0)