def test_color_bar_full(): """Assert that a complete 16-color bar draws properly.""" out = StringIO() term = MockTerminal(kind='xterm-256color', stream=out, force_styling=True) bar = ProgressBar(28, term) bar.update('HI', 28) eq_(out.getvalue(), u'\x1b7\x1b[25d\x1b[1mHI ' '\x1b(B\x1b[m \x1b[100m \x1b(B\x1b[m' '\x1b[47m\x1b(B\x1b[m\x1b8')
def test_monochrome_bar(): """Assert that the black-and-white bar draws properly when < 16 colors are available.""" out = StringIO() term = MonochromeTerminal(kind='xterm', stream=out, force_styling=True) assert term.number_of_colors < 16 bar = ProgressBar(28, term) bar.update('HI', 14) eq_(out.getvalue(), u'\x1b7\x1b[25d\x1b[1mHI ' '\x1b(B\x1b[m \x1b[7m \x1b(B\x1b[m' '_______\x1b8')
def test_color_bar_full(): """Assert that a complete 16-color bar draws properly.""" out = StringIO() term = MockTerminal(kind='xterm-256color', stream=out, force_styling=True) bar = ProgressBar(28, term) bar.update('HI', 28) eq_( out.getvalue(), u'\x1b7\x1b[25d\x1b[1mHI ' '\x1b(B\x1b[m \x1b[100m \x1b(B\x1b[m' '\x1b[47m\x1b(B\x1b[m\x1b8')
def test_monochrome_bar(): """Assert that the black-and-white bar draws properly when < 16 colors are available.""" out = StringIO() term = MonochromeTerminal(kind='xterm', stream=out, force_styling=True) assert term.number_of_colors < 16 bar = ProgressBar(28, term) bar.update('HI', 14) eq_( out.getvalue(), u'\x1b7\x1b[25d\x1b[1mHI ' '\x1b(B\x1b[m \x1b[7m \x1b(B\x1b[m' '_______\x1b8')
def test_color_bar_full(): """Assert that a complete 16-color bar draws properly.""" out = StringIO() term = MockTerminal(kind='xterm-256color', stream=out, force_styling=True) bar = ProgressBar(28, term) bar.update('HI', 28) eq_(out.getvalue(), ''.join([term.save, term.move(24, 0), term.bold('HI '), ' ', term.on_color(8)(' '), term.on_color(7)(''), term.restore]))
def test_monochrome_bar(): """Assert that the black-and-white bar draws properly when < 16 colors are available.""" out = StringIO() term = MonochromeTerminal(kind='xterm', stream=out, force_styling=True) assert term.number_of_colors < 16 bar = ProgressBar(28, term) bar.update('HI', 14) eq_(out.getvalue(), ''.join([term.save, term.move(24, 0), term.bold('HI '), ' ', term.reverse(' '), '_______', term.restore]))
class ProgressiveResult(TextTestResult): """Test result which updates a progress bar instead of printing dots Nose's ResultProxy will wrap it, and other plugins can still print stuff---but without smashing into my progress bar, care of my Plugin's stderr/out wrapping. """ def __init__(self, cwd, totalTests, stream, showAdvisories, config=None): super(ProgressiveResult, self).__init__(stream, None, 0, config=config) self._cwd = cwd self._codes = self._terminalCodes(stream) # 1 in case test counting failed and returned 0 self.bar = ProgressBar(stream, totalTests or 1, self._codes) # Declare errorclass-savviness so the errorclass plugin doesn't # monkeypatch half my methods away: self.errorClasses = {} self._showAdvisories = showAdvisories def _terminalCodes(self, stream): """Return a hash of termcap codes and values for the terminal `stream`. If `stream` is not a terminal, return empty values so you can pipe this to a file without making a mess of it. (I'm not sure why you would want to do that; this is mostly in place to make the Progressive's test suite run.) """ capabilities = ['bold', 'sc', 'rc', 'sgr0', 'el'] if hasattr(stream, 'fileno') and isatty(stream.fileno()): # Explicit args make setupterm() work even when -s is passed: setupterm(None, stream.fileno()) # so things like tigetstr() work codes = dict((x, tigetstr(x)) for x in capabilities) cup = tigetstr('cup') codes['cup'] = lambda line, column: tparm(cup, line, column) else: # If you're crazy enough to pipe this to a file or something, don't # output terminal codes: codes = defaultdict(lambda: '', cup=lambda line, column: '') return codes def startTest(self, test): """Update the progress bar.""" super(ProgressiveResult, self).startTest(test) self.bar.update(test, self.testsRun) def _printError(self, kind, err, test, isFailure=True): """Output a human-readable error report to the stream. kind -- the (string) type of incident the precipitated this call err -- exc_info()-style traceback triple test -- the test that precipitated this call """ if isFailure or self._showAdvisories: # Don't bind third item to a local var; that can create circular # refs which are expensive to collect. See the sys.exc_info() docs. exception_type, exception_value = err[:2] extracted_tb = extract_tb(err[2]) formatted_traceback = ''.join(format_list(extracted_tb)) # TODO: Canonicalize the path to remove /kitsune/../kitsune # nonsense. Don't relativize, though, as that hurts the ability to # paste into running editors. writeln = self.stream.writeln write = self.stream.write with self.bar.dodging(): writeln('\n' + (self._codes['bold'] if isFailure else '') + '%s: %s' % (kind, nose_selector(test))) if isFailure: # Then show traceback # File name and line num in a format vi can take: try: address = test_address(test) except TypeError: # Explodes if the function passed to @with_setup # applied to a test generator has an error. address = None if address: # None if no such callable found. No sense # trying to find the test frame if there's no # such thing. file, line = frame_of_test(address, exception_type, exception_value, extracted_tb)[:2] writeln(' ' * len(kind) + ' %s +%s %s' % (os.environ.get('EDITOR', 'vi'), line, human_path(src(file), self._cwd))) write(self._codes['sgr0']) # end bold # Traceback: # TODO: Think about using self._exc_info_to_string, which # does some pretty whizzy skipping of unittest frames. write(formatted_traceback) # Exception: write(''.join(format_exception_only(exception_type, exception_value))) else: write(self._codes['sgr0']) # end bold def addError(self, test, err): exc, val, tb = err # We don't read this, but some other plugin might conceivably expect it # to be there: excInfo = self._exc_info_to_string(err, test) # We duplicate the errorclass handling from super rather than calling # it and monkeying around with flags to keep it from printing anything. isErrorClass = False for cls, (storage, label, isFailure) in self.errorClasses.iteritems(): if isclass(exc) and issubclass(exc, cls): if isFailure: test.passed = False storage.append((test, excInfo)) isErrorClass = True if not isErrorClass: self.errors.append((test, excInfo)) test.passed = False with self.bar.dodging(): self._printError(label if isErrorClass else 'ERROR', err, test, isFailure=not isErrorClass or isFailure) def addFailure(self, test, err): super(ProgressiveResult, self).addFailure(test, err) self._printError('FAIL', err, test) def printSummary(self, start, stop): """As a final summary, print number of tests, broken down by result.""" def renderResultType(type, number, isFailure): """Return a rendering like '2 failures'. Args: type: A singular label, like "failure" number: The number of tests with a result of that type isFailure: Whether that type counts as a failure. """ # I'd rather hope for the best with plurals than totally punt on # being Englishlike: ret = '%s %s%s' % (number, type, 's' if number != 1 else '') if isFailure and number: ret = self._codes['bold'] + ret + self._codes['sgr0'] return ret # Summarize the special cases: counts = [('test', self.testsRun, False), ('failure', len(self.failures), True), ('error', len(self.errors), True)] # Support custom errorclasses as well as normal failures and errors. # Lowercase any all-caps labels, but leave the rest alone in case there # are hard-to-read camelCaseWordBreaks. counts.extend([(label.lower() if label.isupper() else label, len(storage), isFailure) for (storage, label, isFailure) in self.errorClasses.itervalues() if len(storage)]) summary = (', '.join(renderResultType(*a) for a in counts) + ' in %.1fs' % (stop - start)) # Erase progress bar. Bash doesn't clear the whole line when printing # the prompt, leaving a piece of the bar. Also, the prompt may not be # at the bottom of the terminal. self.bar.erase() self.stream.writeln() if self.wasSuccessful(): self.stream.write('OK! ') self.stream.writeln(summary)
class ProgressiveResult(TextTestResult): """Test result which updates a progress bar instead of printing dots Nose's ResultProxy will wrap it, and other plugins can still print stuff---but without smashing into my progress bar, care of my Plugin's stderr/out wrapping. """ def __init__(self, cwd, totalTests, stream, config=None): super(ProgressiveResult, self).__init__(stream, None, 0, config=config) self._cwd = cwd self._term = Terminal(stream=stream) # 1 in case test counting failed and returned 0 self.bar = ProgressBar(totalTests or 1, self._term) # Declare errorclass-savviness so the errorclass plugin doesn't # monkeypatch half my methods away: self.errorClasses = {} self._options = config.options def startTest(self, test): """Update the progress bar.""" super(ProgressiveResult, self).startTest(test) self.bar.update(test, self.testsRun) def _printError(self, kind, err, test, isFailure=True): """Output a human-readable error report to the stream. kind -- the (string) type of incident the precipitated this call err -- exc_info()-style traceback triple test -- the test that precipitated this call """ if isFailure or self._options.show_advisories: writeln = self.stream.writeln write = self.stream.write with self.bar.dodging(): writeln('\n' + (self._term.bold if isFailure else '') + '%s: %s' % (kind, nose_selector(test)) + (self._term.normal if isFailure else '')) # end bold if isFailure: # Then show traceback # Don't bind third item to a local var; that can create # circular refs which are expensive to collect. See the # sys.exc_info() docs. exception_type, exception_value = err[:2] extracted_tb = extract_relevant_tb( err[2], exception_type, exception_type is test.failureException) test_frame_index = index_of_test_frame( extracted_tb, exception_type, exception_value, test) if test_frame_index: # We have a good guess at which frame is the test, so # trim everything until that. We don't care to see test # framework frames. extracted_tb = extracted_tb[test_frame_index:] write(''.join( format_traceback( extracted_tb, exception_type, exception_value, self._cwd, self._term, self._options.function_color, self._options.dim_color, self._options.editor))) def addError(self, test, err): exc, val, tb = err # We don't read this, but some other plugin might conceivably expect it # to be there: excInfo = self._exc_info_to_string(err, test) # We duplicate the errorclass handling from super rather than calling # it and monkeying around with flags to keep it from printing anything. isErrorClass = False for cls, (storage, label, isFailure) in self.errorClasses.iteritems(): if isclass(exc) and issubclass(exc, cls): if isFailure: test.passed = False storage.append((test, excInfo)) isErrorClass = True if not isErrorClass: self.errors.append((test, excInfo)) test.passed = False self._printError(label if isErrorClass else 'ERROR', err, test, isFailure=not isErrorClass or isFailure) def addFailure(self, test, err): super(ProgressiveResult, self).addFailure(test, err) self._printError('FAIL', err, test) def printSummary(self, start, stop): """As a final summary, print number of tests, broken down by result.""" def renderResultType(type, number, isFailure): """Return a rendering like '2 failures'. Args: type: A singular label, like "failure" number: The number of tests with a result of that type isFailure: Whether that type counts as a failure. """ # I'd rather hope for the best with plurals than totally punt on # being Englishlike: ret = '%s %s%s' % (number, type, 's' if number != 1 else '') if isFailure and number: ret = self._term.bold + ret + self._term.normal return ret # Summarize the special cases: counts = [('test', self.testsRun, False), ('failure', len(self.failures), True), ('error', len(self.errors), True)] # Support custom errorclasses as well as normal failures and errors. # Lowercase any all-caps labels, but leave the rest alone in case there # are hard-to-read camelCaseWordBreaks. counts.extend([(label.lower() if label.isupper() else label, len(storage), isFailure) for (storage, label, isFailure) in self.errorClasses.itervalues() if len(storage)]) summary = (', '.join(renderResultType(*a) for a in counts) + ' in %.1fs' % (stop - start)) # Erase progress bar. Bash doesn't clear the whole line when printing # the prompt, leaving a piece of the bar. Also, the prompt may not be # at the bottom of the terminal. self.bar.erase() self.stream.writeln() if self.wasSuccessful(): self.stream.write('OK! ') self.stream.writeln(summary)