From d76b38eb00d7fd05c8a80493e220cfeddb158ebe Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Tue, 18 Aug 2026 21:28:18 +0200 Subject: [PATCH] Wait for the shell command before sending the next one `shell` closed the pipe without reading it, on the reasoning that "the command runs whether or not anything reads its output". It does not: the pipe is what carries the command's end, and closing it unread returns in a handful of milliseconds whatever the command was still doing. Measured on an emulator, three rounds: a `sleep 2` came back in 5, 12 and 16ms closed, and in 2004, 2035 and 2015ms read. Which is why the first full set off CI has a status bar reading 6:54 with a charging bolt in the battery, in all fifteen locales of both devices. `demo enter` landed - the clock is frozen at the same minute from the first locale to the last, half an hour apart - and the six commands behind it did not. The clock, the battery, the notifications and the radios were all fired into a device already busy with the one before. On this machine they arrive anyway, which is why it was 9:41 here and never there. Reading to the end waits, and it also puts the demo commands in order: `am broadcast` answers `Broadcast completed: result=0` once its receiver has run, so there is something to wait for. The same helper writes `user_rotation`, `accelerometer_rotation`, the night mode and the navigation bar overlay, none of which were being waited on either. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01UBGYtacAAsAiA19UJGvdXb --- .../droid/test/ScreenshotTests.kt | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/app/src/androidTest/java/app/opendocument/droid/test/ScreenshotTests.kt b/app/src/androidTest/java/app/opendocument/droid/test/ScreenshotTests.kt index 87fe1c59e26d..2cadd02cb36d 100644 --- a/app/src/androidTest/java/app/opendocument/droid/test/ScreenshotTests.kt +++ b/app/src/androidTest/java/app/opendocument/droid/test/ScreenshotTests.kt @@ -9,6 +9,7 @@ import android.graphics.Bitmap import android.net.Uri import android.os.Build import android.os.LocaleList +import android.os.ParcelFileDescriptor import android.os.SystemClock import android.view.View import android.view.ViewGroup @@ -753,11 +754,24 @@ class ScreenshotTests { private fun argument(name: String): String? = InstrumentationRegistry.getArguments().getString(name) + /** + * Runs a shell command and waits for it to have finished. + * + * Waits by reading: the pipe reaches its end when the command exits, and closing it unread + * instead returns in a handful of milliseconds whatever the command was doing - five, + * measured, for a `sleep 2`. Which is how the store set came back with a status bar reading + * the real time: `demo enter` landed and the six commands after it, the clock and the + * battery among them, were fired into a device already busy with the next one. `am + * broadcast` answers `Broadcast completed` once its receiver has run, so waiting for it is + * also what puts them in order. + */ private fun shell(command: String) { - InstrumentationRegistry.getInstrumentation() - .uiAutomation - .executeShellCommand(command) - .use { /* the command runs whether or not anything reads its output */ } + val pipe = + InstrumentationRegistry.getInstrumentation() + .uiAutomation + .executeShellCommand(command) + + ParcelFileDescriptor.AutoCloseInputStream(pipe).use { it.readBytes() } } /**