# 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)
