gh-144415: Android testbed fixes by mhsmith · Pull Request #142912 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 80 additions & 28 deletions Android/android.py
24 changes: 17 additions & 7 deletions Android/testbed/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,13 @@ android {
}

// This controls the API level of the maxVersion managed emulator, which is used
// by CI and cibuildwheel. 34 takes up too much disk space (#142289), 35 has
// issues connecting to the internet (#142387), and 36 and later are not
// available as aosp_atd images yet.
targetSdk = 33
// by CI and cibuildwheel.
// * 33 has excessive buffering in the logcat client
// (https://cs.android.com/android/_/android/platform/system/logging/+/d340721894f223327339010df59b0ac514308826).
// * 34 consumes too much disk space on GitHub Actions (#142289).
// * 35 has issues connecting to the internet (#142387).
// * 36 and later are not available as aosp_atd images yet.
targetSdk = 32

versionCode = 1
versionName = "1.0"
Expand Down Expand Up @@ -130,9 +133,10 @@ android {
path("src/main/c/CMakeLists.txt")
}

// Set this property to something non-empty, otherwise it'll use the default
// list, which ignores asset directories beginning with an underscore.
aaptOptions.ignoreAssetsPattern = ".git"
// Set this property to something nonexistent but non-empty. Otherwise it'll use the
// default list, which ignores asset directories beginning with an underscore, and
// maybe also other files required by tests.
aaptOptions.ignoreAssetsPattern = "android-testbed-dont-ignore-anything"

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
Expand Down Expand Up @@ -234,6 +238,12 @@ androidComponents.onVariants { variant ->
from(cwd)
}
}

// A filename ending with .gz will be automatically decompressed
// while building the APK. Avoid this by adding a dash to the end,
// and add an extra dash to any filenames that already end with one.
// This will be undone in MainActivity.kt.
rename(""".*(\.gz|-)""", "$0-")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ class PythonTestRunner(val context: Context) {
continue
}
input.use {
File(targetSubdir, name).outputStream().use { output ->
// Undo the .gz workaround from build.gradle.kts.
val outputName = name.replace(Regex("""(.*)-"""), "$1")
File(targetSubdir, outputName).outputStream().use { output ->
input.copyTo(output)
}
}
Expand Down
7 changes: 7 additions & 0 deletions Lib/_android_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ def write(self, prio, tag, message):
# message.
message = message.replace(b"\x00", b"\xc0\x80")

# On API level 30 and higher, Logcat will strip any number of leading
# newlines. This is visible in all `logcat` modes, even --binary. Work
# around this by adding a leading space, which shouldn't make any
# difference to the log's usability.
if message.startswith(b"\n"):
message = b" " + message

with self._lock:
now = time()
self._bucket_level += (
Expand Down
41 changes: 20 additions & 21 deletions Lib/test/test_android.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import io
import platform
import queue
import re
import subprocess
Expand All @@ -17,8 +16,6 @@
if sys.platform != "android":
raise unittest.SkipTest("Android-specific")

api_level = platform.android_ver().api_level

# (name, level, fileno)
STREAM_INFO = [("stdout", "I", 1), ("stderr", "W", 2)]

Expand Down Expand Up @@ -179,14 +176,18 @@ def write(s, lines=None, *, write_len=None):

# Multi-line messages. Avoid identical consecutive lines, as
# they may activate "chatty" filtering and break the tests.
write("\nx", [""])
#
# Additional spaces will appear in the output where necessary to
# protect leading newlines.
write("\nx", [" "])
write("\na\n", ["x", "a"])
write("\n", [""])
write("\n", [" "])
write("\n\n", [" ", " "])
write("b\n", ["b"])
write("c\n\n", ["c", ""])
write("c\n\n", ["c", " "])
write("d\ne", ["d"])
write("xx", [])
write("f\n\ng", ["exxf", ""])
write("f\n\ng", ["exxf", " "])
write("\n", ["g"])

# Since this is a line-based logging system, line buffering
Expand All @@ -197,15 +198,16 @@ def write(s, lines=None, *, write_len=None):
# However, buffering can be turned off completely if you want a
# flush after every write.
with self.reconfigure(stream, write_through=True):
write("\nx", ["", "x"])
write("\na\n", ["", "a"])
write("\n", [""])
write("\nx", [" ", "x"])
write("\na\n", [" ", "a"])
write("\n", [" "])
write("\n\n", [" ", " "])
write("b\n", ["b"])
write("c\n\n", ["c", ""])
write("c\n\n", ["c", " "])
write("d\ne", ["d", "e"])
write("xx", ["xx"])
write("f\n\ng", ["f", "", "g"])
write("\n", [""])
write("f\n\ng", ["f", " ", "g"])
write("\n", [" "])

# "\r\n" should be translated into "\n".
write("hello\r\n", ["hello"])
Expand Down Expand Up @@ -325,19 +327,16 @@ def write(b, lines=None, *, write_len=None):
# currently use `logcat -v tag`, which shows each line as if it
# was a separate log entry, but strips a single trailing
# newline.
#
# On newer versions of Android, all three of the above tools (or
# maybe Logcat itself) will also strip any number of leading
# newlines.
write(b"\nx", ["", "x"] if api_level < 30 else ["x"])
write(b"\na\n", ["", "a"] if api_level < 30 else ["a"])
write(b"\n", [""])
write(b"\nx", [" ", "x"])
write(b"\na\n", [" ", "a"])
write(b"\n", [" "])
write(b"\n\n", [" ", ""])
write(b"b\n", ["b"])
write(b"c\n\n", ["c", ""])
write(b"d\ne", ["d", "e"])
write(b"xx", ["xx"])
write(b"f\n\ng", ["f", "", "g"])
write(b"\n", [""])
write(b"\n", [" "])

# "\r\n" should be translated into "\n".
write(b"hello\r\n", ["hello"])
Expand Down
Loading