Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[3.14] gh-144415: Android testbed fixes (GH-142912)#144416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall 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 deletionsAndroid/android.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
fromasyncioimportwait_for
fromcontextlibimportasynccontextmanager
fromdatetimeimportdatetime,timezone
fromenumimportIntEnum,auto
fromglobimportglob
fromos.pathimportabspath,basename,relpath
frompathlibimportPath
Expand DownExpand Up@@ -61,6 +62,19 @@
hidden_output= []


# Based on android/log.h in the NDK.
classLogPriority(IntEnum):
UNKNOWN=0
DEFAULT=auto()
VERBOSE=auto()
DEBUG=auto()
INFO=auto()
WARN=auto()
ERROR=auto()
FATAL=auto()
SILENT=auto()


deflog_verbose(context,line,stream=sys.stdout):
ifcontext.verbose:
stream.write(line)
Expand DownExpand Up@@ -505,47 +519,47 @@ async def logcat_task(context, initial_devices):
pid=awaitwait_for(find_pid(serial),startup_timeout)

# `--pid` requires API level 24 or higher.
args= [adb,"-s",serial,"logcat","--pid",pid,"--format","tag"]
#
# `--binary` mode is used in order to detect which messages end with a
# newline, which most of the other modes don't indicate (except `--format
# long`). For example, every time pytest runs a test, it prints a "." and
# flushes the stream. Each "." becomes a separate log message, but we should
# show them all on the same line.
args= [adb,"-s",serial,"logcat","--pid",pid,"--binary"]
logcat_started=False
asyncwithasync_process(
*args,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,
*args,stdout=subprocess.PIPE,stderr=None
)asprocess:
whileline:= (awaitprocess.stdout.readline()).decode(*DECODE_ARGS):
ifmatch:=re.fullmatch(r"([A-Z])/(.*)",line,re.DOTALL):
whileTrue:
try:
priority,tag,message=awaitread_logcat(process.stdout)
logcat_started=True
level,message=match.groups()
else:
# If the regex doesn't match, this is either a logcat startup
# error, or the second or subsequent line of a multi-line
# message. Python won't produce multi-line messages, but other
# components might.
level,message=None,line
exceptasyncio.IncompleteReadError:
break

# Exclude high-volume messages which are rarely useful.
ifcontext.verbose<2and"from python test_syslog"inmessage:
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
iflevelin ["W","E","F"]# WARNING, ERROR, FATAL (aka ASSERT)
elsesys.stdout
)

# To simplify automated processing of the output, e.g. a buildbot
# posting a failure notice on a GitHub PR, we strip the level and
# tag indicators from Python's stdout and stderr.
forprefixin ["python.stdout: ","python.stderr: "]:
ifmessage.startswith(prefix):
globalpython_started
python_started=True
stream.write(message.removeprefix(prefix))
break
stream=sys.stderrifpriority>=LogPriority.WARNelsesys.stdout

# The app's stdout and stderr should be passed through transparently
# to our own corresponding streams.
iftagin ["python.stdout","python.stderr"]:
globalpython_started
python_started=True
stream.write(message)
stream.flush()
else:
# Non-Python messages add a lot of noise, but they may
# sometimes help explain a failure.
log_verbose(context,line,stream)
# sometimes help explain a failure. Format them in the same way
# as `logcat --format tag`.
formatted=f"{priority.name[0]}/{tag}:{message}"
ifnotformatted.endswith("\n"):
formatted+="\n"
log_verbose(context,formatted,stream)

# If the device disconnects while logcat is running, which always
# happens in --managed mode, some versions of adb return non-zero.
Expand All@@ -556,6 +570,44 @@ async def logcat_task(context, initial_devices):
raiseCalledProcessError(status,args)


# Read one binary log message from the given StreamReader. The message format is
# described at https://android.stackexchange.com/a/74660. All supported versions
# of Android use format version 2 or later.
asyncdefread_logcat(stream):
asyncdefread_bytes(size):
returnawaitstream.readexactly(size)

asyncdefread_int(size):
returnint.from_bytes(awaitread_bytes(size),"little")

payload_len=awaitread_int(2)
ifpayload_len<2:
# 1 byte for priority, 1 byte for null terminator of tag.
raiseValueError(f"payload length{payload_len} is too short")

header_len=awaitread_int(2)
ifheader_len<4:
raiseValueError(f"header length{header_len} is too short")
awaitread_bytes(header_len-4)# Ignore other header fields.

priority_int=awaitread_int(1)
try:
priority=LogPriority(priority_int)
exceptValueError:
priority=LogPriority.UNKNOWN

payload_fields= (awaitread_bytes(payload_len-1)).split(b"\0")
iflen(payload_fields)<2:
raiseValueError(
f"payload{payload!r} does not contain at least 2 "
f"null-separated fields"
)
tag,message,*_= [
field.decode(*DECODE_ARGS)forfieldinpayload_fields
]
returnpriority,tag,message


defstop_app(serial):
run([adb,"-s",serial,"shell","am","force-stop",APP_ID],log=False)

Expand Down
24 changes: 17 additions & 7 deletionsAndroid/testbed/app/build.gradle.kts
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand 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 DownExpand 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
View file
Open in desktop
Original file line numberDiff line numberDiff 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 deletionsLib/_android_support.py
View file
Open in desktop
Original file line numberDiff line numberDiff 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.
ifmessage.startswith(b"\n"):
message=b" "+message

withself._lock:
now=time()
self._bucket_level+= (
Expand Down
41 changes: 20 additions & 21 deletionsLib/test/test_android.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
importio
importplatform
importqueue
importre
importsubprocess
Expand All@@ -17,8 +16,6 @@
ifsys.platform!="android":
raiseunittest.SkipTest("Android-specific")

api_level=platform.android_ver().api_level

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

Expand DownExpand 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.
withself.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 DownExpand 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"]ifapi_level<30else ["x"])
write(b"\na\n", ["","a"]ifapi_level<30else ["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
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
The Android testbed now distinguishes between stdout/stderr messages which
were triggered by a newline, and those triggered by a manual call to
``flush``. This fixes logging of progress indicators and similar content.
Loading

[8]ページ先頭

©2009-2026 Movatter.jp