diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2021-01-14 11:35:05 +0100 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2021-01-22 12:18:59 +0100 |
commit | 111f22a4f8c45e2191caeed42de739953a41d4fa (patch) | |
tree | ee743e60eede656d54593002ff31c8ae96b48dc9 /mesonbuild/mtest.py | |
parent | a118cae9d29a172fac9e1fa7874902ab2475551d (diff) | |
download | meson-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.
Diffstat (limited to 'mesonbuild/mtest.py')
-rw-r--r-- | mesonbuild/mtest.py | 111 |
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)) |