Skip to content

Commit 4745dc3

Browse files
committed
Allow tests that produce no Python output
1 parent 3365f1f commit 4745dc3

File tree

1 file changed

+39
-32
lines changed

1 file changed

+39
-32
lines changed

Android/android.py

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,19 @@
5050
+ (".bat" if os.name == "nt" else "")
5151
)
5252

53-
logcat_started = False
53+
# Whether we've seen any output from Python yet.
54+
python_started = False
55+
56+
# Buffer for verbose output which will be displayed only if a test fails and
57+
# there has been no output from Python.
58+
hidden_output = []
59+
60+
61+
def log_verbose(context, line, stream=sys.stdout):
62+
if context.verbose:
63+
stream.write(line)
64+
else:
65+
hidden_output.append((stream, line))
5466

5567

5668
def delete_glob(pattern):
@@ -453,17 +465,19 @@ async def logcat_task(context, initial_devices):
453465

454466
# `--pid` requires API level 24 or higher.
455467
args = [adb, "-s", serial, "logcat", "--pid", pid, "--format", "tag"]
456-
hidden_output = []
468+
logcat_started = False
457469
async with async_process(
458470
*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
459471
) as process:
460472
while line := (await process.stdout.readline()).decode(*DECODE_ARGS):
461473
if match := re.fullmatch(r"([A-Z])/(.*)", line, re.DOTALL):
474+
logcat_started = True
462475
level, message = match.groups()
463476
else:
464-
# If the regex doesn't match, this is probably the second or
465-
# subsequent line of a multi-line message. Python won't produce
466-
# such messages, but other components might.
477+
# If the regex doesn't match, this is either a logcat startup
478+
# error, or the second or subsequent line of a multi-line
479+
# message. Python won't produce multi-line messages, but other
480+
# components might.
467481
level, message = None, line
468482

469483
# Exclude high-volume messages which are rarely useful.
@@ -483,25 +497,22 @@ async def logcat_task(context, initial_devices):
483497
# tag indicators from Python's stdout and stderr.
484498
for prefix in ["python.stdout: ", "python.stderr: "]:
485499
if message.startswith(prefix):
486-
global logcat_started
487-
logcat_started = True
500+
global python_started
501+
python_started = True
488502
stream.write(message.removeprefix(prefix))
489503
break
490504
else:
491-
if context.verbose:
492-
# Non-Python messages add a lot of noise, but they may
493-
# sometimes help explain a failure.
494-
stream.write(line)
495-
else:
496-
hidden_output.append(line)
505+
# Non-Python messages add a lot of noise, but they may
506+
# sometimes help explain a failure.
507+
log_verbose(context, line, stream)
497508

498509
# If the device disconnects while logcat is running, which always
499510
# happens in --managed mode, some versions of adb return non-zero.
500511
# Distinguish this from a logcat startup error by checking whether we've
501-
# received a message from Python yet.
512+
# received any logcat messages yet.
502513
status = await wait_for(process.wait(), timeout=1)
503514
if status != 0 and not logcat_started:
504-
raise CalledProcessError(status, args, "".join(hidden_output))
515+
raise CalledProcessError(status, args)
505516

506517

507518
def stop_app(serial):
@@ -516,16 +527,6 @@ async def gradle_task(context):
516527
task_prefix = "connected"
517528
env["ANDROID_SERIAL"] = context.connected
518529

519-
hidden_output = []
520-
521-
def log(line):
522-
# Gradle may take several minutes to install SDK packages, so it's worth
523-
# showing those messages even in non-verbose mode.
524-
if context.verbose or line.startswith('Preparing "Install'):
525-
sys.stdout.write(line)
526-
else:
527-
hidden_output.append(line)
528-
529530
if context.command:
530531
mode = "-c"
531532
module = context.command
@@ -550,27 +551,27 @@ def log(line):
550551
]
551552
if context.verbose >= 2:
552553
args.append("--info")
553-
log("> " + join_command(args))
554+
log_verbose(context, f"> {join_command(args)}\n")
554555

555556
try:
556557
async with async_process(
557558
*args, cwd=TESTBED_DIR, env=env,
558559
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
559560
) as process:
560561
while line := (await process.stdout.readline()).decode(*DECODE_ARGS):
561-
log(line)
562+
# Gradle may take several minutes to install SDK packages, so
563+
# it's worth showing those messages even in non-verbose mode.
564+
if line.startswith('Preparing "Install'):
565+
sys.stdout.write(line)
566+
else:
567+
log_verbose(context, line)
562568

563569
status = await wait_for(process.wait(), timeout=1)
564570
if status == 0:
565571
exit(0)
566572
else:
567573
raise CalledProcessError(status, args)
568574
finally:
569-
# If logcat never started, then something has gone badly wrong, so the
570-
# user probably wants to see the Gradle output even in non-verbose mode.
571-
if hidden_output and not logcat_started:
572-
sys.stdout.write("".join(hidden_output))
573-
574575
# Gradle does not stop the tests when interrupted.
575576
if context.connected:
576577
stop_app(context.connected)
@@ -600,6 +601,12 @@ async def run_testbed(context):
600601
except* MySystemExit as e:
601602
raise SystemExit(*e.exceptions[0].args) from None
602603
except* CalledProcessError as e:
604+
# If Python produced no output, then the user probably wants to see the
605+
# verbose output to explain why the test failed.
606+
if not python_started:
607+
for stream, line in hidden_output:
608+
stream.write(line)
609+
603610
# Extract it from the ExceptionGroup so it can be handled by `main`.
604611
raise e.exceptions[0]
605612

0 commit comments

Comments
 (0)