|
| 1 | +# Patchwork - automated patch tracking system |
| 2 | +# Copyright (C) 2023 Stephen Finucane <stephen@that.guru> |
| 3 | +# |
| 4 | +# SPDX-License-Identifier: GPL-2.0-or-later |
| 5 | + |
| 6 | +from unittest import TestResult |
| 7 | +from unittest import TextTestRunner |
| 8 | + |
| 9 | +from django.test.runner import DiscoverRunner |
| 10 | +from termcolor import colored |
| 11 | + |
| 12 | + |
| 13 | +# Based on upstream source |
| 14 | +# https://github.com/python/cpython/blob/v3.11.4/Lib/unittest/runner.py |
| 15 | +class ColourTextTestResult(TestResult): |
| 16 | + def __init__(self, stream, descriptions, verbosity, *, durations=None): |
| 17 | + super().__init__(stream, descriptions, verbosity) |
| 18 | + |
| 19 | + self.stream = stream |
| 20 | + self.descriptions = descriptions |
| 21 | + self.verbosity = verbosity |
| 22 | + |
| 23 | + def startTest(self, test): |
| 24 | + super().startTest(test) |
| 25 | + if self.verbosity > 1: |
| 26 | + self.stream.write(colored(str(test), 'white')) |
| 27 | + self.stream.write(colored(' ... ', 'white')) |
| 28 | + self.stream.flush() |
| 29 | + |
| 30 | + def _reportResult(self, short, long, color): |
| 31 | + if self.verbosity == 1: |
| 32 | + self.stream.write(short) |
| 33 | + else: # > 1 |
| 34 | + self.stream.writeln(colored(long, color, attrs=['bold'])) |
| 35 | + self.stream.flush() |
| 36 | + |
| 37 | + def addSuccess(self, test): |
| 38 | + super().addSuccess(test) |
| 39 | + self._reportResult('.', 'ok', 'green') |
| 40 | + |
| 41 | + def addError(self, test, err): |
| 42 | + super().addError(test, err) |
| 43 | + self._reportResult('E', 'ERROR', 'red') |
| 44 | + |
| 45 | + def addFailure(self, test, err): |
| 46 | + super().addFailure(test, err) |
| 47 | + self._reportResult('F', 'FAIL', 'yellow') |
| 48 | + |
| 49 | + def addSkip(self, test, reason): |
| 50 | + super().addSkip(test, reason) |
| 51 | + self._reportResult('s', f'skipped {reason!r}', 'white') |
| 52 | + |
| 53 | + def addExpectedFailure(self, test, err): |
| 54 | + super().addExpectedFailure(test, err) |
| 55 | + self._reportResult('s', 'expected failure', 'blue') |
| 56 | + |
| 57 | + def addUnexpectedSuccess(self, test): |
| 58 | + super().addUnexpectedSuccess(test) |
| 59 | + self._reportResult('s', 'unexpected success', 'red') |
| 60 | + |
| 61 | + def printErrors(self): |
| 62 | + self.stream.writeln() |
| 63 | + self.stream.flush() |
| 64 | + |
| 65 | + self.printErrorList('ERROR', self.errors) |
| 66 | + self.printErrorList('FAIL', self.failures) |
| 67 | + |
| 68 | + unexpectedSuccesses = getattr(self, 'unexpectedSuccesses', ()) |
| 69 | + if unexpectedSuccesses: |
| 70 | + self.stream.writeln('=' * 70) |
| 71 | + for test in unexpectedSuccesses: |
| 72 | + self.stream.writeln(f"UNEXPECTED SUCCESS: {str(test)}") |
| 73 | + self.stream.flush() |
| 74 | + |
| 75 | + def printErrorList(self, flavour, errors): |
| 76 | + for test, err in errors: |
| 77 | + self.stream.writeln('=' * 70) |
| 78 | + self.stream.writeln(f'{flavour}: {str(test)}') |
| 79 | + self.stream.writeln('-' * 70) |
| 80 | + self.stream.writeln(str(err)) |
| 81 | + self.stream.flush() |
| 82 | + |
| 83 | + |
| 84 | +class ColourTextTestRunner(TextTestRunner): |
| 85 | + resultclass = ColourTextTestResult |
| 86 | + |
| 87 | + |
| 88 | +class PatchworkTestRunner(DiscoverRunner): |
| 89 | + test_runner = ColourTextTestRunner |
0 commit comments