From 1f9d163850c43ba85193ef853986c5e96b168c8c Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Fri, 13 Sep 2024 05:23:54 +0100 Subject: [PATCH] gh-116622: Android test script improvements (#124012) * Set Android test script stdout to line-buffered * Print warning logcat messages on stderr * Add a -vv option to display high-volume messages which are rarely useful * Documentation and comment improvements --- Android/README.md | 26 ++++++++++++++------------ Android/android.py | 25 ++++++++++++++++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/Android/README.md b/Android/README.md index bae9150ef05..f9e3f290ef4 100644 --- a/Android/README.md +++ b/Android/README.md @@ -12,8 +12,12 @@ approachable user experience: ## Prerequisites -Export the `ANDROID_HOME` environment variable to point at your Android SDK. If -you don't already have the SDK, here's how to install it: +First, make sure you have all the usual tools and libraries needed to build +Python for your development machine. + +Second, you'll need an Android SDK. If you already have the SDK installed, +export the `ANDROID_HOME` environment variable to point at its location. +Otherwise, here's how to install it: * Download the "Command line tools" from . * Create a directory `android-sdk/cmdline-tools`, and unzip the command line @@ -37,11 +41,6 @@ development tools, which currently means Linux or macOS. This involves doing a cross-build where you use a "build" Python (for your development machine) to help produce a "host" Python for Android. -First, make sure you have all the usual tools and libraries needed to build -Python for your development machine. The only Android tool you need to install -is the command line tools package above: the build script will download the -rest. - The easiest way to do a build is to use the `android.py` script. You can either have it perform the entire build process from start to finish in one step, or you can do it in discrete steps that mirror running `configure` and `make` for @@ -80,12 +79,15 @@ call. For example, if you want a pydebug build that also caches the results from ## Testing -The tests can be run on Linux, macOS, or Windows, although on Windows you'll -have to build the `cross-build/HOST` subdirectory on one of the other platforms -and copy it over. +The test suite can be run on Linux, macOS, or Windows: -The test suite can usually be run on a device with 2 GB of RAM, though for some -configurations or test orders you may need to increase this. As of Android +* On Linux, the emulator needs access to the KVM virtualization interface, and + a DISPLAY environment variable pointing at an X server. +* On Windows, you won't be able to do the build on the same machine, so you'll + have to copy the `cross-build/HOST` directory from somewhere else. + +The test suite can usually be run on a device with 2 GB of RAM, but this is +borderline, so you may need to increase it to 4 GB. As of Android Studio Koala, 2 GB is the default for all emulators, although the user interface may indicate otherwise. The effective setting is `hw.ramSize` in ~/.android/avd/*.avd/hardware-qemu.ini, whereas Android Studio displays the diff --git a/Android/android.py b/Android/android.py index bfa7832a4a8..8696d9eaeca 100755 --- a/Android/android.py +++ b/Android/android.py @@ -259,8 +259,8 @@ def setup_testbed(): f"{temp_dir}/{outer_jar}", "gradle-wrapper.jar"]) -# run_testbed will build the app automatically, but it hides the Gradle output -# by default, so it's useful to have this as a separate command for the buildbot. +# run_testbed will build the app automatically, but it's useful to have this as +# a separate command to allow running the app outside of this script. def build_testbed(context): setup_sdk() setup_testbed() @@ -376,6 +376,8 @@ async def find_pid(serial): shown_error = False while True: try: + # `pidof` requires API level 24 or higher. The level 23 emulator + # includes it, but it doesn't work (it returns all processes). pid = (await async_check_output( adb, "-s", serial, "shell", "pidof", "-s", APP_ID )).strip() @@ -407,6 +409,7 @@ async def logcat_task(context, initial_devices): serial = await wait_for(find_device(context, initial_devices), startup_timeout) pid = await wait_for(find_pid(serial), startup_timeout) + # `--pid` requires API level 24 or higher. args = [adb, "-s", serial, "logcat", "--pid", pid, "--format", "tag"] hidden_output = [] async with async_process( @@ -421,11 +424,15 @@ async def logcat_task(context, initial_devices): # such messages, but other components might. level, message = None, line + # Exclude high-volume messages which are rarely useful. + if context.verbose < 2 and "from python test_syslog" in message: + continue + # Put high-level messages on stderr so they're highlighted in the # buildbot logs. This will include Python's own stderr. stream = ( sys.stderr - if level in ["E", "F"] # ERROR and FATAL (aka ASSERT) + if level in ["W", "E", "F"] # WARNING, ERROR, FATAL (aka ASSERT) else sys.stdout ) @@ -573,8 +580,9 @@ def parse_args(): test = subcommands.add_parser( "test", help="Run the test suite") test.add_argument( - "-v", "--verbose", action="store_true", - help="Show Gradle output, and non-Python logcat messages") + "-v", "--verbose", action="count", default=0, + help="Show Gradle output, and non-Python logcat messages. " + "Use twice to include high-volume messages which are rarely useful.") device_group = test.add_mutually_exclusive_group(required=True) device_group.add_argument( "--connected", metavar="SERIAL", help="Run on a connected device. " @@ -591,6 +599,13 @@ def parse_args(): def main(): install_signal_handler() + + # Under the buildbot, stdout is not a TTY, but we must still flush after + # every line to make sure our output appears in the correct order relative + # to the output of our subprocesses. + for stream in [sys.stdout, sys.stderr]: + stream.reconfigure(line_buffering=True) + context = parse_args() dispatch = {"configure-build": configure_build_python, "make-build": make_build_python,