diff options
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)) |