blob: 0daa6751440aba507ab12674996152ef4bdde957 [file]
# Copyright 2025 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
import shutil
from unittest import TextTestResult
from color_runner import ColorTextResult, ColorTextRunner
from tools.colored_logger import CYAN, GREEN, RED, with_color
def clearline(stream):
stream.write('\r\033[K')
stream.flush()
def term_width():
return shutil.get_terminal_size().columns
class SingleLineTestResult(ColorTextResult):
"""Like standard TextTestResult but uses ANSI escape codes for color output and reusing a single line on the terminal."""
# Reserve at least 20 columns for the status message
min_status_msg = 20
status_limit = None
def _write_status(self, _test, status):
# Add some color to the status message
if status == 'ok':
color = GREEN
elif status.isupper():
color = RED
# Use a newline when a test fails, so you can see a list of failures while
# the other tests are still running
status += '\n'
else:
color = CYAN
status = status[:self.status_limit]
line = f'{with_color(color, status)}'
self.stream.write(line)
self.stream.flush()
def startTest(self, test):
self.showAll = False
super().startTest(test)
self.showAll = True
clearline(self.stream)
prefix_len = self.writeProgressPrefix()
total_width = term_width()
max_desc = total_width - prefix_len - len(' ... ') - self.min_status_msg
desc = str(test)[:max_desc]
self.stream.write(desc)
self.stream.write(' ... ')
self.stream.flush()
# Calculate the remaining terminal width available for the _write_status
# message. This will never be lower than `self.max_status_msg`
self.status_limit = total_width - prefix_len - len(desc) - len(' ... ')
def printErrors(self):
# All tests have been run at this point so print a final newline
# to end out status line
self.stream.write('\n')
super().printErrors()
# Override addExpectedFailure and addUnexpectedSuccess since, for some reason
# these methods in TextTestResult do not use `_write_status` like the other ones.
# TODO(sbc): Send a patch to upstream python to sue `_write_status` in TextTestResult.
def addExpectedFailure(self, test, err):
super(TextTestResult, self).addExpectedFailure(test, err)
if self.showAll:
self._write_status(test, "expected failure")
self.stream.flush()
elif self.dots:
self.stream.write("x")
self.stream.flush()
def addUnexpectedSuccess(self, test):
super(TextTestResult, self).addUnexpectedSuccess(test)
if self.showAll:
self._write_status(test, "unexpected success")
self.stream.flush()
elif self.dots:
self.stream.write("u")
self.stream.flush()
class SingleLineTestRunner(ColorTextRunner):
"""Subclass of TextTestResult that uses SingleLineTestResult."""
resultclass = SingleLineTestResult # type: ignore
def __init__(self, *args, **kwargs):
super().__init__(*args, buffer=True, **kwargs)