aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2021-01-14 11:35:05 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2021-01-22 12:18:59 +0100
commit111f22a4f8c45e2191caeed42de739953a41d4fa (patch)
treeee743e60eede656d54593002ff31c8ae96b48dc9
parenta118cae9d29a172fac9e1fa7874902ab2475551d (diff)
downloadmeson-111f22a4f8c45e2191caeed42de739953a41d4fa.zip
meson-111f22a4f8c45e2191caeed42de739953a41d4fa.tar.gz
meson-111f22a4f8c45e2191caeed42de739953a41d4fa.tar.bz2
mtest: make log output more suitable for console
Right now the same code is used to print the logs for both the console and the text log. Differentiating them lets the important bits of the console output stand out, and makes the console output a bit more readable.
-rw-r--r--mesonbuild/mtest.py111
1 files changed, 74 insertions, 37 deletions
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 2dcc27d..dd03515 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -154,6 +154,15 @@ def join_lines(a: str, b: str) -> str:
return a
return a + '\n' + b
+def dashes(s: str, dash: str, cols: int) -> str:
+ if not s:
+ return dash * cols
+ s = ' ' + s + ' '
+ width = uniwidth(s)
+ first = (cols - width) // 2
+ s = dash * first + s
+ return s + dash * (cols - first - width)
+
def returncode_to_status(retcode: int) -> str:
# Note: We can't use `os.WIFSIGNALED(result.returncode)` and the related
# functions here because the status returned by subprocess is munged. It
@@ -241,6 +250,9 @@ class TestResult(enum.Enum):
result_str = '{res:{reslen}}'.format(res=self.value, reslen=self.maxlen())
return self.colorize(result_str).get_text(colorize)
+ def get_command_marker(self) -> str:
+ return str(self.colorize('>>> '))
+
TYPE_TAPResult = T.Union['TAPParser.Test', 'TAPParser.Error', 'TAPParser.Version', 'TAPParser.Plan', 'TAPParser.Bailout']
@@ -452,6 +464,9 @@ class ConsoleLogger(TestLogger):
SPINNER = "\U0001f311\U0001f312\U0001f313\U0001f314" + \
"\U0001f315\U0001f316\U0001f317\U0001f318"
+ SCISSORS = "\u2700 "
+ HLINE = "\u2015"
+
def __init__(self) -> None:
self.update = asyncio.Event()
self.running_tests = OrderedSet() # type: OrderedSet['TestRun']
@@ -464,6 +479,20 @@ class ConsoleLogger(TestLogger):
self.test_count = 0
self.started_tests = 0
self.spinner_index = 0
+ try:
+ self.cols, _ = os.get_terminal_size(1)
+ self.is_tty = True
+ except OSError:
+ self.cols = 80
+ self.is_tty = False
+
+ self.output_start = dashes(self.SCISSORS, self.HLINE, self.cols - 2)
+ self.output_end = dashes('', self.HLINE, self.cols - 2)
+ try:
+ self.output_start.encode(sys.stdout.encoding or 'ascii')
+ except UnicodeEncodeError:
+ self.output_start = dashes('8<', '-', self.cols - 2)
+ self.output_end = dashes('', '-', self.cols - 2)
def flush(self) -> None:
if self.should_erase_line:
@@ -504,14 +533,6 @@ class ConsoleLogger(TestLogger):
left=left, right=right)
self.print_progress(line)
- @staticmethod
- def is_tty() -> bool:
- try:
- _, _ = os.get_terminal_size(1)
- return True
- except OSError:
- return False
-
def start(self, harness: 'TestHarness') -> None:
async def report_progress() -> None:
loop = asyncio.get_event_loop()
@@ -544,8 +565,9 @@ class ConsoleLogger(TestLogger):
self.flush()
self.test_count = harness.test_count
+ self.cols = max(self.cols, harness.max_left_width + 30)
- if self.is_tty() and not harness.need_console:
+ if self.is_tty and not harness.need_console:
# Account for "[aa-bb/cc] OO " in the progress report
self.max_left_width = 3 * len(str(self.test_count)) + 8
self.progress_task = asyncio.ensure_future(report_progress())
@@ -557,19 +579,23 @@ class ConsoleLogger(TestLogger):
self.request_update()
def shorten_log(self, result: 'TestRun') -> str:
- log = result.get_log()
+ log = result.get_log(mlog.colorize_console())
lines = log.splitlines()
- if len(lines) < 103:
+ if len(lines) < 100:
return log
else:
- log = '\n'.join(lines[:2])
- log += '\n--- Listing only the last 100 lines from a long log. ---\n'
- log += lines[2] + '\n'
- log += '\n'.join(lines[-100:])
- return log
+ return str(mlog.bold('Listing only the last 100 lines from a long log.\n')) + '\n'.join(lines[-100:])
def print_log(self, result: 'TestRun', log: str) -> None:
- print_safe(log, end='')
+ cmdline = result.cmdline
+ if not cmdline:
+ print(result.res.get_command_marker() + result.stdo)
+ return
+ print(result.res.get_command_marker() + cmdline)
+ if log:
+ print(self.output_start)
+ print_safe(log, end='')
+ print(self.output_end)
print(flush=True)
def log(self, harness: 'TestHarness', result: 'TestRun') -> None:
@@ -582,6 +608,8 @@ class ConsoleLogger(TestLogger):
self.flush()
print(harness.format(result, mlog.colorize_console(), max_left_width=self.max_left_width),
flush=True)
+ if result.res.is_bad():
+ self.print_log(result, '')
self.request_update()
async def finish(self, harness: 'TestHarness') -> None:
@@ -616,8 +644,14 @@ class TextLogfileBuilder(TestFileLogger):
self.file.write('Inherited environment: {}\n\n'.format(inherit_env))
def log(self, harness: 'TestHarness', result: 'TestRun') -> None:
- self.file.write(harness.format(result, False))
- self.file.write("\n\n" + result.get_log() + "\n")
+ self.file.write(harness.format(result, False) + '\n')
+ cmdline = result.cmdline
+ if cmdline:
+ starttime_str = time.strftime("%H:%M:%S", time.gmtime(result.starttime))
+ self.file.write(starttime_str + ' ' + cmdline + '\n')
+ self.file.write(dashes('output', '-', 78) + '\n')
+ self.file.write(result.get_log())
+ self.file.write(dashes('', '-', 78) + '\n\n')
async def finish(self, harness: 'TestHarness') -> None:
if harness.collected_failures:
@@ -847,24 +881,20 @@ class TestRun:
stdo: T.Optional[str], stde: T.Optional[str]) -> None:
self._complete(returncode, res, stdo, stde)
- def get_log(self) -> str:
- res = '--- command ---\n'
- if self.cmd is None:
- res += 'NONE\n'
- else:
- starttime_str = time.strftime("%H:%M:%S", time.gmtime(self.starttime))
- res += '{} {}\n'.format(starttime_str, self.cmdline)
- if self.stdo:
- res += '--- stdout ---\n'
- res += self.stdo
+ def get_log(self, colorize: bool = False) -> str:
if self.stde:
- if res[-1:] != '\n':
- res += '\n'
- res += '--- stderr ---\n'
+ res = ''
+ if self.stdo:
+ res += mlog.cyan('stdout:').get_text(colorize) + '\n'
+ res += self.stdo
+ if res[-1:] != '\n':
+ res += '\n'
+ res += mlog.cyan('stderr:').get_text(colorize) + '\n'
res += self.stde
- if res[-1:] != '\n':
+ else:
+ res = self.stdo
+ if res and res[-1:] != '\n':
res += '\n'
- res += '-------\n'
return res
@property
@@ -1418,21 +1448,28 @@ class TestHarness:
for l in self.loggers:
l.log(self, result)
+ @property
+ def numlen(self) -> int:
+ return len(str(self.test_count))
+
+ @property
+ def max_left_width(self) -> int:
+ return 2 * self.numlen + 2
+
def format(self, result: TestRun, colorize: bool,
max_left_width: int = 0,
left: T.Optional[str] = None,
right: T.Optional[str] = None) -> str:
- numlen = len(str(self.test_count))
if left is None:
left = '{num:{numlen}}/{testcount} '.format(
- numlen=numlen,
+ numlen=self.numlen,
num=result.num,
testcount=self.test_count)
# A non-default max_left_width lets the logger print more stuff before the
# name, while ensuring that the rightmost columns remain aligned.
- max_left_width = max(max_left_width, 2 * numlen + 2)
+ max_left_width = max(max_left_width, self.max_left_width)
extra_name_width = max_left_width + self.name_max_len + 1 - uniwidth(result.name) - uniwidth(left)
middle = result.name + (' ' * max(1, extra_name_width))