diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..14738bf6 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +68495ca617a9daef563af259ff25ca95b3e7e21d # initial reformat with ruff diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c6b788a..ca2a8b86 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,6 +41,10 @@ jobs: run: | python -m ruff check . + - name: Format + run: | + python -m ruff format --check . + - name: Tests run: | python -W once -m testtools.run testtools.tests.test_suite diff --git a/doc/conf.py b/doc/conf.py index 8c172ed1..ff8ed6d1 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,188 +14,193 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.append(os.path.abspath('.')) +# sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8' +# source_encoding = 'utf-8' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'testtools' -copyright = '2010-2016, The testtools authors' +project = "testtools" +copyright = "2010-2016, The testtools authors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = 'VERSION' +version = "VERSION" # The full version, including alpha/beta/rc tags. -release = 'VERSION' +release = "VERSION" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. -#unused_docs = [] +# unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. -exclude_trees = ['_build'] +exclude_trees = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'default' +html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +# html_use_modindex = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'testtoolsdoc' +htmlhelp_basename = "testtoolsdoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'testtools.tex', 'testtools Documentation', - 'The testtools authors', 'manual'), + ( + "index", + "testtools.tex", + "testtools Documentation", + "The testtools authors", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +# latex_use_modindex = True intersphinx_mapping = { - 'py2': ('https://docs.python.org/2', None), - 'py3': ('https://docs.python.org/3', None), - 'twisted': ('https://twistedmatrix.com/documents/current/api/', None), + "py2": ("https://docs.python.org/2", None), + "py3": ("https://docs.python.org/3", None), + "twisted": ("https://twistedmatrix.com/documents/current/api/", None), } diff --git a/scripts/_lp_release.py b/scripts/_lp_release.py index 80ddc926..ddb24644 100644 --- a/scripts/_lp_release.py +++ b/scripts/_lp_release.py @@ -27,18 +27,18 @@ from launchpadlib import uris -APP_NAME = 'testtools-lp-release' -CACHE_DIR = os.path.expanduser('~/.launchpadlib/cache') +APP_NAME = "testtools-lp-release" +CACHE_DIR = os.path.expanduser("~/.launchpadlib/cache") SERVICE_ROOT = uris.LPNET_SERVICE_ROOT FIX_COMMITTED = "Fix Committed" FIX_RELEASED = "Fix Released" # Launchpad file type for a tarball upload. -CODE_RELEASE_TARBALL = 'Code Release Tarball' +CODE_RELEASE_TARBALL = "Code Release Tarball" -PROJECT_NAME = 'testtools' -NEXT_MILESTONE_NAME = 'next' +PROJECT_NAME = "testtools" +NEXT_MILESTONE_NAME = "next" class _UTC(tzinfo): @@ -53,6 +53,7 @@ def tzname(self, dt): def dst(self, dt): return timedelta(0) + UTC = _UTC() @@ -66,14 +67,16 @@ def configure_logging(): handler.setFormatter(formatter) log.addHandler(handler) return log + + LOG = configure_logging() def get_path(relpath): """Get the absolute path for something relative to this file.""" return os.path.abspath( - os.path.join( - os.path.dirname(os.path.dirname(__file__)), relpath)) + os.path.join(os.path.dirname(os.path.dirname(__file__)), relpath) + ) def assign_fix_committed_to_next(testtools, next_milestone): @@ -108,27 +111,26 @@ def is_heading_marker(line, marker_char): for line in news: line = line.strip() if state is None: - if (is_heading_marker(line, '~') and - not last_line.startswith('NEXT')): + if is_heading_marker(line, "~") and not last_line.startswith("NEXT"): milestone_name = last_line - state = 'release-notes' + state = "release-notes" else: last_line = line - elif state == 'title': + elif state == "title": # The line after the title is a heading marker line, so we # ignore it and change state. That which follows are the # release notes. - state = 'release-notes' - elif state == 'release-notes': - if is_heading_marker(line, '-'): - state = 'changelog' + state = "release-notes" + elif state == "release-notes": + if is_heading_marker(line, "-"): + state = "changelog" # Last line in the release notes is actually the first # line of the changelog. changelog = [release_notes.pop(), line] else: release_notes.append(line) - elif state == 'changelog': - if is_heading_marker(line, '~'): + elif state == "changelog": + if is_heading_marker(line, "~"): # Last line in changelog is actually the first line of the # next section. changelog.pop() @@ -138,20 +140,19 @@ def is_heading_marker(line, marker_char): else: raise ValueError("Couldn't parse NEWS") - release_notes = '\n'.join(release_notes).strip() + '\n' - changelog = '\n'.join(changelog).strip() + '\n' + release_notes = "\n".join(release_notes).strip() + "\n" + changelog = "\n".join(changelog).strip() + "\n" return milestone_name, release_notes, changelog def release_milestone(milestone, release_notes, changelog): date_released = datetime.now(tz=UTC) - LOG.info( - f"Releasing milestone: {milestone.name}, date {date_released}") + LOG.info(f"Releasing milestone: {milestone.name}, date {date_released}") release = milestone.createProductRelease( date_released=date_released, changelog=changelog, release_notes=release_notes, - ) + ) milestone.is_active = False milestone.lp_save() return release @@ -171,8 +172,7 @@ def close_fixed_bugs(milestone): LOG.info(f"Closing {task.title}") task.status = FIX_RELEASED else: - LOG.warning( - f"Bug not fixed, removing from milestone: {task.title}") + LOG.warning(f"Bug not fixed, removing from milestone: {task.title}") task.milestone = None task.lp_save() @@ -180,33 +180,36 @@ def close_fixed_bugs(milestone): def upload_tarball(release, tarball_path): with open(tarball_path) as tarball: tarball_content = tarball.read() - sig_path = tarball_path + '.asc' + sig_path = tarball_path + ".asc" with open(sig_path) as sig: sig_content = sig.read() tarball_name = os.path.basename(tarball_path) LOG.info(f"Uploading tarball: {tarball_path}") release.add_file( file_type=CODE_RELEASE_TARBALL, - file_content=tarball_content, filename=tarball_name, + file_content=tarball_content, + filename=tarball_name, signature_content=sig_content, signature_filename=sig_path, - content_type="application/x-gzip; charset=binary") + content_type="application/x-gzip; charset=binary", + ) def release_project(launchpad, project_name, next_milestone_name): testtools = launchpad.projects[project_name] next_milestone = testtools.getMilestone(name=next_milestone_name) release_name, release_notes, changelog = get_release_notes_and_changelog( - get_path('NEWS')) + get_path("NEWS") + ) LOG.info(f"Releasing {project_name} {release_name}") # Since reversing these operations is hard, and inspecting errors from # Launchpad is also difficult, do some looking before leaping. errors = [] - tarball_path = get_path(f'dist/{project_name}-{release_name}.tar.gz') + tarball_path = get_path(f"dist/{project_name}-{release_name}.tar.gz") if not os.path.isfile(tarball_path): errors.append(f"{tarball_path} does not exist") - if not os.path.isfile(tarball_path + '.asc'): - errors.append("{} does not exist".format(tarball_path + '.asc')) + if not os.path.isfile(tarball_path + ".asc"): + errors.append("{} does not exist".format(tarball_path + ".asc")) if testtools.getMilestone(name=release_name): errors.append(f"Milestone {release_name} exists on {project_name}") if errors: @@ -224,9 +227,10 @@ def release_project(launchpad, project_name, next_milestone_name): def main(args): launchpad = Launchpad.login_with( - APP_NAME, SERVICE_ROOT, CACHE_DIR, credentials_file='.lp_creds') + APP_NAME, SERVICE_ROOT, CACHE_DIR, credentials_file=".lp_creds" + ) return release_project(launchpad, PROJECT_NAME, NEXT_MILESTONE_NAME) -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(main(sys.argv)) diff --git a/testtools/__init__.py b/testtools/__init__.py index 91fb83b7..9af8522b 100644 --- a/testtools/__init__.py +++ b/testtools/__init__.py @@ -3,48 +3,48 @@ """Extensions to the standard Python unittest library.""" __all__ = [ - 'clone_test_with_new_id', - 'CopyStreamResult', - 'ConcurrentTestSuite', - 'ConcurrentStreamTestSuite', - 'DecorateTestCaseResult', - 'ErrorHolder', - 'ExpectedException', - 'ExtendedToOriginalDecorator', - 'ExtendedToStreamDecorator', - 'FixtureSuite', - 'iterate_tests', - 'MultipleExceptions', - 'MultiTestResult', - 'PlaceHolder', - 'run_test_with', - 'ResourcedToStreamDecorator', - 'Tagger', - 'TestCase', - 'TestByTestResult', - 'TestResult', - 'TestResultDecorator', - 'TextTestResult', - 'RunTest', - 'skip', - 'skipIf', - 'skipUnless', - 'StreamFailFast', - 'StreamResult', - 'StreamResultRouter', - 'StreamSummary', - 'StreamTagger', - 'StreamToDict', - 'StreamToExtendedDecorator', - 'StreamToQueue', - 'TestControl', - 'ThreadsafeForwardingResult', - 'TimestampingStreamResult', - 'try_import', - 'unique_text_generator', - 'version', - '__version__', - ] + "clone_test_with_new_id", + "CopyStreamResult", + "ConcurrentTestSuite", + "ConcurrentStreamTestSuite", + "DecorateTestCaseResult", + "ErrorHolder", + "ExpectedException", + "ExtendedToOriginalDecorator", + "ExtendedToStreamDecorator", + "FixtureSuite", + "iterate_tests", + "MultipleExceptions", + "MultiTestResult", + "PlaceHolder", + "run_test_with", + "ResourcedToStreamDecorator", + "Tagger", + "TestCase", + "TestByTestResult", + "TestResult", + "TestResultDecorator", + "TextTestResult", + "RunTest", + "skip", + "skipIf", + "skipUnless", + "StreamFailFast", + "StreamResult", + "StreamResultRouter", + "StreamSummary", + "StreamTagger", + "StreamToDict", + "StreamToExtendedDecorator", + "StreamToQueue", + "TestControl", + "ThreadsafeForwardingResult", + "TimestampingStreamResult", + "try_import", + "unique_text_generator", + "version", + "__version__", +] from testtools.helpers import try_import from testtools.matchers._impl import Matcher # noqa: F401 @@ -116,13 +116,14 @@ # This will fail with LookupError if the package is not installed in # editable mode or if Git is not installed. version = get_version(root="..", relative_to=__file__) - __version__ = tuple(version.split('.')) + __version__ = tuple(version.split(".")) except (ImportError, LookupError): # As a fallback, use the version that is hard-coded in the file. try: - from ._version import (__version__, version) + from ._version import __version__, version except ModuleNotFoundError: # The user is probably trying to run this without having installed # the package, so complain. raise RuntimeError( - "Testtools is not correctly installed. Please install it with pip.") + "Testtools is not correctly installed. Please install it with pip." + ) diff --git a/testtools/assertions.py b/testtools/assertions.py index b241df63..f4d6bab8 100644 --- a/testtools/assertions.py +++ b/testtools/assertions.py @@ -5,10 +5,10 @@ from testtools.matchers import ( Annotate, MismatchError, - ) +) -def assert_that(matchee, matcher, message='', verbose=False): +def assert_that(matchee, matcher, message="", verbose=False): """Assert that matchee is matched by matcher. This should only be used when you need to use a function based diff --git a/testtools/compat.py b/testtools/compat.py index 6562317d..437ba6af 100644 --- a/testtools/compat.py +++ b/testtools/compat.py @@ -3,13 +3,13 @@ """Compatibility support for python 2 and 3.""" __all__ = [ - '_b', - 'advance_iterator', - 'reraise', - 'unicode_output_stream', - 'StringIO', - 'BytesIO', - ] + "_b", + "advance_iterator", + "reraise", + "unicode_output_stream", + "StringIO", + "BytesIO", +] import codecs import io @@ -17,6 +17,7 @@ import os import sys import unicodedata + # Ensure retro-compatibility with older testtools releases from io import StringIO, BytesIO @@ -69,8 +70,7 @@ def _slow_escape(text): def text_repr(text, multiline=None): - """Rich repr for ``text`` returning unicode, triple quoted if ``multiline``. - """ + """Rich repr for ``text`` returning unicode, triple quoted if ``multiline``.""" nl = isinstance(text, bytes) and bytes((0xA,)) or "\n" if multiline is None: multiline = nl in text @@ -120,8 +120,7 @@ def unicode_output_stream(stream): The wrapper only allows unicode to be written, not non-ascii bytestrings, which is a good thing to ensure sanity and sanitation. """ - if (sys.platform == "cli" or - isinstance(stream, (io.TextIOWrapper, io.StringIO))): + if sys.platform == "cli" or isinstance(stream, (io.TextIOWrapper, io.StringIO)): # Best to never encode before writing in IronPython, or if it is # already a TextIO [which in the io library has no encoding # attribute). @@ -135,8 +134,13 @@ def unicode_output_stream(stream): return stream # Python 3 doesn't seem to make this easy, handle a common case try: - return stream.__class__(stream.buffer, stream.encoding, "replace", - stream.newlines, stream.line_buffering) + return stream.__class__( + stream.buffer, + stream.encoding, + "replace", + stream.newlines, + stream.line_buffering, + ) except AttributeError: pass return writer(stream, "replace") @@ -152,4 +156,3 @@ def _get_exception_encoding(): # no benefit in asking more than once as it's a global # setting that can change after the message is formatted. return locale.getlocale(locale.LC_MESSAGES)[1] or "ascii" - diff --git a/testtools/content.py b/testtools/content.py index 8ed573cd..0dd6177d 100644 --- a/testtools/content.py +++ b/testtools/content.py @@ -3,14 +3,14 @@ """Content - a MIME-like Content object.""" __all__ = [ - 'attach_file', - 'Content', - 'content_from_file', - 'content_from_stream', - 'json_content', - 'text_content', - 'TracebackContent', - ] + "attach_file", + "Content", + "content_from_file", + "content_from_stream", + "json_content", + "text_content", + "TracebackContent", +] import codecs import functools @@ -27,8 +27,8 @@ DEFAULT_CHUNK_SIZE = 4096 -STDOUT_LINE = '\nStdout:\n%s' -STDERR_LINE = '\nStderr:\n%s' +STDOUT_LINE = "\nStdout:\n%s" +STDERR_LINE = "\nStderr:\n%s" def _iter_chunks(stream, chunk_size, seek_offset=None, seek_whence=0): @@ -62,14 +62,16 @@ class Content: def __init__(self, content_type, get_bytes): """Create a ContentType.""" if None in (content_type, get_bytes): - raise ValueError("None not permitted in {!r}, {!r}".format( - content_type, get_bytes)) + raise ValueError( + "None not permitted in {!r}, {!r}".format(content_type, get_bytes) + ) self.content_type = content_type self._get_bytes = get_bytes def __eq__(self, other): - return (self.content_type == other.content_type and - _join_b(self.iter_bytes()) == _join_b(other.iter_bytes())) + return self.content_type == other.content_type and _join_b( + self.iter_bytes() + ) == _join_b(other.iter_bytes()) def as_text(self): """Return all of the content as text. @@ -78,7 +80,7 @@ def as_text(self): content into memory. Where this is a concern, use ``iter_text`` instead. """ - return ''.join(self.iter_text()) + return "".join(self.iter_text()) def iter_bytes(self): """Iterate over bytestrings of the serialised content.""" @@ -99,17 +101,18 @@ def iter_text(self): def _iter_text(self): """Worker for iter_text - does the decoding.""" - encoding = self.content_type.parameters.get('charset', 'ISO-8859-1') + encoding = self.content_type.parameters.get("charset", "ISO-8859-1") decoder = codecs.getincrementaldecoder(encoding)() for bytes in self.iter_bytes(): yield decoder.decode(bytes) - final = decoder.decode(_b(''), True) + final = decoder.decode(_b(""), True) if final: yield final def __repr__(self): return "".format( - self.content_type, _join_b(self.iter_bytes())) + self.content_type, _join_b(self.iter_bytes()) + ) class StackLinesContent(Content): @@ -139,19 +142,18 @@ def __init__(self, stack_lines, prefix_content="", postfix_content=""): :param postfix_content: If specified, a unicode string to append to the text content. """ - content_type = ContentType('text', 'x-traceback', - {"language": "python", "charset": "utf8"}) - value = prefix_content + \ - self._stack_lines_to_unicode(stack_lines) + \ - postfix_content - super().__init__( - content_type, lambda: [value.encode("utf8")]) + content_type = ContentType( + "text", "x-traceback", {"language": "python", "charset": "utf8"} + ) + value = ( + prefix_content + self._stack_lines_to_unicode(stack_lines) + postfix_content + ) + super().__init__(content_type, lambda: [value.encode("utf8")]) def _stack_lines_to_unicode(self, stack_lines): - """Converts a list of pre-processed stack lines into a unicode string. - """ + """Converts a list of pre-processed stack lines into a unicode string.""" msg_lines = traceback.format_list(stack_lines) - return ''.join(msg_lines) + return "".join(msg_lines) class TracebackContent(Content): @@ -175,27 +177,32 @@ def __init__(self, err, test, capture_locals=False): exctype, value, tb = err # Skip test runner traceback levels if StackLinesContent.HIDE_INTERNAL_STACK: - while tb and '__unittest' in tb.tb_frame.f_globals: + while tb and "__unittest" in tb.tb_frame.f_globals: tb = tb.tb_next limit = None # Disabled due to https://bugs.launchpad.net/testtools/+bug/1188420 - if (False + if ( + False and StackLinesContent.HIDE_INTERNAL_STACK and test.failureException - and isinstance(value, test.failureException)): + and isinstance(value, test.failureException) + ): # Skip assert*() traceback levels limit = 0 while tb and not self._is_relevant_tb_level(tb): limit += 1 tb = tb.tb_next - stack_lines = list(traceback.TracebackException(exctype, value, tb, - limit=limit, capture_locals=capture_locals).format()) - content_type = ContentType('text', 'x-traceback', - {"language": "python", "charset": "utf8"}) - super().__init__( - content_type, lambda: [x.encode('utf8') for x in stack_lines]) + stack_lines = list( + traceback.TracebackException( + exctype, value, tb, limit=limit, capture_locals=capture_locals + ).format() + ) + content_type = ContentType( + "text", "x-traceback", {"language": "python", "charset": "utf8"} + ) + super().__init__(content_type, lambda: [x.encode("utf8") for x in stack_lines]) def StacktraceContent(prefix_content="", postfix_content=""): @@ -211,6 +218,7 @@ def StacktraceContent(prefix_content="", postfix_content=""): :param postfix_content: A unicode string to add after the stack lines. """ stack = traceback.walk_stack(None) + def filter_stack(stack): # Discard the filter_stack frame. next(stack) @@ -218,9 +226,10 @@ def filter_stack(stack): next(stack) for f, f_lineno in stack: if StackLinesContent.HIDE_INTERNAL_STACK: - if '__unittest' in f.f_globals: + if "__unittest" in f.f_globals: return yield f, f_lineno + extract = traceback.StackSummary.extract(filter_stack(stack)) extract.reverse() return StackLinesContent(extract, prefix_content, postfix_content) @@ -230,7 +239,7 @@ def json_content(json_data): """Create a JSON Content object from JSON-encodeable data.""" data = json.dumps(json_data) # The json module perversely returns native str not bytes - data = data.encode('utf8') + data = data.encode("utf8") return Content(JSON, lambda: [data]) @@ -243,7 +252,7 @@ def text_content(text): raise TypeError( "text_content must be given text, not '%s'." % type(text).__name__ ) - return Content(UTF8_TEXT, lambda: [text.encode('utf8')]) + return Content(UTF8_TEXT, lambda: [text.encode("utf8")]) def maybe_wrap(wrapper, func): @@ -253,8 +262,14 @@ def maybe_wrap(wrapper, func): return wrapper -def content_from_file(path, content_type=None, chunk_size=DEFAULT_CHUNK_SIZE, - buffer_now=False, seek_offset=None, seek_whence=0): +def content_from_file( + path, + content_type=None, + chunk_size=DEFAULT_CHUNK_SIZE, + buffer_now=False, + seek_offset=None, + seek_whence=0, +): """Create a Content object from a file on disk. Note that unless ``buffer_now`` is explicitly passed in as True, the file @@ -272,18 +287,22 @@ def content_from_file(path, content_type=None, chunk_size=DEFAULT_CHUNK_SIZE, """ if content_type is None: content_type = UTF8_TEXT + def reader(): - with open(path, 'rb') as stream: - yield from _iter_chunks(stream, - chunk_size, - seek_offset, - seek_whence) + with open(path, "rb") as stream: + yield from _iter_chunks(stream, chunk_size, seek_offset, seek_whence) + return content_from_reader(reader, content_type, buffer_now) -def content_from_stream(stream, content_type=None, - chunk_size=DEFAULT_CHUNK_SIZE, buffer_now=False, - seek_offset=None, seek_whence=0): +def content_from_stream( + stream, + content_type=None, + chunk_size=DEFAULT_CHUNK_SIZE, + buffer_now=False, + seek_offset=None, + seek_whence=0, +): """Create a Content object from a file-like stream. Note that unless ``buffer_now`` is explicitly passed in as True, the stream @@ -302,8 +321,10 @@ def content_from_stream(stream, content_type=None, """ if content_type is None: content_type = UTF8_TEXT + def reader(): return _iter_chunks(stream, chunk_size, seek_offset, seek_whence) + return content_from_reader(reader, content_type, buffer_now) @@ -320,13 +341,21 @@ def content_from_reader(reader, content_type, buffer_now): content_type = UTF8_TEXT if buffer_now: contents = list(reader()) + def reader(): return contents + return Content(content_type, reader) -def attach_file(detailed, path, name=None, content_type=None, - chunk_size=DEFAULT_CHUNK_SIZE, buffer_now=True): +def attach_file( + detailed, + path, + name=None, + content_type=None, + chunk_size=DEFAULT_CHUNK_SIZE, + buffer_now=True, +): """Attach a file to this test as a detail. This is a convenience method wrapping around ``addDetail``. @@ -354,6 +383,5 @@ def attach_file(detailed, path, name=None, content_type=None, """ if name is None: name = os.path.basename(path) - content_object = content_from_file( - path, content_type, chunk_size, buffer_now) + content_object = content_from_file(path, content_type, chunk_size, buffer_now) detailed.addDetail(name, content_object) diff --git a/testtools/content_type.py b/testtools/content_type.py index 18db09f8..aaa9e71b 100644 --- a/testtools/content_type.py +++ b/testtools/content_type.py @@ -15,8 +15,9 @@ class ContentType: def __init__(self, primary_type, sub_type, parameters=None): """Create a ContentType.""" if None in (primary_type, sub_type): - raise ValueError("None not permitted in {!r}, {!r}".format( - primary_type, sub_type)) + raise ValueError( + "None not permitted in {!r}, {!r}".format(primary_type, sub_type) + ) self.type = primary_type self.subtype = sub_type self.parameters = parameters or {} @@ -28,14 +29,15 @@ def __eq__(self, other): def __repr__(self): if self.parameters: - params = '; ' - params += '; '.join( - sorted(f'{k}="{v}"' for k, v in self.parameters.items())) + params = "; " + params += "; ".join( + sorted(f'{k}="{v}"' for k, v in self.parameters.items()) + ) else: - params = '' + params = "" return f"{self.type}/{self.subtype}{params}" -JSON = ContentType('application', 'json') +JSON = ContentType("application", "json") -UTF8_TEXT = ContentType('text', 'plain', {'charset': 'utf8'}) +UTF8_TEXT = ContentType("text", "plain", {"charset": "utf8"}) diff --git a/testtools/helpers.py b/testtools/helpers.py index f3c87de3..f3e470f0 100644 --- a/testtools/helpers.py +++ b/testtools/helpers.py @@ -17,7 +17,7 @@ def try_import(name, alternative=None, error_callback=None): :param error_callback: If non-None, a callable that is passed the ImportError when the module cannot be loaded. """ - module_segments = name.split('.') + module_segments = name.split(".") last_error = None remainder = [] @@ -25,7 +25,7 @@ def try_import(name, alternative=None, error_callback=None): # __import__ result because in import loops (A imports A.B, which imports # C, which calls try_import("A.B")) A.B will not yet be set. while module_segments: - module_name = '.'.join(module_segments) + module_name = ".".join(module_segments) try: __import__(module_name) except ImportError: diff --git a/testtools/matchers/__init__.py b/testtools/matchers/__init__.py index fdd6b100..ba63f41a 100644 --- a/testtools/matchers/__init__.py +++ b/testtools/matchers/__init__.py @@ -13,53 +13,53 @@ """ __all__ = [ - 'AfterPreprocessing', - 'AllMatch', - 'Always', - 'Annotate', - 'AnyMatch', - 'Contains', - 'ContainsAll', - 'ContainedByDict', - 'ContainsDict', - 'DirContains', - 'DirExists', - 'DocTestMatches', - 'EndsWith', - 'Equals', - 'FileContains', - 'FileExists', - 'GreaterThan', - 'HasLength', - 'HasPermissions', - 'Is', - 'IsDeprecated', - 'IsInstance', - 'KeysEqual', - 'LessThan', - 'MatchesAll', - 'MatchesAny', - 'MatchesDict', - 'MatchesException', - 'MatchesListwise', - 'MatchesPredicate', - 'MatchesPredicateWithParams', - 'MatchesRegex', - 'MatchesSetwise', - 'MatchesStructure', - 'Never', - 'NotEquals', - 'Not', - 'PathExists', - 'Raises', - 'raises', - 'SameMembers', - 'SamePath', - 'StartsWith', - 'TarballContains', - 'Warnings', - 'WarningMessage' - ] + "AfterPreprocessing", + "AllMatch", + "Always", + "Annotate", + "AnyMatch", + "Contains", + "ContainsAll", + "ContainedByDict", + "ContainsDict", + "DirContains", + "DirExists", + "DocTestMatches", + "EndsWith", + "Equals", + "FileContains", + "FileExists", + "GreaterThan", + "HasLength", + "HasPermissions", + "Is", + "IsDeprecated", + "IsInstance", + "KeysEqual", + "LessThan", + "MatchesAll", + "MatchesAny", + "MatchesDict", + "MatchesException", + "MatchesListwise", + "MatchesPredicate", + "MatchesPredicateWithParams", + "MatchesRegex", + "MatchesSetwise", + "MatchesStructure", + "Never", + "NotEquals", + "Not", + "PathExists", + "Raises", + "raises", + "SameMembers", + "SamePath", + "StartsWith", + "TarballContains", + "Warnings", + "WarningMessage", +] from ._basic import ( Contains, @@ -74,31 +74,31 @@ NotEquals, SameMembers, StartsWith, - ) +) from ._const import ( Always, Never, - ) +) from ._datastructures import ( ContainsAll, MatchesListwise, MatchesSetwise, MatchesStructure, - ) +) from ._dict import ( ContainedByDict, ContainsDict, KeysEqual, MatchesDict, - ) +) from ._doctest import ( DocTestMatches, - ) +) from ._exception import ( MatchesException, Raises, raises, - ) +) from ._filesystem import ( DirContains, DirExists, @@ -108,7 +108,7 @@ PathExists, SamePath, TarballContains, - ) +) from ._higherorder import ( AfterPreprocessing, AllMatch, @@ -119,12 +119,12 @@ MatchesPredicate, MatchesPredicateWithParams, Not, - ) +) from ._warnings import ( IsDeprecated, WarningMessage, Warnings, - ) +) # XXX: These are not explicitly included in __all__. It's unclear how much of # the public interface they really are. @@ -132,4 +132,4 @@ Matcher, Mismatch, MismatchError, - ) +) diff --git a/testtools/matchers/_basic.py b/testtools/matchers/_basic.py index 7c5d88f9..dc840fd9 100644 --- a/testtools/matchers/_basic.py +++ b/testtools/matchers/_basic.py @@ -1,19 +1,19 @@ # Copyright (c) 2009-2012 testtools developers. See LICENSE for details. __all__ = [ - 'Contains', - 'EndsWith', - 'Equals', - 'GreaterThan', - 'HasLength', - 'Is', - 'IsInstance', - 'LessThan', - 'MatchesRegex', - 'NotEquals', - 'SameMembers', - 'StartsWith', - ] + "Contains", + "EndsWith", + "Equals", + "GreaterThan", + "HasLength", + "Is", + "IsInstance", + "LessThan", + "MatchesRegex", + "NotEquals", + "SameMembers", + "StartsWith", +] import operator from pprint import pformat @@ -22,16 +22,16 @@ from ..compat import ( text_repr, - ) +) from ..helpers import list_subtract from ._higherorder import ( MatchesPredicateWithParams, PostfixedMismatch, - ) +) from ._impl import ( Matcher, Mismatch, - ) +) def _format(thing): @@ -65,8 +65,7 @@ def comparator(self, expected, other): class _BinaryMismatch(Mismatch): """Two things did not match.""" - def __init__(self, actual, mismatch_string, reference, - reference_on_right=True): + def __init__(self, actual, mismatch_string, reference, reference_on_right=True): self._actual = actual self._mismatch_string = mismatch_string self._reference = reference @@ -75,7 +74,7 @@ def __init__(self, actual, mismatch_string, reference, @property def expected(self): warnings.warn( - f'{self.__class__.__name__}.expected deprecated after 1.8.1', + f"{self.__class__.__name__}.expected deprecated after 1.8.1", DeprecationWarning, stacklevel=2, ) @@ -84,7 +83,7 @@ def expected(self): @property def other(self): warnings.warn( - f'{self.__class__.__name__}.other deprecated after 1.8.1', + f"{self.__class__.__name__}.other deprecated after 1.8.1", DeprecationWarning, stacklevel=2, ) @@ -95,8 +94,8 @@ def describe(self): reference = repr(self._reference) if len(actual) + len(reference) > 70: return "{}:\nreference = {}\nactual = {}\n".format( - self._mismatch_string, _format(self._reference), - _format(self._actual)) + self._mismatch_string, _format(self._reference), _format(self._actual) + ) else: if self._reference_on_right: left, right = actual, reference @@ -109,7 +108,7 @@ class Equals(_BinaryComparison): """Matches if the items are equal.""" comparator = operator.eq - mismatch_string = '!=' + mismatch_string = "!=" class _FlippedEquals: @@ -129,7 +128,7 @@ def match(self, other): mismatch = Equals(self._expected).match(other) if not mismatch: return None - return _BinaryMismatch(other, '!=', self._expected, False) + return _BinaryMismatch(other, "!=", self._expected, False) class NotEquals(_BinaryComparison): @@ -140,28 +139,28 @@ class NotEquals(_BinaryComparison): """ comparator = operator.ne - mismatch_string = '==' + mismatch_string = "==" class Is(_BinaryComparison): """Matches if the items are identical.""" comparator = operator.is_ - mismatch_string = 'is not' + mismatch_string = "is not" class LessThan(_BinaryComparison): """Matches if the item is less than the matchers reference object.""" comparator = operator.__lt__ - mismatch_string = '>=' + mismatch_string = ">=" class GreaterThan(_BinaryComparison): """Matches if the item is greater than the matchers reference object.""" comparator = operator.__gt__ - mismatch_string = '<=' + mismatch_string = "<=" class SameMembers(Matcher): @@ -176,7 +175,7 @@ def __init__(self, expected): self.expected = expected def __str__(self): - return f'{self.__class__.__name__}({self.expected!r})' + return f"{self.__class__.__name__}({self.expected!r})" def match(self, observed): expected_only = list_subtract(self.expected, observed) @@ -185,12 +184,13 @@ def match(self, observed): return return PostfixedMismatch( "\nmissing: {}\nextra: {}".format( - _format(expected_only), _format(observed_only)), - _BinaryMismatch(observed, 'elements differ', self.expected)) + _format(expected_only), _format(observed_only) + ), + _BinaryMismatch(observed, "elements differ", self.expected), + ) class DoesNotStartWith(Mismatch): - def __init__(self, matchee, expected): """Create a DoesNotStartWith Mismatch. @@ -202,7 +202,8 @@ def __init__(self, matchee, expected): def describe(self): return "{} does not start with {}.".format( - text_repr(self.matchee), text_repr(self.expected)) + text_repr(self.matchee), text_repr(self.expected) + ) class StartsWith(Matcher): @@ -225,7 +226,6 @@ def match(self, matchee): class DoesNotEndWith(Mismatch): - def __init__(self, matchee, expected): """Create a DoesNotEndWith Mismatch. @@ -237,7 +237,8 @@ def __init__(self, matchee, expected): def describe(self): return "{} does not end with {}.".format( - text_repr(self.matchee), text_repr(self.expected)) + text_repr(self.matchee), text_repr(self.expected) + ) class EndsWith(Matcher): @@ -266,8 +267,9 @@ def __init__(self, *types): self.types = tuple(types) def __str__(self): - return "{}({})".format(self.__class__.__name__, - ', '.join(type.__name__ for type in self.types)) + return "{}({})".format( + self.__class__.__name__, ", ".join(type.__name__ for type in self.types) + ) def match(self, other): if isinstance(other, self.types): @@ -276,7 +278,6 @@ def match(self, other): class NotAnInstance(Mismatch): - def __init__(self, matchee, types): """Create a NotAnInstance Mismatch. @@ -290,13 +291,11 @@ def describe(self): if len(self.types) == 1: typestr = self.types[0].__name__ else: - typestr = 'any of (%s)' % ', '.join(type.__name__ for type in - self.types) + typestr = "any of (%s)" % ", ".join(type.__name__ for type in self.types) return f"'{self.matchee}' is not an instance of {typestr}" class DoesNotContain(Mismatch): - def __init__(self, matchee, needle): """Create a DoesNotContain Mismatch. @@ -341,16 +340,16 @@ def __init__(self, pattern, flags=0): self.flags = flags def __str__(self): - args = ['%r' % self.pattern] + args = ["%r" % self.pattern] flag_arg = [] # dir() sorts the attributes for us, so we don't need to do it again. for flag in dir(re): if len(flag) == 1: if self.flags & getattr(re, flag): - flag_arg.append('re.%s' % flag) + flag_arg.append("re.%s" % flag) if flag_arg: - args.append('|'.join(flag_arg)) - return '{}({})'.format(self.__class__.__name__, ', '.join(args)) + args.append("|".join(flag_arg)) + return "{}({})".format(self.__class__.__name__, ", ".join(args)) def match(self, value): if not re.match(self.pattern, value, self.flags): @@ -358,8 +357,9 @@ def match(self, value): if not isinstance(pattern, str): pattern = pattern.decode("latin1") pattern = pattern.encode("unicode_escape").decode("ascii") - return Mismatch("{!r} does not match /{}/".format( - value, pattern.replace("\\\\", "\\"))) + return Mismatch( + "{!r} does not match /{}/".format(value, pattern.replace("\\\\", "\\")) + ) def has_len(x, y): diff --git a/testtools/matchers/_const.py b/testtools/matchers/_const.py index 3103a2c8..df405347 100644 --- a/testtools/matchers/_const.py +++ b/testtools/matchers/_const.py @@ -1,9 +1,9 @@ # Copyright (c) 2016 testtools developers. See LICENSE for details. __all__ = [ - 'Always', - 'Never', - ] + "Always", + "Never", +] from ._impl import Mismatch @@ -12,7 +12,7 @@ class _Always: """Always matches.""" def __str__(self): - return 'Always()' + return "Always()" def match(self, value): return None @@ -36,11 +36,10 @@ class _Never: """Never matches.""" def __str__(self): - return 'Never()' + return "Never()" def match(self, value): - return Mismatch( - f'Inevitable mismatch on {value!r}') + return Mismatch(f"Inevitable mismatch on {value!r}") def Never(): diff --git a/testtools/matchers/_datastructures.py b/testtools/matchers/_datastructures.py index dcb640db..5d7edf2b 100644 --- a/testtools/matchers/_datastructures.py +++ b/testtools/matchers/_datastructures.py @@ -7,17 +7,15 @@ Annotate, MatchesAll, MismatchesAll, - ) +) from ._impl import Mismatch __all__ = [ - 'ContainsAll', - 'MatchesListwise', - 'MatchesSetwise', - 'MatchesStructure', - ] - - + "ContainsAll", + "MatchesListwise", + "MatchesSetwise", + "MatchesStructure", +] def ContainsAll(items): @@ -28,6 +26,7 @@ def ContainsAll(items): the matchee. """ from ._basic import Contains + return MatchesAll(*map(Contains, items), first_only=False) @@ -61,9 +60,11 @@ def __init__(self, matchers, first_only=False): def match(self, values): from ._basic import HasLength + mismatches = [] length_mismatch = Annotate( - "Length mismatch", HasLength(len(self.matchers))).match(values) + "Length mismatch", HasLength(len(self.matchers)) + ).match(values) if length_mismatch: mismatches.append(length_mismatch) for matcher, value in zip(self.matchers, values): @@ -108,6 +109,7 @@ def byEquality(cls, **kwargs): Equals. """ from ._basic import Equals + return cls.byMatcher(Equals, **kwargs) @classmethod @@ -122,6 +124,7 @@ def byMatcher(cls, matcher, **kwargs): @classmethod def fromExample(cls, example, *attributes): from ._basic import Equals + kwargs = {} for attr in attributes: kwargs[attr] = Equals(getattr(example, attr)) @@ -140,7 +143,7 @@ def __str__(self): kws = [] for attr, matcher in sorted(self.kws.items()): kws.append(f"{attr}={matcher}") - return "{}({})".format(self.__class__.__name__, ', '.join(kws)) + return "{}({})".format(self.__class__.__name__, ", ".join(kws)) def match(self, value): matchers = [] @@ -191,20 +194,23 @@ def match(self, observed): if len(not_matched) == 0: if len(remaining_matchers) > 1: msg = "There were {} matchers left over: ".format( - len(remaining_matchers)) + len(remaining_matchers) + ) else: msg = "There was 1 matcher left over: " - msg += ', '.join(map(str, remaining_matchers)) + msg += ", ".join(map(str, remaining_matchers)) return Mismatch(msg) elif len(remaining_matchers) == 0: if len(not_matched) > 1: return Mismatch( "There were {} values left over: {}".format( - len(not_matched), not_matched)) + len(not_matched), not_matched + ) + ) else: return Mismatch( - "There was 1 value left over: {}".format( - not_matched)) + "There was 1 value left over: {}".format(not_matched) + ) else: common_length = min(len(remaining_matchers), len(not_matched)) if common_length == 0: @@ -218,13 +224,13 @@ def match(self, observed): msg += f" and {len(extra_matchers)} extra matcher" if len(extra_matchers) > 1: msg += "s" - msg += ': ' + ', '.join(map(str, extra_matchers)) + msg += ": " + ", ".join(map(str, extra_matchers)) elif len(not_matched) > len(remaining_matchers): extra_values = not_matched[common_length:] msg += f" and {len(extra_values)} extra value" if len(extra_values) > 1: msg += "s" - msg += ': ' + str(extra_values) + msg += ": " + str(extra_values) return Annotate( msg, MatchesListwise(remaining_matchers[:common_length]) - ).match(not_matched[:common_length]) + ).match(not_matched[:common_length]) diff --git a/testtools/matchers/_dict.py b/testtools/matchers/_dict.py index 953f4c8f..2c0eaecf 100644 --- a/testtools/matchers/_dict.py +++ b/testtools/matchers/_dict.py @@ -1,27 +1,27 @@ # Copyright (c) 2009-2012 testtools developers. See LICENSE for details. __all__ = [ - 'KeysEqual', - ] + "KeysEqual", +] from ..helpers import ( dict_subtract, filter_values, map_values, - ) +) from ._higherorder import ( AnnotatedMismatch, PrefixedMismatch, MismatchesAll, - ) +) from ._impl import Matcher, Mismatch def LabelledMismatches(mismatches, details=None): """A collection of mismatches, each labelled.""" return MismatchesAll( - (PrefixedMismatch(k, v) for (k, v) in sorted(mismatches.items())), - wrap=False) + (PrefixedMismatch(k, v) for (k, v) in sorted(mismatches.items())), wrap=False + ) class MatchesAllDict(Matcher): @@ -36,14 +36,13 @@ def __init__(self, matchers): self.matchers = matchers def __str__(self): - return f'MatchesAllDict({_format_matcher_dict(self.matchers)})' + return f"MatchesAllDict({_format_matcher_dict(self.matchers)})" def match(self, observed): mismatches = {} for label in self.matchers: mismatches[label] = self.matchers[label].match(observed) - return _dict_to_mismatch( - mismatches, result_mismatch=LabelledMismatches) + return _dict_to_mismatch(mismatches, result_mismatch=LabelledMismatches) class DictMismatches(Mismatch): @@ -54,16 +53,18 @@ def __init__(self, mismatches, details=None): self.mismatches = mismatches def describe(self): - lines = ['{'] + lines = ["{"] lines.extend( - [f' {key!r}: {mismatch.describe()},' - for (key, mismatch) in sorted(self.mismatches.items())]) - lines.append('}') - return '\n'.join(lines) + [ + f" {key!r}: {mismatch.describe()}," + for (key, mismatch) in sorted(self.mismatches.items()) + ] + ) + lines.append("}") + return "\n".join(lines) -def _dict_to_mismatch(data, to_mismatch=None, - result_mismatch=DictMismatches): +def _dict_to_mismatch(data, to_mismatch=None, result_mismatch=DictMismatches): if to_mismatch: data = map_values(to_mismatch, data) mismatches = filter_values(bool, data) @@ -114,13 +115,11 @@ def __init__(self, super_dict, format_value=repr): def match(self, observed): excess = dict_subtract(observed, self.super_dict) - return _dict_to_mismatch( - excess, lambda v: Mismatch(self.format_value(v))) + return _dict_to_mismatch(excess, lambda v: Mismatch(self.format_value(v))) class _SuperDictOf(Matcher): - """Matches if all of the keys in the given dict are in the matched dict. - """ + """Matches if all of the keys in the given dict are in the matched dict.""" def __init__(self, sub_dict, format_value=repr): super().__init__() @@ -132,8 +131,7 @@ def match(self, super_dict): def _format_matcher_dict(matchers): - return '{%s}' % ( - ', '.join(sorted(f'{k!r}: {v}' for k, v in matchers.items()))) + return "{%s}" % (", ".join(sorted(f"{k!r}: {v}" for k, v in matchers.items()))) class _CombinedMatcher(Matcher): @@ -156,12 +154,12 @@ def format_expected(self, expected): return repr(expected) def __str__(self): - return '{}({})'.format( - self.__class__.__name__, self.format_expected(self._expected)) + return "{}({})".format( + self.__class__.__name__, self.format_expected(self._expected) + ) def match(self, observed): - matchers = { - k: v(self._expected) for k, v in self.matcher_factories.items()} + matchers = {k: v(self._expected) for k, v in self.matcher_factories.items()} return MatchesAllDict(matchers).match(observed) @@ -175,10 +173,10 @@ class MatchesDict(_CombinedMatcher): """ matcher_factories = { - 'Extra': _SubDictOf, - 'Missing': lambda m: _SuperDictOf(m, format_value=str), - 'Differences': _MatchCommonKeys, - } + "Extra": _SubDictOf, + "Missing": lambda m: _SuperDictOf(m, format_value=str), + "Differences": _MatchCommonKeys, + } def format_expected(self, expected) -> str: return _format_matcher_dict(expected) @@ -200,9 +198,9 @@ class ContainsDict(_CombinedMatcher): """ matcher_factories = { - 'Missing': lambda m: _SuperDictOf(m, format_value=str), - 'Differences': _MatchCommonKeys, - } + "Missing": lambda m: _SuperDictOf(m, format_value=str), + "Differences": _MatchCommonKeys, + } def format_expected(self, expected): return _format_matcher_dict(expected) @@ -224,9 +222,9 @@ class ContainedByDict(_CombinedMatcher): """ matcher_factories = { - 'Extra': _SubDictOf, - 'Differences': _MatchCommonKeys, - } + "Extra": _SubDictOf, + "Differences": _MatchCommonKeys, + } def format_expected(self, expected): return _format_matcher_dict(expected) @@ -251,14 +249,15 @@ def __init__(self, *expected): self.expected = list(expected) def __str__(self): - return "KeysEqual(%s)" % ', '.join(map(repr, self.expected)) + return "KeysEqual(%s)" % ", ".join(map(repr, self.expected)) def match(self, matchee): from ._basic import _BinaryMismatch, Equals + expected = sorted(self.expected) matched = Equals(expected).match(sorted(matchee.keys())) if matched: return AnnotatedMismatch( - 'Keys not equal', - _BinaryMismatch(expected, 'does not match', matchee)) + "Keys not equal", _BinaryMismatch(expected, "does not match", matchee) + ) return None diff --git a/testtools/matchers/_doctest.py b/testtools/matchers/_doctest.py index 8ae31ffe..89cec67c 100644 --- a/testtools/matchers/_doctest.py +++ b/testtools/matchers/_doctest.py @@ -1,8 +1,8 @@ # Copyright (c) 2009-2012 testtools developers. See LICENSE for details. __all__ = [ - 'DocTestMatches', - ] + "DocTestMatches", +] import doctest import re @@ -38,11 +38,14 @@ def _toAscii(self, s): # Only do this overriding hackery if doctest has a broken _input function if getattr(doctest, "_encoding", None) is not None: from types import FunctionType as __F + __f = doctest.OutputChecker.output_difference.im_func __g = dict(__f.func_globals) + def _indent(s, indent=4, _pattern=re.compile("^(?!$)", re.MULTILINE)): """Prepend non-empty lines in ``s`` with ``indent`` number of spaces""" - return _pattern.sub(indent*" ", s) + return _pattern.sub(indent * " ", s) + __g["_indent"] = _indent output_difference = __F(__f.func_code, __g, "output_difference") del __F, __f, __g, _indent @@ -58,9 +61,9 @@ def __init__(self, example, flags=0): :param flags: doctest comparison flags to match on. e.g. doctest.ELLIPSIS. """ - if not example.endswith('\n'): - example += '\n' - self.want = example # required variable name by doctest. + if not example.endswith("\n"): + example += "\n" + self.want = example # required variable name by doctest. self.flags = flags self._checker = _NonManglingOutputChecker() @@ -69,12 +72,12 @@ def __str__(self): flagstr = ", flags=%d" % self.flags else: flagstr = "" - return f'DocTestMatches({self.want!r}{flagstr})' + return f"DocTestMatches({self.want!r}{flagstr})" def _with_nl(self, actual): result = self.want.__class__(actual) - if not result.endswith('\n'): - result += '\n' + if not result.endswith("\n"): + result += "\n" return result def match(self, actual): diff --git a/testtools/matchers/_exception.py b/testtools/matchers/_exception.py index fe2c7669..a07eecd4 100644 --- a/testtools/matchers/_exception.py +++ b/testtools/matchers/_exception.py @@ -1,10 +1,10 @@ # Copyright (c) 2009-2012 testtools developers. See LICENSE for details. __all__ = [ - 'MatchesException', - 'Raises', - 'raises', - ] + "MatchesException", + "Raises", + "raises", +] import sys @@ -13,7 +13,7 @@ from ._impl import ( Matcher, Mismatch, - ) +) _error_repr = BaseException.__repr__ @@ -50,21 +50,25 @@ def __init__(self, exception, value_re=None): value_re = AfterPreproccessing(str, MatchesRegex(value_re), False) self.value_re = value_re expected_type = type(self.expected) - self._is_instance = not any(issubclass(expected_type, class_type) - for class_type in (type, tuple)) + self._is_instance = not any( + issubclass(expected_type, class_type) for class_type in (type, tuple) + ) def match(self, other): if type(other) != tuple: - return Mismatch('%r is not an exc_info tuple' % other) + return Mismatch("%r is not an exc_info tuple" % other) expected_class = self.expected if self._is_instance: expected_class = expected_class.__class__ if not issubclass(other[0], expected_class): - return Mismatch(f'{other[0]!r} is not a {expected_class!r}') + return Mismatch(f"{other[0]!r} is not a {expected_class!r}") if self._is_instance: if other[1].args != self.expected.args: - return Mismatch('{} has different arguments to {}.'.format( - _error_repr(other[1]), _error_repr(self.expected))) + return Mismatch( + "{} has different arguments to {}.".format( + _error_repr(other[1]), _error_repr(self.expected) + ) + ) elif self.value_re is not None: return self.value_re.match(other[1]) @@ -95,7 +99,7 @@ def __init__(self, exception_matcher=None): def match(self, matchee): try: result = matchee() - return Mismatch(f'{matchee!r} returned {result!r}') + return Mismatch(f"{matchee!r} returned {result!r}") # Catch all exceptions: Raises() should be able to match a # KeyboardInterrupt or SystemExit. except BaseException: @@ -117,7 +121,7 @@ def match(self, matchee): return mismatch def __str__(self): - return 'Raises()' + return "Raises()" def raises(exception): diff --git a/testtools/matchers/_filesystem.py b/testtools/matchers/_filesystem.py index 0b3eb537..d769106e 100644 --- a/testtools/matchers/_filesystem.py +++ b/testtools/matchers/_filesystem.py @@ -3,14 +3,14 @@ """Matchers for things related to the filesystem.""" __all__ = [ - 'FileContains', - 'DirExists', - 'FileExists', - 'HasPermissions', - 'PathExists', - 'SamePath', - 'TarballContains', - ] + "FileContains", + "DirExists", + "FileExists", + "HasPermissions", + "PathExists", + "SamePath", + "TarballContains", +] import os import tarfile @@ -19,10 +19,10 @@ from ._higherorder import ( MatchesAll, MatchesPredicate, - ) +) from ._impl import ( Matcher, - ) +) def PathExists(): @@ -40,7 +40,8 @@ def DirExists(): return MatchesAll( PathExists(), MatchesPredicate(os.path.isdir, "%s is not a directory."), - first_only=True) + first_only=True, + ) def FileExists(): @@ -48,7 +49,8 @@ def FileExists(): return MatchesAll( PathExists(), MatchesPredicate(os.path.isfile, "%s is not a file."), - first_only=True) + first_only=True, + ) class DirContains(Matcher): @@ -72,11 +74,11 @@ def __init__(self, filenames=None, matcher=None): against this matcher. """ if filenames == matcher is None: - raise AssertionError( - "Must provide one of `filenames` or `matcher`.") + raise AssertionError("Must provide one of `filenames` or `matcher`.") if None not in (filenames, matcher): raise AssertionError( - "Must provide either `filenames` or `matcher`, not both.") + "Must provide either `filenames` or `matcher`, not both." + ) if filenames is None: self.matcher = matcher else: @@ -106,11 +108,11 @@ def __init__(self, contents=None, matcher=None): this matcher. """ if contents == matcher is None: - raise AssertionError( - "Must provide one of `contents` or `matcher`.") + raise AssertionError("Must provide one of `contents` or `matcher`.") if None not in (contents, matcher): raise AssertionError( - "Must provide either `contents` or `matcher`, not both.") + "Must provide either `contents` or `matcher`, not both." + ) if matcher is None: self.matcher = Equals(contents) else: @@ -165,6 +167,7 @@ def __init__(self, path): def match(self, other_path): def f(x): return os.path.abspath(os.path.realpath(x)) + return Equals(f(self.path)).match(f(other_path)) diff --git a/testtools/matchers/_higherorder.py b/testtools/matchers/_higherorder.py index 4871ae60..2de64a60 100644 --- a/testtools/matchers/_higherorder.py +++ b/testtools/matchers/_higherorder.py @@ -1,14 +1,14 @@ # Copyright (c) 2009-2012 testtools developers. See LICENSE for details. __all__ = [ - 'AfterPreprocessing', - 'AllMatch', - 'Annotate', - 'AnyMatch', - 'MatchesAny', - 'MatchesAll', - 'Not', - ] + "AfterPreprocessing", + "AllMatch", + "Annotate", + "AnyMatch", + "MatchesAny", + "MatchesAll", + "Not", +] import types @@ -16,7 +16,7 @@ Matcher, Mismatch, MismatchDecorator, - ) +) class MatchesAny: @@ -35,8 +35,7 @@ def match(self, matchee): return MismatchesAll(results) def __str__(self): - return "MatchesAny(%s)" % ', '.join([ - str(matcher) for matcher in self.matchers]) + return "MatchesAny(%s)" % ", ".join([str(matcher) for matcher in self.matchers]) class MatchesAll: @@ -51,10 +50,10 @@ def __init__(self, *matchers, **options): reported. """ self.matchers = matchers - self.first_only = options.get('first_only', False) + self.first_only = options.get("first_only", False) def __str__(self): - return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers)) + return "MatchesAll(%s)" % ", ".join(map(str, self.matchers)) def match(self, matchee): results = [] @@ -85,7 +84,7 @@ def describe(self): descriptions.append(mismatch.describe()) if self._wrap: descriptions.append("]") - return '\n'.join(descriptions) + return "\n".join(descriptions) class Not: @@ -95,7 +94,7 @@ def __init__(self, matcher): self.matcher = matcher def __str__(self): - return f'Not({self.matcher})' + return f"Not({self.matcher})" def match(self, other): mismatch = self.matcher.match(other) @@ -134,7 +133,7 @@ def if_message(cls, annotation, matcher): return cls(annotation, matcher) def __str__(self): - return f'Annotate({self.annotation!r}, {self.matcher})' + return f"Annotate({self.annotation!r}, {self.matcher})" def match(self, other): mismatch = self.matcher.match(other) @@ -151,20 +150,19 @@ def __init__(self, annotation, mismatch): self.mismatch = mismatch def describe(self): - return f'{self.original.describe()}: {self.annotation}' + return f"{self.original.describe()}: {self.annotation}" AnnotatedMismatch = PostfixedMismatch class PrefixedMismatch(MismatchDecorator): - def __init__(self, prefix, mismatch): super().__init__(mismatch) self.prefix = prefix def describe(self): - return f'{self.prefix}: {self.original.describe()}' + return f"{self.prefix}: {self.original.describe()}" class AfterPreprocessing: @@ -195,19 +193,20 @@ def __init__(self, preprocessor, matcher, annotate=True): def _str_preprocessor(self): if isinstance(self.preprocessor, types.FunctionType): - return '' % self.preprocessor.__name__ + return "" % self.preprocessor.__name__ return str(self.preprocessor) def __str__(self): return "AfterPreprocessing({}, {})".format( - self._str_preprocessor(), self.matcher) + self._str_preprocessor(), self.matcher + ) def match(self, value): after = self.preprocessor(value) if self.annotate: matcher = Annotate( - f"after {self._str_preprocessor()} on {value!r}", - self.matcher) + f"after {self._str_preprocessor()} on {value!r}", self.matcher + ) else: matcher = self.matcher return matcher.match(after) @@ -225,7 +224,7 @@ def __init__(self, matcher): self.matcher = matcher def __str__(self): - return f'AllMatch({self.matcher})' + return f"AllMatch({self.matcher})" def match(self, values): mismatches = [] @@ -244,7 +243,7 @@ def __init__(self, matcher): self.matcher = matcher def __str__(self): - return f'AnyMatch({self.matcher})' + return f"AnyMatch({self.matcher})" def match(self, values): mismatches = [] @@ -282,8 +281,9 @@ def __init__(self, predicate, message): self.message = message def __str__(self): - return '{}({!r}, {!r})'.format( - self.__class__.__name__, self.predicate, self.message) + return "{}({!r}, {!r})".format( + self.__class__.__name__, self.predicate, self.message + ) def match(self, x): if not self.predicate(x): @@ -315,14 +315,14 @@ def MatchesPredicateWithParams(predicate, message, name=None): :param message: A format string for describing mis-matches. :param name: Optional replacement name for the matcher. """ + def construct_matcher(*args, **kwargs): - return _MatchesPredicateWithParams( - predicate, message, name, *args, **kwargs) + return _MatchesPredicateWithParams(predicate, message, name, *args, **kwargs) + return construct_matcher class _MatchesPredicateWithParams(Matcher): - def __init__(self, predicate, message, name, *args, **kwargs): """Create a ``MatchesPredicateWithParams`` matcher. @@ -356,13 +356,13 @@ def __str__(self): kwargs = ["%s=%s" % item for item in self.kwargs.items()] args = ", ".join(args + kwargs) if self.name is None: - name = 'MatchesPredicateWithParams({!r}, {!r})'.format( - self.predicate, self.message) + name = "MatchesPredicateWithParams({!r}, {!r})".format( + self.predicate, self.message + ) else: name = self.name - return f'{name}({args})' + return f"{name}({args})" def match(self, x): if not self.predicate(x, *self.args, **self.kwargs): - return Mismatch( - self.message.format(*((x,) + self.args), **self.kwargs)) + return Mismatch(self.message.format(*((x,) + self.args), **self.kwargs)) diff --git a/testtools/matchers/_impl.py b/testtools/matchers/_impl.py index b7dc4fa5..f6a2174e 100644 --- a/testtools/matchers/_impl.py +++ b/testtools/matchers/_impl.py @@ -11,15 +11,13 @@ """ __all__ = [ - 'Matcher', - 'Mismatch', - 'MismatchDecorator', - 'MismatchError', - ] + "Matcher", + "Mismatch", + "MismatchDecorator", + "MismatchError", +] -from testtools.compat import ( - text_repr - ) +from testtools.compat import text_repr class Matcher: @@ -37,8 +35,7 @@ class Matcher: """ def match(self, something): - """Return None if this matcher matches something, a Mismatch otherwise. - """ + """Return None if this matcher matches something, a Mismatch otherwise.""" raise NotImplementedError(self.match) def __str__(self): @@ -95,11 +92,12 @@ def get_details(self): to the result. For more information see the API to which items from this dict are passed testtools.TestCase.addDetail. """ - return getattr(self, '_details', {}) + return getattr(self, "_details", {}) def __repr__(self): - return "".format( - id(self), self.__dict__) + return "".format( + id(self), self.__dict__ + ) class MismatchError(AssertionError): @@ -126,9 +124,11 @@ def __str__(self): matchee = text_repr(self.matchee, multiline=False) else: matchee = repr(self.matchee) - return ( - 'Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n' - % (matchee, self.matcher, difference)) + return "Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n" % ( + matchee, + self.matcher, + difference, + ) else: return difference @@ -149,7 +149,7 @@ def __init__(self, original): self.original = original def __repr__(self): - return f'' + return f"" def describe(self): return self.original.describe() diff --git a/testtools/matchers/_warnings.py b/testtools/matchers/_warnings.py index 82ca4d80..15355d70 100644 --- a/testtools/matchers/_warnings.py +++ b/testtools/matchers/_warnings.py @@ -1,9 +1,6 @@ # Copyright (c) 2009-2016 testtools developers. See LICENSE for details. -__all__ = [ - 'Warnings', - 'WarningMessage', - 'IsDeprecated'] +__all__ = ["Warnings", "WarningMessage", "IsDeprecated"] import warnings @@ -13,12 +10,11 @@ from ._higherorder import ( AfterPreprocessing, Annotate, - ) +) from ._impl import Mismatch -def WarningMessage(category_type, message=None, filename=None, lineno=None, - line=None): +def WarningMessage(category_type, message=None, filename=None, lineno=None, line=None): r""" Create a matcher that will match `warnings.WarningMessage`\s. @@ -48,27 +44,21 @@ def WarningMessage(category_type, message=None, filename=None, lineno=None, lineno_matcher = lineno or Always() line_matcher = line or Always() return MatchesStructure( - category=Annotate( - "Warning's category type does not match", - category_matcher), + category=Annotate("Warning's category type does not match", category_matcher), message=Annotate( - "Warning's message does not match", - AfterPreprocessing(str, message_matcher)), - filename=Annotate( - "Warning's filname does not match", - filename_matcher), - lineno=Annotate( - "Warning's line number does not match", - lineno_matcher), - line=Annotate( - "Warning's source line does not match", - line_matcher)) + "Warning's message does not match", AfterPreprocessing(str, message_matcher) + ), + filename=Annotate("Warning's filname does not match", filename_matcher), + lineno=Annotate("Warning's line number does not match", lineno_matcher), + line=Annotate("Warning's source line does not match", line_matcher), + ) class Warnings: """ Match if the matchee produces warnings. """ + def __init__(self, warnings_matcher=None): """ Create a Warnings matcher. @@ -81,15 +71,15 @@ def __init__(self, warnings_matcher=None): def match(self, matchee): with warnings.catch_warnings(record=True) as w: - warnings.simplefilter('always') + warnings.simplefilter("always") matchee() if self.warnings_matcher is not None: return self.warnings_matcher.match(w) elif not w: - return Mismatch('Expected at least one warning, got none') + return Mismatch("Expected at least one warning, got none") def __str__(self): - return f'Warnings({self.warnings_matcher!s})' + return f"Warnings({self.warnings_matcher!s})" def IsDeprecated(message): @@ -100,7 +90,7 @@ def IsDeprecated(message): :param message: Matcher for the warning message. """ return Warnings( - MatchesListwise([ - WarningMessage( - category_type=DeprecationWarning, - message=message)])) + MatchesListwise( + [WarningMessage(category_type=DeprecationWarning, message=message)] + ) + ) diff --git a/testtools/monkey.py b/testtools/monkey.py index 483bd9a6..56768e5e 100644 --- a/testtools/monkey.py +++ b/testtools/monkey.py @@ -3,9 +3,9 @@ """Helpers for monkey-patching Python code.""" __all__ = [ - 'MonkeyPatcher', - 'patch', - ] + "MonkeyPatcher", + "patch", +] class MonkeyPatcher: diff --git a/testtools/run.py b/testtools/run.py index baa1230e..5e49f1ed 100755 --- a/testtools/run.py +++ b/testtools/run.py @@ -21,7 +21,7 @@ defaultTestLoader = unittest.defaultTestLoader defaultTestLoaderCls = unittest.TestLoader have_discover = True -discover_impl = getattr(unittest, 'loader', None) +discover_impl = getattr(unittest, "loader", None) # Kept for API compatibility, but no longer used. BUFFEROUTPUT = "" @@ -29,6 +29,7 @@ FAILFAST = "" USAGE_AS_MAIN = "" + def list_test(test): """Return the test ids that would be run if test() was run. @@ -43,16 +44,16 @@ def list_test(test): describing things that failed to import. """ unittest_import_strs = { - 'unittest.loader.ModuleImportFailure.', - 'discover.ModuleImportFailure.' - } + "unittest.loader.ModuleImportFailure.", + "discover.ModuleImportFailure.", + } test_ids = [] errors = [] for test in iterate_tests(test): # Much ugly. for prefix in unittest_import_strs: if test.id().startswith(prefix): - errors.append(test.id()[len(prefix):]) + errors.append(test.id()[len(prefix) :]) break else: test_ids.append(test.id()) @@ -60,10 +61,17 @@ def list_test(test): class TestToolsTestRunner: - """ A thunk object to support unittest.TestProgram.""" - - def __init__(self, verbosity=None, failfast=None, buffer=None, - stdout=None, tb_locals=False, **kwargs): + """A thunk object to support unittest.TestProgram.""" + + def __init__( + self, + verbosity=None, + failfast=None, + buffer=None, + stdout=None, + tb_locals=False, + **kwargs, + ): """Create a TestToolsTestRunner. :param verbosity: Ignored. @@ -82,18 +90,20 @@ def list(self, test, loader): """List the tests that would be run if test() was run.""" test_ids, _ = list_test(test) for test_id in test_ids: - self.stdout.write('%s\n' % test_id) + self.stdout.write("%s\n" % test_id) errors = loader.errors if errors: for test_id in errors: - self.stdout.write('%s\n' % test_id) + self.stdout.write("%s\n" % test_id) sys.exit(2) def run(self, test): "Run the given test case or test suite." result = TextTestResult( - unicode_output_stream(self.stdout), failfast=self.failfast, - tb_locals=self.tb_locals) + unicode_output_stream(self.stdout), + failfast=self.failfast, + tb_locals=self.tb_locals, + ) result.startTestRun() try: return test.run(result) @@ -114,24 +124,35 @@ def run(self, test): class TestProgram(unittest.TestProgram): """A command-line program that runs a set of tests; this is primarily - for making test modules conveniently executable. + for making test modules conveniently executable. """ # defaults for testing - module=None + module = None verbosity = 1 failfast = catchbreak = buffer = progName = None _discovery_parser = None - def __init__(self, module=__name__, defaultTest=None, argv=None, - testRunner=None, testLoader=defaultTestLoader, - exit=True, verbosity=1, failfast=None, catchbreak=None, - buffer=None, stdout=None, tb_locals=False): + def __init__( + self, + module=__name__, + defaultTest=None, + argv=None, + testRunner=None, + testLoader=defaultTestLoader, + exit=True, + verbosity=1, + failfast=None, + catchbreak=None, + buffer=None, + stdout=None, + tb_locals=False, + ): if module == __name__: self.module = None elif isinstance(module, str): self.module = __import__(module) - for part in module.split('.')[1:]: + for part in module.split(".")[1:]: self.module = getattr(self.module, part) else: self.module = module @@ -154,9 +175,9 @@ def __init__(self, module=__name__, defaultTest=None, argv=None, self.testRunner = testRunner self.testLoader = testLoader progName = argv[0] - if progName.endswith('%srun.py' % os.path.sep): + if progName.endswith("%srun.py" % os.path.sep): elements = progName.split(os.path.sep) - progName = '%s.run' % elements[-2] + progName = "%s.run" % elements[-2] else: progName = os.path.basename(argv[0]) self.progName = progName @@ -167,36 +188,46 @@ def __init__(self, module=__name__, defaultTest=None, argv=None, # OptimisingTestSuite.add, but with a standard protocol). # This is needed because the load_tests hook allows arbitrary # suites, even if that is rarely used. - source = open(self.load_list, 'rb') + source = open(self.load_list, "rb") try: lines = source.readlines() finally: source.close() - test_ids = {line.strip().decode('utf-8') for line in lines} + test_ids = {line.strip().decode("utf-8") for line in lines} self.test = filter_by_ids(self.test, test_ids) # XXX: Local edit (see http://bugs.python.org/issue22860) if not self.listtests: self.runTests() else: runner = self._get_runner() - if hasattr(runner, 'list'): + if hasattr(runner, "list"): try: runner.list(self.test, loader=self.testLoader) except TypeError: runner.list(self.test) else: for test in iterate_tests(self.test): - self.stdout.write('%s\n' % test.id()) + self.stdout.write("%s\n" % test.id()) del self.testLoader.errors[:] def _getParentArgParser(self): parser = super()._getParentArgParser() # XXX: Local edit (see http://bugs.python.org/issue22860) - parser.add_argument('-l', '--list', dest='listtests', default=False, - action='store_true', help='List tests rather than executing them') - parser.add_argument('--load-list', dest='load_list', default=None, - help='Specifies a file containing test ids, only tests matching ' - 'those ids are executed') + parser.add_argument( + "-l", + "--list", + dest="listtests", + default=False, + action="store_true", + help="List tests rather than executing them", + ) + parser.add_argument( + "--load-list", + dest="load_list", + default=None, + help="Specifies a file containing test ids, only tests matching " + "those ids are executed", + ) return parser def _do_discovery(self, argv, Loader=None): @@ -206,8 +237,7 @@ def _do_discovery(self, argv, Loader=None): def runTests(self): # XXX: Local edit (see http://bugs.python.org/issue22860) - if (self.catchbreak - and getattr(unittest, 'installHandler', None) is not None): + if self.catchbreak and getattr(unittest, "installHandler", None) is not None: unittest.installHandler() testRunner = self._get_runner() self.result = testRunner.run(self.test) @@ -220,24 +250,28 @@ def _get_runner(self): self.testRunner = TestToolsTestRunner try: try: - testRunner = self.testRunner(verbosity=self.verbosity, - failfast=self.failfast, - buffer=self.buffer, - stdout=self.stdout, - tb_locals=self.tb_locals) + testRunner = self.testRunner( + verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + stdout=self.stdout, + tb_locals=self.tb_locals, + ) except TypeError: # didn't accept the tb_locals parameter - testRunner = self.testRunner(verbosity=self.verbosity, - failfast=self.failfast, - buffer=self.buffer, - stdout=self.stdout) + testRunner = self.testRunner( + verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + stdout=self.stdout, + ) except TypeError: # didn't accept the verbosity, buffer, failfast or stdout arguments # Try with the prior contract try: - testRunner = self.testRunner(verbosity=self.verbosity, - failfast=self.failfast, - buffer=self.buffer) + testRunner = self.testRunner( + verbosity=self.verbosity, failfast=self.failfast, buffer=self.buffer + ) except TypeError: # Now try calling it with defaults try: @@ -248,12 +282,14 @@ def _get_runner(self): return testRunner - ################ + def main(argv, stdout): - TestProgram(argv=argv, testRunner=partial(TestToolsTestRunner, stdout=stdout), - stdout=stdout) + TestProgram( + argv=argv, testRunner=partial(TestToolsTestRunner, stdout=stdout), stdout=stdout + ) + -if __name__ == '__main__': +if __name__ == "__main__": main(sys.argv, sys.stdout) diff --git a/testtools/runtest.py b/testtools/runtest.py index db95c884..f3ecaf9e 100644 --- a/testtools/runtest.py +++ b/testtools/runtest.py @@ -3,9 +3,9 @@ """Individual test case execution.""" __all__ = [ - 'MultipleExceptions', - 'RunTest', - ] + "MultipleExceptions", + "RunTest", +] import sys @@ -103,8 +103,7 @@ def _run_prepared_result(self, result): self.result = result try: self._exceptions = [] - self.case.__testtools_tb_locals__ = getattr( - result, 'tb_locals', False) + self.case.__testtools_tb_locals__ = getattr(result, "tb_locals", False) self._run_core() if self._exceptions: # One or more caught exceptions, now trigger the test's @@ -124,17 +123,19 @@ def _run_prepared_result(self, result): def _run_core(self): """Run the user supplied test code.""" test_method = self.case._get_test_method() - skip_case = getattr(self.case, '__unittest_skip__', False) - if skip_case or getattr(test_method, '__unittest_skip__', False): + skip_case = getattr(self.case, "__unittest_skip__", False) + if skip_case or getattr(test_method, "__unittest_skip__", False): self.result.addSkip( self.case, - reason=getattr(self.case if skip_case else test_method, - '__unittest_skip_why__', None) + reason=getattr( + self.case if skip_case else test_method, + "__unittest_skip_why__", + None, + ), ) return - if self.exception_caught == self._run_user(self.case._run_setup, - self.result): + if self.exception_caught == self._run_user(self.case._run_setup, self.result): # Don't run the test method if we failed getting here. self._run_cleanups(self.result) return @@ -143,25 +144,29 @@ def _run_core(self): failed = False try: if self.exception_caught == self._run_user( - self.case._run_test_method, self.result): + self.case._run_test_method, self.result + ): failed = True finally: try: if self.exception_caught == self._run_user( - self.case._run_teardown, self.result): + self.case._run_teardown, self.result + ): failed = True finally: try: if self.exception_caught == self._run_user( - self._run_cleanups, self.result): + self._run_cleanups, self.result + ): failed = True finally: - if getattr(self.case, 'force_failure', None): + if getattr(self.case, "force_failure", None): self._run_user(_raise_force_fail_error) failed = True if not failed: - self.result.addSuccess(self.case, - details=self.case.getDetails()) + self.result.addSuccess( + self.case, details=self.case.getDetails() + ) def _run_cleanups(self, result): """Run the cleanups that have been added with addCleanup. @@ -174,8 +179,7 @@ def _run_cleanups(self, result): failing = False while self.case._cleanups: function, arguments, keywordArguments = self.case._cleanups.pop() - got_exception = self._run_user( - function, *arguments, **keywordArguments) + got_exception = self._run_user(function, *arguments, **keywordArguments) if got_exception == self.exception_caught: failing = True if failing: @@ -194,7 +198,7 @@ def _run_user(self, fn, *args, **kwargs): except BaseException: return self._got_user_exception(sys.exc_info()) - def _got_user_exception(self, exc_info, tb_label='traceback'): + def _got_user_exception(self, exc_info, tb_label="traceback"): """Called when user code raises an exception. If 'exc_info' is a `MultipleExceptions`, then we recurse into it diff --git a/testtools/testcase.py b/testtools/testcase.py index faa0672d..08b158b6 100644 --- a/testtools/testcase.py +++ b/testtools/testcase.py @@ -3,17 +3,17 @@ """Test case related stuff.""" __all__ = [ - 'attr', - 'clone_test_with_new_id', - 'ExpectedException', - 'gather_details', - 'run_test_with', - 'skip', - 'skipIf', - 'skipUnless', - 'TestCase', - 'unique_text_generator', - ] + "attr", + "clone_test_with_new_id", + "ExpectedException", + "gather_details", + "run_test_with", + "skip", + "skipIf", + "skipUnless", + "TestCase", + "unique_text_generator", +] import copy import functools @@ -37,7 +37,7 @@ IsInstance, Not, Raises, - ) +) from testtools.matchers._basic import _FlippedEquals from testtools.monkey import patch from testtools.runtest import ( @@ -52,9 +52,10 @@ class TestSkipped(SkipTest): """Raised within TestCase.run() when a test is skipped.""" + def __init__(self, *args, **kwargs): warnings.warn( - 'Use SkipTest from unittest instead.', + "Use SkipTest from unittest instead.", DeprecationWarning, stacklevel=2, ) @@ -88,6 +89,7 @@ def wrapper(*args, **kwargs): except Exception: raise _ExpectedFailure(sys.exc_info()) raise _UnexpectedSuccess + return wrapper @@ -116,20 +118,23 @@ def test_foo(self): :return: A decorator to be used for marking a test as needing a special runner. """ + def decorator(function): # Set an attribute on 'function' which will inform TestCase how to # make the runner. def _run_test_with(case, handlers=None, last_resort=None): try: return test_runner( - case, handlers=handlers, last_resort=last_resort, - **kwargs) + case, handlers=handlers, last_resort=last_resort, **kwargs + ) except TypeError: # Backwards compat: if we can't call the constructor # with last_resort, try without that. return test_runner(case, handlers=handlers, **kwargs) + function._run_test_with = _run_test_with return function + return decorator @@ -145,8 +150,10 @@ def _copy_content(content_object): ``content_object`` and a non-volatile copy of its content. """ content_bytes = list(content_object.iter_bytes()) + def content_callback(): return content_bytes + return content.Content(content_object.content_type, content_callback) @@ -163,7 +170,7 @@ def gather_details(source_dict, target_dict): new_name = name disambiguator = itertools.count(1) while new_name in target_dict: - new_name = '%s-%d' % (name, next(disambiguator)) + new_name = "%s-%d" % (name, next(disambiguator)) name = new_name target_dict[name] = _copy_content(content_object) @@ -171,7 +178,7 @@ def gather_details(source_dict, target_dict): # Circular import: fixtures imports gather_details from here, we import # fixtures, leading to gather_details not being available and fixtures being # unable to import it. -fixtures = try_import('fixtures') +fixtures = try_import("fixtures") def _mods(i, mod): @@ -184,7 +191,7 @@ def _mods(i, mod): def _unique_text(base_cp, cp_range, index): - s = '' + s = "" for m in _mods(index, cp_range): s += chr(base_cp + m) return s @@ -203,12 +210,12 @@ def unique_text_generator(prefix): # 0x1e00 is the start of a range of glyphs that are easy to see are # unicode since they've got circles and dots and other diacriticals. # 0x1eff is the end of the range of these diacritical chars. - BASE_CP = 0x1e00 - CP_RANGE = 0x1f00 - BASE_CP + BASE_CP = 0x1E00 + CP_RANGE = 0x1F00 - BASE_CP index = 0 while True: unique_text = _unique_text(BASE_CP, CP_RANGE, index) - yield f'{prefix}-{unique_text}' + yield f"{prefix}-{unique_text}" index = index + 1 @@ -238,15 +245,14 @@ def __init__(self, *args, **kwargs): when run() is invoked, so will be fresh each time. Overrides ``TestCase.run_tests_with`` if given. """ - runTest = kwargs.pop('runTest', None) + runTest = kwargs.pop("runTest", None) super().__init__(*args, **kwargs) self._reset() test_method = self._get_test_method() if runTest is None: - runTest = getattr( - test_method, '_run_test_with', self.run_tests_with) + runTest = getattr(test_method, "_run_test_with", self.run_tests_with) self.__RunTest = runTest - if getattr(test_method, '__unittest_expecting_failure__', False): + if getattr(test_method, "__unittest_expecting_failure__", False): setattr(self, self._testMethodName, _expectedFailure(test_method)) # Used internally for onException processing - used to gather extra # data from exceptions. @@ -258,7 +264,7 @@ def __init__(self, *args, **kwargs): (_ExpectedFailure, self._report_expected_failure), (_UnexpectedSuccess, self._report_unexpected_success), (Exception, self._report_error), - ] + ] def _reset(self): """Reset the test case as if it had never been run.""" @@ -274,10 +280,10 @@ def _reset(self): self.__details = None def __eq__(self, other): - eq = getattr(unittest.TestCase, '__eq__', None) + eq = getattr(unittest.TestCase, "__eq__", None) if eq is not None and not unittest.TestCase.__eq__(self, other): return False - return self.__dict__ == getattr(other, '__dict__', None) + return self.__dict__ == getattr(other, "__dict__", None) # We need to explicitly set this since we're overriding __eq__ # https://docs.python.org/3/reference/datamodel.html#object.__hash__ @@ -290,11 +296,12 @@ def __repr__(self): def _deprecate(original_func): def deprecated_func(*args, **kwargs): warnings.warn( - 'Please use {0} instead.'.format(original_func.__name__), + "Please use {0} instead.".format(original_func.__name__), DeprecationWarning, stacklevel=2, ) return original_func(*args, **kwargs) + return deprecated_func def addDetail(self, name, content_object): @@ -351,15 +358,17 @@ def skipTest(self, reason): def skip(self, reason): """DEPRECATED: Use skipTest instead.""" warnings.warn( - 'Only valid in 1.8.1 and earlier. Use skipTest instead.', - DeprecationWarning, stacklevel=2) + "Only valid in 1.8.1 and earlier. Use skipTest instead.", + DeprecationWarning, + stacklevel=2, + ) self.skipTest(reason) def _formatTypes(self, classOrIterable): """Format a class or a bunch of classes for display in an error.""" - className = getattr(classOrIterable, '__name__', None) + className = getattr(classOrIterable, "__name__", None) if className is None: - className = ', '.join(klass.__name__ for klass in classOrIterable) + className = ", ".join(klass.__name__ for klass in classOrIterable) return className def addCleanup(self, function, *arguments, **keywordArguments): @@ -397,9 +406,9 @@ def addOnException(self, handler): self.__exception_handlers.append(handler) def _add_reason(self, reason): - self.addDetail('reason', content.text_content(reason)) + self.addDetail("reason", content.text_content(reason)) - def assertEqual(self, expected, observed, message=''): + def assertEqual(self, expected, observed, message=""): """Assert that 'expected' is equal to 'observed'. :param expected: The expected value. @@ -411,11 +420,11 @@ def assertEqual(self, expected, observed, message=''): failUnlessEqual = assertEquals = _deprecate(assertEqual) - def assertIn(self, needle, haystack, message=''): + def assertIn(self, needle, haystack, message=""): """Assert that needle is in haystack.""" self.assertThat(haystack, Contains(needle), message) - def assertIsNone(self, observed, message=''): + def assertIsNone(self, observed, message=""): """Assert that 'observed' is equal to None. :param observed: The observed value. @@ -424,7 +433,7 @@ def assertIsNone(self, observed, message=''): matcher = Is(None) self.assertThat(observed, matcher, message) - def assertIsNotNone(self, observed, message=''): + def assertIsNotNone(self, observed, message=""): """Assert that 'observed' is not equal to None. :param observed: The observed value. @@ -433,7 +442,7 @@ def assertIsNotNone(self, observed, message=''): matcher = Not(Is(None)) self.assertThat(observed, matcher, message) - def assertIs(self, expected, observed, message=''): + def assertIs(self, expected, observed, message=""): """Assert that 'expected' is 'observed'. :param expected: The expected value. @@ -443,12 +452,12 @@ def assertIs(self, expected, observed, message=''): matcher = Is(expected) self.assertThat(observed, matcher, message) - def assertIsNot(self, expected, observed, message=''): + def assertIsNot(self, expected, observed, message=""): """Assert that 'expected' is not 'observed'.""" matcher = Not(Is(expected)) self.assertThat(observed, matcher, message) - def assertNotIn(self, needle, haystack, message=''): + def assertNotIn(self, needle, haystack, message=""): """Assert that needle is not in haystack.""" matcher = Not(Contains(needle)) self.assertThat(haystack, matcher, message) @@ -462,12 +471,13 @@ def assertIsInstance(self, obj, klass, msg=None): def assertRaises(self, excClass, callableObj, *args, **kwargs): """Fail unless an exception of class excClass is thrown - by callableObj when invoked with arguments args and keyword - arguments kwargs. If a different type of exception is - thrown, it will not be caught, and the test case will be - deemed to have suffered an error, exactly as for an - unexpected exception. + by callableObj when invoked with arguments args and keyword + arguments kwargs. If a different type of exception is + thrown, it will not be caught, and the test case will be + deemed to have suffered an error, exactly as for an + unexpected exception. """ + class ReRaiseOtherTypes: def match(self, matchee): if not issubclass(matchee[0], excClass): @@ -476,17 +486,18 @@ def match(self, matchee): class CaptureMatchee: def match(self, matchee): self.matchee = matchee[1] + capture = CaptureMatchee() matcher = Raises( - MatchesAll(ReRaiseOtherTypes(), - MatchesException(excClass), capture)) + MatchesAll(ReRaiseOtherTypes(), MatchesException(excClass), capture) + ) our_callable = Nullary(callableObj, *args, **kwargs) self.assertThat(our_callable, matcher) return capture.matchee failUnlessRaises = _deprecate(assertRaises) - def assertThat(self, matchee, matcher, message='', verbose=False): + def assertThat(self, matchee, matcher, message="", verbose=False): """Assert that matchee is matched by matcher. :param matchee: An object to match with matcher. @@ -520,7 +531,7 @@ def addDetailUniqueName(self, name, content_object): suffix += 1 self.addDetail(full_name, content_object) - def expectThat(self, matchee, matcher, message='', verbose=False): + def expectThat(self, matchee, matcher, message="", verbose=False): """Check that matchee is matched by matcher, but delay the assertion failure. This method behaves similarly to ``assertThat``, except that a failed @@ -540,7 +551,7 @@ def expectThat(self, matchee, matcher, message='', verbose=False): "Failed expectation", content.StacktraceContent( postfix_content="MismatchError: " + str(mismatch_error) - ) + ), ) self.force_failure = True @@ -549,7 +560,7 @@ def _matchHelper(self, matchee, matcher, message, verbose): mismatch = matcher.match(matchee) if not mismatch: return - for (name, value) in mismatch.get_details().items(): + for name, value in mismatch.get_details().items(): self.addDetailUniqueName(name, value) return MismatchError(matchee, matcher, mismatch, verbose) @@ -610,15 +621,17 @@ def getUniqueString(self, prefix=None): """ if prefix is None: prefix = self.id() - return '%s-%d' % (prefix, self.getUniqueInteger()) + return "%s-%d" % (prefix, self.getUniqueInteger()) - def onException(self, exc_info, tb_label='traceback'): + def onException(self, exc_info, tb_label="traceback"): """Called when an exception propagates from test code. :seealso addOnException: """ if exc_info[0] not in [ - self.skipException, _UnexpectedSuccess, _ExpectedFailure, + self.skipException, + _UnexpectedSuccess, + _ExpectedFailure, ]: self._report_traceback(exc_info, tb_label=tb_label) for handler in self.__exception_handlers: @@ -645,18 +658,22 @@ def _report_skip(self, result, err): self._add_reason(reason) result.addSkip(self, details=self.getDetails()) - def _report_traceback(self, exc_info, tb_label='traceback'): - id_gen = self._traceback_id_gens.setdefault( - tb_label, itertools.count(0)) + def _report_traceback(self, exc_info, tb_label="traceback"): + id_gen = self._traceback_id_gens.setdefault(tb_label, itertools.count(0)) while True: tb_id = next(id_gen) if tb_id: - tb_label = '%s-%d' % (tb_label, tb_id) + tb_label = "%s-%d" % (tb_label, tb_id) if tb_label not in self.getDetails(): break - self.addDetail(tb_label, content.TracebackContent( - exc_info, self, capture_locals=getattr( - self, '__testtools_tb_locals__', False))) + self.addDetail( + tb_label, + content.TracebackContent( + exc_info, + self, + capture_locals=getattr(self, "__testtools_tb_locals__", False), + ), + ) @staticmethod def _report_unexpected_success(self, result, err): @@ -666,7 +683,8 @@ def run(self, result=None): self._reset() try: run_test = self.__RunTest( - self, self.exception_handlers, last_resort=self._report_error) + self, self.exception_handlers, last_resort=self._report_error + ) except TypeError: # Backwards compat: if we can't call the constructor # with last_resort, try without that. @@ -687,8 +705,11 @@ def _run_setup(self, result): "TestCase.setUp was not called. Have you upcalled all the " "way up the hierarchy from your setUp? e.g. Call " "super(%s, self).setUp() from your setUp()." - % (sys.modules[self.__class__.__module__].__file__, - self.__class__.__name__)) + % ( + sys.modules[self.__class__.__module__].__file__, + self.__class__.__name__, + ) + ) return ret def _run_teardown(self, result): @@ -705,12 +726,15 @@ def _run_teardown(self, result): "TestCase.tearDown was not called. Have you upcalled all the " "way up the hierarchy from your tearDown? e.g. Call " "super(%s, self).tearDown() from your tearDown()." - % (sys.modules[self.__class__.__module__].__file__, - self.__class__.__name__)) + % ( + sys.modules[self.__class__.__module__].__file__, + self.__class__.__name__, + ) + ) return ret def _get_test_method(self): - method_name = getattr(self, '_testMethodName') + method_name = getattr(self, "_testMethodName") return getattr(self, method_name) def _run_test_method(self, result): @@ -733,8 +757,7 @@ def useFixture(self, fixture): try: fixture.setUp() except MultipleExceptions as e: - if (fixtures is not None and - e.args[-1][0] is fixtures.fixture.SetupError): + if fixtures is not None and e.args[-1][0] is fixtures.fixture.SetupError: gather_details(e.args[-1][1].args[0], self.getDetails()) raise except BaseException: @@ -745,10 +768,7 @@ def useFixture(self, fixture): # the fixture. Ideally this whole try/except is not # really needed any more, however, we keep this code to # remain compatible with the older setUp(). - if ( - hasattr(fixture, '_details') and - fixture._details is not None - ): + if hasattr(fixture, "_details") and fixture._details is not None: gather_details(fixture.getDetails(), self.getDetails()) except BaseException: # Report the setUp exception, then raise the error during @@ -761,8 +781,7 @@ def useFixture(self, fixture): reraise(*exc_info) else: self.addCleanup(fixture.cleanUp) - self.addCleanup( - gather_details, fixture.getDetails(), self.getDetails()) + self.addCleanup(gather_details, fixture.getDetails(), self.getDetails()) return fixture def setUp(self): @@ -772,8 +791,8 @@ def setUp(self): "In File: %s\n" "TestCase.setUp was already called. Do not explicitly call " "setUp from your tests. In your own setUp, use super to call " - "the base setUp." - % (sys.modules[self.__class__.__module__].__file__,)) + "the base setUp." % (sys.modules[self.__class__.__module__].__file__,) + ) self.__setup_called = True def tearDown(self): @@ -784,7 +803,8 @@ def tearDown(self): "TestCase.tearDown was already called. Do not explicitly call " "tearDown from your tests. In your own tearDown, use super to " "call the base tearDown." - % (sys.modules[self.__class__.__module__].__file__,)) + % (sys.modules[self.__class__.__module__].__file__,) + ) self.__teardown_called = True @@ -797,8 +817,16 @@ class PlaceHolder: failureException = None - def __init__(self, test_id, short_description=None, details=None, - outcome='addSuccess', error=None, tags=None, timestamps=(None, None)): + def __init__( + self, + test_id, + short_description=None, + details=None, + outcome="addSuccess", + error=None, + tags=None, + timestamps=(None, None), + ): """Construct a `PlaceHolder`. :param test_id: The id of the placeholder test. @@ -815,7 +843,7 @@ def __init__(self, test_id, short_description=None, details=None, self._details = details or {} self._outcome = outcome if error is not None: - self._details['traceback'] = content.TracebackContent(error, self) + self._details["traceback"] = content.TracebackContent(error, self) tags = tags or frozenset() self._tags = frozenset(tags) self._timestamps = timestamps @@ -830,7 +858,8 @@ def __repr__(self): return "<{}.{}({})>".format( self.__class__.__module__, self.__class__.__name__, - ", ".join(map(repr, internal))) + ", ".join(map(repr, internal)), + ) def __str__(self): return self.id() @@ -881,8 +910,12 @@ def ErrorHolder(test_id, error, short_description=None, details=None): :param details: Outcome details as accepted by addSuccess etc. """ return PlaceHolder( - test_id, short_description=short_description, - details=details, outcome='addError', error=error) + test_id, + short_description=short_description, + details=details, + outcome="addError", + error=error, + ) def _clone_test_id_callback(test, callback): @@ -916,11 +949,13 @@ def attr(*args): :return: A callable that when applied to a WithAttributes will alter its id to enumerate the added attributes. """ + def decorate(fn): - if not hasattr(fn, '__testtools_attrs'): + if not hasattr(fn, "__testtools_attrs"): fn.__testtools_attrs = set() fn.__testtools_attrs.update(args) return fn + return decorate @@ -941,14 +976,14 @@ def id(self): # Depends on testtools.TestCase._get_test_method, be nice to support # plain unittest. fn = self._get_test_method() - attributes = getattr(fn, '__testtools_attrs', None) + attributes = getattr(fn, "__testtools_attrs", None) if not attributes: return orig - return orig + '[' + ','.join(sorted(attributes)) + ']' + return orig + "[" + ",".join(sorted(attributes)) + "]" class_types = [type] -if getattr(types, 'ClassType', None) is not None: +if getattr(types, "ClassType", None) is not None: class_types.append(types.ClassType) class_types = tuple(class_types) @@ -960,11 +995,14 @@ def skip(reason): unit tests in order to migrate to python 2.7, which provides the @unittest.skip decorator. """ + def decorator(test_item): if not isinstance(test_item, class_types): + @functools.wraps(test_item) def skip_wrapper(*args, **kwargs): raise TestCase.skipException(reason) + test_item = skip_wrapper # This attribute signals to RunTest._run_core that the entire test @@ -974,6 +1012,7 @@ def skip_wrapper(*args, **kwargs): test_item.__unittest_skip__ = True test_item.__unittest_skip_why__ = reason return test_item + return decorator @@ -984,6 +1023,7 @@ def skipIf(condition, reason): def _id(obj): return obj + return _id @@ -994,6 +1034,7 @@ def skipUnless(condition, reason): def _id(obj): return obj + return _id @@ -1027,9 +1068,9 @@ def __enter__(self): def __exit__(self, exc_type, exc_value, traceback): if exc_type is None: - error_msg = '%s not raised.' % self.exc_type.__name__ + error_msg = "%s not raised." % self.exc_type.__name__ if self.msg: - error_msg = error_msg + ' : ' + self.msg + error_msg = error_msg + " : " + self.msg raise AssertionError(error_msg) if exc_type != self.exc_type: return False @@ -1105,7 +1146,7 @@ def __delattr__(self, name): delattr(self.decorated, name) def __setattr__(self, name, value): - if name in ('decorated', 'callout', 'before_run', 'after_run'): + if name in ("decorated", "callout", "before_run", "after_run"): self.__dict__[name] = value return setattr(self.decorated, name, value) diff --git a/testtools/testresult/__init__.py b/testtools/testresult/__init__.py index 83d8cc7d..104be36c 100644 --- a/testtools/testresult/__init__.py +++ b/testtools/testresult/__init__.py @@ -3,28 +3,28 @@ """Test result objects.""" __all__ = [ - 'CopyStreamResult', - 'ExtendedToOriginalDecorator', - 'ExtendedToStreamDecorator', - 'MultiTestResult', - 'ResourcedToStreamDecorator', - 'StreamFailFast', - 'StreamResult', - 'StreamResultRouter', - 'StreamSummary', - 'StreamTagger', - 'StreamToDict', - 'StreamToExtendedDecorator', - 'StreamToQueue', - 'Tagger', - 'TestByTestResult', - 'TestControl', - 'TestResult', - 'TestResultDecorator', - 'TextTestResult', - 'ThreadsafeForwardingResult', - 'TimestampingStreamResult', - ] + "CopyStreamResult", + "ExtendedToOriginalDecorator", + "ExtendedToStreamDecorator", + "MultiTestResult", + "ResourcedToStreamDecorator", + "StreamFailFast", + "StreamResult", + "StreamResultRouter", + "StreamSummary", + "StreamTagger", + "StreamToDict", + "StreamToExtendedDecorator", + "StreamToQueue", + "Tagger", + "TestByTestResult", + "TestControl", + "TestResult", + "TestResultDecorator", + "TextTestResult", + "ThreadsafeForwardingResult", + "TimestampingStreamResult", +] from testtools.testresult.real import ( CopyStreamResult, @@ -48,4 +48,4 @@ TextTestResult, ThreadsafeForwardingResult, TimestampingStreamResult, - ) +) diff --git a/testtools/testresult/doubles.py b/testtools/testresult/doubles.py index 6f0fe98e..3dadaff3 100644 --- a/testtools/testresult/doubles.py +++ b/testtools/testresult/doubles.py @@ -7,12 +7,12 @@ from testtools.tags import TagContext __all__ = [ - 'Python26TestResult', - 'Python27TestResult', - 'ExtendedTestResult', - 'TwistedTestResult', - 'StreamResult', - ] + "Python26TestResult", + "Python27TestResult", + "ExtendedTestResult", + "TwistedTestResult", + "StreamResult", +] class LoggingBase: @@ -35,24 +35,24 @@ def __init__(self, event_log=None): def addError(self, test, err): self._was_successful = False - self._events.append(('addError', test, err)) + self._events.append(("addError", test, err)) def addFailure(self, test, err): self._was_successful = False - self._events.append(('addFailure', test, err)) + self._events.append(("addFailure", test, err)) def addSuccess(self, test): - self._events.append(('addSuccess', test)) + self._events.append(("addSuccess", test)) def startTest(self, test): - self._events.append(('startTest', test)) + self._events.append(("startTest", test)) self.testsRun += 1 def stop(self): self.shouldStop = True def stopTest(self, test): - self._events.append(('stopTest', test)) + self._events.append(("stopTest", test)) def wasSuccessful(self): return self._was_successful @@ -76,21 +76,21 @@ def addFailure(self, test, err): self.stop() def addExpectedFailure(self, test, err): - self._events.append(('addExpectedFailure', test, err)) + self._events.append(("addExpectedFailure", test, err)) def addSkip(self, test, reason): - self._events.append(('addSkip', test, reason)) + self._events.append(("addSkip", test, reason)) def addUnexpectedSuccess(self, test): - self._events.append(('addUnexpectedSuccess', test)) + self._events.append(("addUnexpectedSuccess", test)) if self.failfast: self.stop() def startTestRun(self): - self._events.append(('startTestRun',)) + self._events.append(("startTestRun",)) def stopTestRun(self): - self._events.append(('stopTestRun',)) + self._events.append(("stopTestRun",)) class ExtendedTestResult(Python27TestResult): @@ -102,33 +102,33 @@ def __init__(self, event_log=None): def addError(self, test, err=None, details=None): self._was_successful = False - self._events.append(('addError', test, err or details)) + self._events.append(("addError", test, err or details)) def addFailure(self, test, err=None, details=None): self._was_successful = False - self._events.append(('addFailure', test, err or details)) + self._events.append(("addFailure", test, err or details)) def addExpectedFailure(self, test, err=None, details=None): - self._events.append(('addExpectedFailure', test, err or details)) + self._events.append(("addExpectedFailure", test, err or details)) def addSkip(self, test, reason=None, details=None): - self._events.append(('addSkip', test, reason or details)) + self._events.append(("addSkip", test, reason or details)) def addSuccess(self, test, details=None): if details: - self._events.append(('addSuccess', test, details)) + self._events.append(("addSuccess", test, details)) else: - self._events.append(('addSuccess', test)) + self._events.append(("addSuccess", test)) def addUnexpectedSuccess(self, test, details=None): self._was_successful = False if details is not None: - self._events.append(('addUnexpectedSuccess', test, details)) + self._events.append(("addUnexpectedSuccess", test, details)) else: - self._events.append(('addUnexpectedSuccess', test)) + self._events.append(("addUnexpectedSuccess", test)) def progress(self, offset, whence): - self._events.append(('progress', offset, whence)) + self._events.append(("progress", offset, whence)) def startTestRun(self): super().startTestRun() @@ -149,10 +149,10 @@ def current_tags(self): def tags(self, new_tags, gone_tags): self._tags.change_tags(new_tags, gone_tags) - self._events.append(('tags', new_tags, gone_tags)) + self._events.append(("tags", new_tags, gone_tags)) def time(self, time): - self._events.append(('time', time)) + self._events.append(("time", time)) def wasSuccessful(self): return self._was_successful @@ -172,30 +172,30 @@ def __init__(self, event_log=None): def startTest(self, test): self.testsRun += 1 - self._events.append(('startTest', test)) + self._events.append(("startTest", test)) def stopTest(self, test): - self._events.append(('stopTest', test)) + self._events.append(("stopTest", test)) def addSuccess(self, test): - self._events.append(('addSuccess', test)) + self._events.append(("addSuccess", test)) def addError(self, test, error): self._was_successful = False - self._events.append(('addError', test, error)) + self._events.append(("addError", test, error)) def addFailure(self, test, error): self._was_successful = False - self._events.append(('addFailure', test, error)) + self._events.append(("addFailure", test, error)) def addExpectedFailure(self, test, failure, todo=None): - self._events.append(('addExpectedFailure', test, failure)) + self._events.append(("addExpectedFailure", test, failure)) def addUnexpectedSuccess(self, test, todo=None): - self._events.append(('addUnexpectedSuccess', test)) + self._events.append(("addUnexpectedSuccess", test)) def addSkip(self, test, reason): - self._events.append(('addSkip', test, reason)) + self._events.append(("addSkip", test, reason)) def wasSuccessful(self): return self._was_successful @@ -211,23 +211,55 @@ class StreamResult(LoggingBase): """ def startTestRun(self): - self._events.append(('startTestRun',)) + self._events.append(("startTestRun",)) def stopTestRun(self): - self._events.append(('stopTestRun',)) - - def status(self, test_id=None, test_status=None, test_tags=None, - runnable=True, file_name=None, file_bytes=None, eof=False, - mime_type=None, route_code=None, timestamp=None): + self._events.append(("stopTestRun",)) + + def status( + self, + test_id=None, + test_status=None, + test_tags=None, + runnable=True, + file_name=None, + file_bytes=None, + eof=False, + mime_type=None, + route_code=None, + timestamp=None, + ): self._events.append( _StatusEvent( - 'status', test_id, test_status, test_tags, runnable, - file_name, file_bytes, eof, mime_type, route_code, - timestamp)) + "status", + test_id, + test_status, + test_tags, + runnable, + file_name, + file_bytes, + eof, + mime_type, + route_code, + timestamp, + ) + ) # Convenience for easier access to status fields _StatusEvent = namedtuple( - "_Event", [ - "name", "test_id", "test_status", "test_tags", "runnable", "file_name", - "file_bytes", "eof", "mime_type", "route_code", "timestamp"]) + "_Event", + [ + "name", + "test_id", + "test_status", + "test_tags", + "runnable", + "file_name", + "file_bytes", + "eof", + "mime_type", + "route_code", + "timestamp", + ], +) diff --git a/testtools/testresult/real.py b/testtools/testresult/real.py index 9396ef16..d1513f34 100644 --- a/testtools/testresult/real.py +++ b/testtools/testresult/real.py @@ -3,24 +3,24 @@ """Test results and related things.""" __all__ = [ - 'ExtendedToOriginalDecorator', - 'ExtendedToStreamDecorator', - 'MultiTestResult', - 'ResourcedToStreamDecorator', - 'StreamFailFast', - 'StreamResult', - 'StreamSummary', - 'StreamTagger', - 'StreamToDict', - 'StreamToExtendedDecorator', - 'StreamToQueue', - 'Tagger', - 'TestControl', - 'TestResult', - 'TestResultDecorator', - 'ThreadsafeForwardingResult', - 'TimestampingStreamResult', - ] + "ExtendedToOriginalDecorator", + "ExtendedToStreamDecorator", + "MultiTestResult", + "ResourcedToStreamDecorator", + "StreamFailFast", + "StreamResult", + "StreamSummary", + "StreamTagger", + "StreamToDict", + "StreamToExtendedDecorator", + "StreamToQueue", + "Tagger", + "TestControl", + "TestResult", + "TestResultDecorator", + "ThreadsafeForwardingResult", + "TimestampingStreamResult", +] import datetime import email.message @@ -59,6 +59,7 @@ def tzname(self, dt): def dst(self, dt): return _ZERO + utc = UTC() @@ -99,7 +100,8 @@ def addExpectedFailure(self, test, err=None, details=None): """ # This is the python 2.7 implementation self.expectedFailures.append( - (test, self._err_details_to_string(test, err, details))) + (test, self._err_details_to_string(test, err, details)) + ) def addError(self, test, err=None, details=None): """Called when an error has occurred. 'err' is a tuple of values as @@ -108,8 +110,7 @@ def addError(self, test, err=None, details=None): :param details: Alternative way to supply details about the outcome. see the class docstring for more information. """ - self.errors.append( - (test, self._err_details_to_string(test, err, details))) + self.errors.append((test, self._err_details_to_string(test, err, details))) if self.failfast: self.stop() @@ -120,8 +121,7 @@ def addFailure(self, test, err=None, details=None): :param details: Alternative way to supply details about the outcome. see the class docstring for more information. """ - self.failures.append( - (test, self._err_details_to_string(test, err, details))) + self.failures.append((test, self._err_details_to_string(test, err, details))) if self.failfast: self.stop() @@ -142,9 +142,9 @@ def addSkip(self, test, reason=None, details=None): :return: None """ if reason is None: - reason = details.get('reason') + reason = details.get("reason") if reason is None: - reason = 'No reason given' + reason = "No reason given" else: reason = reason.as_text() skip_list = self.skip_reasons.setdefault(reason, []) @@ -174,9 +174,8 @@ def wasSuccessful(self): def _err_details_to_string(self, test, err=None, details=None): """Convert an error in exc_info form or a contents dict to a string.""" if err is not None: - return TracebackContent( - err, test, capture_locals=self.tb_locals).as_text() - return _details_to_str(details, special='traceback') + return TracebackContent(err, test, capture_locals=self.tb_locals).as_text() + return _details_to_str(details, special="traceback") def _exc_info_to_unicode(self, err, test): # Deprecated. Only present because subunit upcalls to it. See @@ -275,7 +274,7 @@ def done(self): running and at any intermediary point they might choose to indicate their continual operation. """ -INTERIM_STATES = frozenset([None, 'inprogress']) +INTERIM_STATES = frozenset([None, "inprogress"]) """Final states: @@ -300,7 +299,8 @@ def done(self): * unknown - we don't know what state the test is in """ FINAL_STATES = frozenset( - ['exists', 'xfail', 'uxsuccess', 'success', 'fail', 'skip', 'unknown']) + ["exists", "xfail", "uxsuccess", "success", "fail", "skip", "unknown"] +) STATES = INTERIM_STATES | FINAL_STATES @@ -362,9 +362,19 @@ def stopTestRun(self): considered failed-or-hung. """ - def status(self, test_id=None, test_status=None, test_tags=None, - runnable=True, file_name=None, file_bytes=None, eof=False, - mime_type=None, route_code=None, timestamp=None): + def status( + self, + test_id=None, + test_status=None, + test_tags=None, + runnable=True, + file_name=None, + file_bytes=None, + eof=False, + mime_type=None, + route_code=None, + timestamp=None, + ): """Inform the result about a test status. :param test_id: The test whose status is being reported. None to @@ -433,7 +443,9 @@ def domap(function, *sequences): """ warnings.warn( "domap deprecated since 1.8.1. Please implement your own strict map.", - DeprecationWarning, stacklevel=2) + DeprecationWarning, + stacklevel=2, + ) return _strict_map(function, *sequences) @@ -455,15 +467,15 @@ def __init__(self, targets): def startTestRun(self): super().startTestRun() - _strict_map(methodcaller('startTestRun'), self.targets) + _strict_map(methodcaller("startTestRun"), self.targets) def stopTestRun(self): super().stopTestRun() - _strict_map(methodcaller('stopTestRun'), self.targets) + _strict_map(methodcaller("stopTestRun"), self.targets) def status(self, *args, **kwargs): super().status(*args, **kwargs) - _strict_map(methodcaller('status', *args, **kwargs), self.targets) + _strict_map(methodcaller("status", *args, **kwargs), self.targets) class StreamFailFast(StreamResult): @@ -478,10 +490,20 @@ def do_something(): def __init__(self, on_error): self.on_error = on_error - def status(self, test_id=None, test_status=None, test_tags=None, - runnable=True, file_name=None, file_bytes=None, eof=False, - mime_type=None, route_code=None, timestamp=None): - if test_status in ('uxsuccess', 'fail'): + def status( + self, + test_id=None, + test_status=None, + test_tags=None, + runnable=True, + file_name=None, + file_bytes=None, + eof=False, + mime_type=None, + route_code=None, + timestamp=None, + ): + if test_status in ("uxsuccess", "fail"): self.on_error() @@ -548,19 +570,19 @@ def stopTestRun(self): self._in_run = False def status(self, **kwargs): - route_code = kwargs.get('route_code', None) - test_id = kwargs.get('test_id', None) + route_code = kwargs.get("route_code", None) + test_id = kwargs.get("test_id", None) if route_code is not None: - prefix = route_code.split('/')[0] + prefix = route_code.split("/")[0] else: prefix = route_code if prefix in self._route_code_prefixes: target, consume_route = self._route_code_prefixes[prefix] if route_code is not None and consume_route: - route_code = route_code[len(prefix) + 1:] + route_code = route_code[len(prefix) + 1 :] if not route_code: route_code = None - kwargs['route_code'] = route_code + kwargs["route_code"] = route_code elif test_id in self._test_ids: target = self._test_ids[test_id] else: @@ -597,15 +619,16 @@ def add_rule(self, sink, policy, do_start_stop_run=False, **policy_args): sink.startTestRun() def _map_route_code_prefix(self, sink, route_prefix, consume_route=False): - if '/' in route_prefix: - raise TypeError( - f"{route_prefix!r} is more than one route step long") + if "/" in route_prefix: + raise TypeError(f"{route_prefix!r} is more than one route step long") self._route_code_prefixes[route_prefix] = (sink, consume_route) - _policies['route_code_prefix'] = _map_route_code_prefix + + _policies["route_code_prefix"] = _map_route_code_prefix def _map_test_id(self, sink, test_id): self._test_ids[test_id] = sink - _policies['test_id'] = _map_test_id + + _policies["test_id"] = _map_test_id class StreamTagger(CopyStreamResult): @@ -624,10 +647,10 @@ def __init__(self, targets, add=None, discard=None): self.discard = frozenset(discard or ()) def status(self, *args, **kwargs): - test_tags = kwargs.get('test_tags') or set() + test_tags = kwargs.get("test_tags") or set() test_tags.update(self.add) test_tags.difference_update(self.discard) - kwargs['test_tags'] = test_tags or None + kwargs["test_tags"] = test_tags or None super().status(*args, **kwargs) @@ -658,7 +681,7 @@ def create(cls, test_id, timestamp): id=test_id, tags=set(), details={}, - status='unknown', + status="unknown", timestamps=(timestamp, None), ) @@ -691,11 +714,11 @@ def to_dict(self): compared - their ordering is purely order received in the stream. """ return { - 'id': self.id, - 'tags': self.tags, - 'details': self.details, - 'status': self.status, - 'timestamps': list(self.timestamps), + "id": self.id, + "tags": self.tags, + "details": self.details, + "status": self.status, + "timestamps": list(self.timestamps), } def got_timestamp(self, timestamp): @@ -718,8 +741,8 @@ def got_file(self, file_name, file_bytes, mime_type=None): content_type = _make_content_type(mime_type) content_bytes = [] case = self.transform( - ['details', file_name], - Content(content_type, lambda: content_bytes)) + ["details", file_name], Content(content_type, lambda: content_bytes) + ) case.details[file_name].iter_bytes().append(file_bytes) return case @@ -757,17 +780,17 @@ def _make_content_type(mime_type=None): # XXX: Not sure what release this was added, so "in a few releases" is # unactionable. if mime_type is None: - mime_type = 'application/octet-stream' + mime_type = "application/octet-stream" msg = email.message.EmailMessage() - msg['content-type'] = mime_type + msg["content-type"] = mime_type - full_type, parameters = msg.get_content_type(), dict(msg['content-type'].params) + full_type, parameters = msg.get_content_type(), dict(msg["content-type"].params) # Ensure any wildcards are valid. - if full_type == '*': - full_type = '*/*' + if full_type == "*": + full_type = "*/*" - type_parts = full_type.split('/') if '/' in full_type else None + type_parts = full_type.split("/") if "/" in full_type else None if not type_parts or len(type_parts) > 2: raise Exception("Can't parse type '%s'" % full_type) @@ -775,22 +798,23 @@ def _make_content_type(mime_type=None): primary_type = primary_type.strip() sub_type = sub_type.strip() - if 'charset' in parameters: - if ',' in parameters['charset']: - parameters['charset'] = parameters['charset'][ - :parameters['charset'].find(',')] + if "charset" in parameters: + if "," in parameters["charset"]: + parameters["charset"] = parameters["charset"][ + : parameters["charset"].find(",") + ] return ContentType(primary_type, sub_type, parameters) _status_map = { - 'inprogress': 'addFailure', - 'unknown': 'addFailure', - 'success': 'addSuccess', - 'skip': 'addSkip', - 'fail': 'addFailure', - 'xfail': 'addExpectedFailure', - 'uxsuccess': 'addUnexpectedSuccess', + "inprogress": "addFailure", + "unknown": "addFailure", + "success": "addSuccess", + "skip": "addSkip", + "fail": "addFailure", + "xfail": "addExpectedFailure", + "uxsuccess": "addUnexpectedSuccess", } @@ -818,14 +842,31 @@ def startTestRun(self): super().startTestRun() self._inprogress = {} - def status(self, test_id=None, test_status=None, test_tags=None, - runnable=True, file_name=None, file_bytes=None, eof=False, - mime_type=None, route_code=None, timestamp=None): + def status( + self, + test_id=None, + test_status=None, + test_tags=None, + runnable=True, + file_name=None, + file_bytes=None, + eof=False, + mime_type=None, + route_code=None, + timestamp=None, + ): super().status( - test_id, test_status, - test_tags=test_tags, runnable=runnable, file_name=file_name, - file_bytes=file_bytes, eof=eof, mime_type=mime_type, - route_code=route_code, timestamp=timestamp) + test_id, + test_status, + test_tags=test_tags, + runnable=runnable, + file_name=file_name, + file_bytes=file_bytes, + eof=eof, + mime_type=mime_type, + route_code=route_code, + timestamp=timestamp, + ) key = self._ensure_key(test_id, route_code, timestamp) if not key: @@ -833,16 +874,29 @@ def status(self, test_id=None, test_status=None, test_tags=None, # update fields self._inprogress[key] = self._update_case( - self._inprogress[key], test_status, test_tags, file_name, - file_bytes, mime_type, timestamp) + self._inprogress[key], + test_status, + test_tags, + file_name, + file_bytes, + mime_type, + timestamp, + ) # notify completed tests. if test_status not in INTERIM_STATES: self.on_test(self._inprogress.pop(key)) - def _update_case(self, case, test_status=None, test_tags=None, - file_name=None, file_bytes=None, mime_type=None, - timestamp=None): + def _update_case( + self, + case, + test_status=None, + test_tags=None, + file_name=None, + file_bytes=None, + mime_type=None, + timestamp=None, + ): if test_status is not None: case = case.set(status=test_status) @@ -852,7 +906,7 @@ def _update_case(self, case, test_status=None, test_tags=None, case = case.got_file(file_name, file_bytes, mime_type) if test_tags is not None: - case = case.set('tags', test_tags) + case = case.set("tags", test_tags) return case @@ -935,11 +989,11 @@ def test_dict_to_case(test_dict): :return: A PlaceHolder test object. """ return _TestRecord( - id=test_dict['id'], - tags=test_dict['tags'], - details=test_dict['details'], - status=test_dict['status'], - timestamps=tuple(test_dict['timestamps']), + id=test_dict["id"], + tags=test_dict["tags"], + details=test_dict["details"], + status=test_dict["status"], + timestamps=tuple(test_dict["timestamps"]), ).to_test_case() @@ -955,15 +1009,15 @@ def __init__(self): super().__init__() self._hook = _StreamToTestRecord(self._gather_test) self._handle_status = { - 'success': self._success, - 'skip': self._skip, - 'exists': self._exists, - 'fail': self._fail, - 'xfail': self._xfail, - 'uxsuccess': self._uxsuccess, - 'unknown': self._incomplete, - 'inprogress': self._incomplete, - } + "success": self._success, + "skip": self._skip, + "exists": self._exists, + "fail": self._fail, + "xfail": self._xfail, + "uxsuccess": self._uxsuccess, + "unknown": self._incomplete, + "inprogress": self._incomplete, + } def startTestRun(self): super().startTestRun() @@ -989,10 +1043,10 @@ def wasSuccessful(self): Note that incomplete tests can only be detected when stopTestRun is called, so that should be called before checking wasSuccessful. """ - return (not self.failures and not self.errors) + return not self.failures and not self.errors def _gather_test(self, test_record): - if test_record.status == 'exists': + if test_record.status == "exists": return self.testsRun += 1 case = test_record.to_test_case() @@ -1005,10 +1059,10 @@ def _success(self, case): pass def _skip(self, case): - if 'reason' not in case._details: + if "reason" not in case._details: reason = "Unknown" else: - reason = case._details['reason'].as_text() + reason = case._details["reason"].as_text() self.skipped.append((case, reason)) def _exists(self, case): @@ -1023,7 +1077,7 @@ def _xfail(self, case): self.expectedFailures.append((case, message)) def _uxsuccess(self, case): - case._outcome = 'addUnexpectedSuccess' + case._outcome = "addUnexpectedSuccess" self.unexpectedSuccesses.append(case) @@ -1053,82 +1107,84 @@ def __init__(self, *results): super().__init__() def __repr__(self): - return '<{} ({})>'.format( - self.__class__.__name__, ', '.join(map(repr, self._results))) + return "<{} ({})>".format( + self.__class__.__name__, ", ".join(map(repr, self._results)) + ) def _dispatch(self, message, *args, **kwargs): return tuple( - getattr(result, message)(*args, **kwargs) - for result in self._results) + getattr(result, message)(*args, **kwargs) for result in self._results + ) def _get_failfast(self): - return getattr(self._results[0], 'failfast', False) + return getattr(self._results[0], "failfast", False) def _set_failfast(self, value): - self._dispatch('__setattr__', 'failfast', value) + self._dispatch("__setattr__", "failfast", value) + failfast = property(_get_failfast, _set_failfast) def _get_shouldStop(self): - return any(self._dispatch('__getattr__', 'shouldStop')) + return any(self._dispatch("__getattr__", "shouldStop")) def _set_shouldStop(self, value): # Called because we subclass TestResult. Probably should not do that. pass + shouldStop = property(_get_shouldStop, _set_shouldStop) def startTest(self, test): super().startTest(test) - return self._dispatch('startTest', test) + return self._dispatch("startTest", test) def stop(self): - return self._dispatch('stop') + return self._dispatch("stop") def stopTest(self, test): super().stopTest(test) - return self._dispatch('stopTest', test) + return self._dispatch("stopTest", test) def addError(self, test, error=None, details=None): - return self._dispatch('addError', test, error, details=details) + return self._dispatch("addError", test, error, details=details) def addExpectedFailure(self, test, err=None, details=None): - return self._dispatch( - 'addExpectedFailure', test, err, details=details) + return self._dispatch("addExpectedFailure", test, err, details=details) def addFailure(self, test, err=None, details=None): - return self._dispatch('addFailure', test, err, details=details) + return self._dispatch("addFailure", test, err, details=details) def addSkip(self, test, reason=None, details=None): - return self._dispatch('addSkip', test, reason, details=details) + return self._dispatch("addSkip", test, reason, details=details) def addSuccess(self, test, details=None): - return self._dispatch('addSuccess', test, details=details) + return self._dispatch("addSuccess", test, details=details) def addUnexpectedSuccess(self, test, details=None): - return self._dispatch('addUnexpectedSuccess', test, details=details) + return self._dispatch("addUnexpectedSuccess", test, details=details) def startTestRun(self): super().startTestRun() - return self._dispatch('startTestRun') + return self._dispatch("startTestRun") def stopTestRun(self): - return self._dispatch('stopTestRun') + return self._dispatch("stopTestRun") def tags(self, new_tags, gone_tags): super().tags(new_tags, gone_tags) - return self._dispatch('tags', new_tags, gone_tags) + return self._dispatch("tags", new_tags, gone_tags) def time(self, a_datetime): - return self._dispatch('time', a_datetime) + return self._dispatch("time", a_datetime) def done(self): - return self._dispatch('done') + return self._dispatch("done") def wasSuccessful(self): """Was this result successful? Only returns True if every constituent result was successful. """ - return all(self._dispatch('wasSuccessful')) + return all(self._dispatch("wasSuccessful")) class TextTestResult(TestResult): @@ -1136,21 +1192,28 @@ class TextTestResult(TestResult): def __init__(self, stream, failfast=False, tb_locals=False): """Construct a TextTestResult writing to stream.""" - super().__init__( - failfast=failfast, tb_locals=tb_locals) + super().__init__(failfast=failfast, tb_locals=tb_locals) self.stream = stream - self.sep1 = '=' * 70 + '\n' - self.sep2 = '-' * 70 + '\n' + self.sep1 = "=" * 70 + "\n" + self.sep2 = "-" * 70 + "\n" def _delta_to_float(self, a_timedelta, precision): # This calls ceiling to ensure that the most pessimistic view of time # taken is shown (rather than leaving it to the Python %f operator # to decide whether to round/floor/ceiling. This was added when we # had pyp3 test failures that suggest a floor was happening. - shift = 10 ** precision - return math.ceil( - (a_timedelta.days * 86400.0 + a_timedelta.seconds + - a_timedelta.microseconds / 1000000.0) * shift) / shift + shift = 10**precision + return ( + math.ceil( + ( + a_timedelta.days * 86400.0 + + a_timedelta.seconds + + a_timedelta.microseconds / 1000000.0 + ) + * shift + ) + / shift + ) def _show_list(self, label, error_list): for test, output in error_list: @@ -1166,28 +1229,33 @@ def startTestRun(self): def stopTestRun(self): if self.testsRun != 1: - plural = 's' + plural = "s" else: - plural = '' + plural = "" stop = self._now() - self._show_list('ERROR', self.errors) - self._show_list('FAIL', self.failures) + self._show_list("ERROR", self.errors) + self._show_list("FAIL", self.failures) for test in self.unexpectedSuccesses: self.stream.write( - "{}UNEXPECTED SUCCESS: {}\n{}".format( - self.sep1, test.id(), self.sep2)) + "{}UNEXPECTED SUCCESS: {}\n{}".format(self.sep1, test.id(), self.sep2) + ) self.stream.write( - "\nRan %d test%s in %.3fs\n" % ( - self.testsRun, plural, - self._delta_to_float(stop - self.__start, 3))) + "\nRan %d test%s in %.3fs\n" + % (self.testsRun, plural, self._delta_to_float(stop - self.__start, 3)) + ) if self.wasSuccessful(): self.stream.write("OK\n") else: self.stream.write("FAILED (") details = [] - details.append("failures=%d" % ( - sum(map(len, ( - self.failures, self.errors, self.unexpectedSuccesses))))) + details.append( + "failures=%d" + % ( + sum( + map(len, (self.failures, self.errors, self.unexpectedSuccesses)) + ) + ) + ) self.stream.write(", ".join(details)) self.stream.write(")\n") super().stopTestRun() @@ -1233,7 +1301,7 @@ def __init__(self, target, semaphore): self._test_tags = set(), set() def __repr__(self): - return f'<{self.__class__.__name__} {self.result!r}>' + return f"<{self.__class__.__name__} {self.result!r}>" def _any_tags(self, tags): return bool(tags[0] or tags[1]) @@ -1260,27 +1328,31 @@ def _add_result_with_semaphore(self, method, test, *args, **kwargs): def addError(self, test, err=None, details=None): self._add_result_with_semaphore( - self.result.addError, test, err, details=details) + self.result.addError, test, err, details=details + ) def addExpectedFailure(self, test, err=None, details=None): self._add_result_with_semaphore( - self.result.addExpectedFailure, test, err, details=details) + self.result.addExpectedFailure, test, err, details=details + ) def addFailure(self, test, err=None, details=None): self._add_result_with_semaphore( - self.result.addFailure, test, err, details=details) + self.result.addFailure, test, err, details=details + ) def addSkip(self, test, reason=None, details=None): self._add_result_with_semaphore( - self.result.addSkip, test, reason, details=details) + self.result.addSkip, test, reason, details=details + ) def addSuccess(self, test, details=None): - self._add_result_with_semaphore( - self.result.addSuccess, test, details=details) + self._add_result_with_semaphore(self.result.addSuccess, test, details=details) def addUnexpectedSuccess(self, test, details=None): self._add_result_with_semaphore( - self.result.addUnexpectedSuccess, test, details=details) + self.result.addUnexpectedSuccess, test, details=details + ) def progress(self, offset, whence): pass @@ -1303,6 +1375,7 @@ def _get_shouldStop(self): def _set_shouldStop(self, value): # Another case where we should not subclass TestResult pass + shouldStop = property(_get_shouldStop, _set_shouldStop) def stop(self): @@ -1337,11 +1410,9 @@ def tags(self, new_tags, gone_tags): """See `TestResult`.""" super().tags(new_tags, gone_tags) if self._test_start is not None: - self._test_tags = _merge_tags( - self._test_tags, (new_tags, gone_tags)) + self._test_tags = _merge_tags(self._test_tags, (new_tags, gone_tags)) else: - self._global_tags = _merge_tags( - self._global_tags, (new_tags, gone_tags)) + self._global_tags = _merge_tags(self._global_tags, (new_tags, gone_tags)) def _merge_tags(existing, changed): @@ -1374,7 +1445,7 @@ def __init__(self, decorated): self._shouldStop = False def __repr__(self): - return f'<{self.__class__.__name__} {self.decorated!r}>' + return f"<{self.__class__.__name__} {self.decorated!r}>" def __getattr__(self, name): return getattr(self.decorated, name) @@ -1395,8 +1466,7 @@ def addError(self, test, err=None, details=None): def addExpectedFailure(self, test, err=None, details=None): self._check_args(err, details) - addExpectedFailure = getattr( - self.decorated, 'addExpectedFailure', None) + addExpectedFailure = getattr(self.decorated, "addExpectedFailure", None) if addExpectedFailure is None: return self.addSuccess(test) if details is not None: @@ -1423,7 +1493,7 @@ def addFailure(self, test, err=None, details=None): def addSkip(self, test, reason=None, details=None): self._check_args(reason, details) - addSkip = getattr(self.decorated, 'addSkip', None) + addSkip = getattr(self.decorated, "addSkip", None) if addSkip is None: return self.decorated.addSuccess(test) if details is not None: @@ -1432,14 +1502,14 @@ def addSkip(self, test, reason=None, details=None): except TypeError: # extract the reason if it's available try: - reason = details['reason'].as_text() + reason = details["reason"].as_text() except KeyError: reason = _details_to_str(details) return addSkip(test, reason) def addUnexpectedSuccess(self, test, details=None): try: - outcome = getattr(self.decorated, 'addUnexpectedSuccess', None) + outcome = getattr(self.decorated, "addUnexpectedSuccess", None) if outcome is None: try: test.fail("") @@ -1471,20 +1541,20 @@ def _check_args(self, err, details): param_count += 1 if param_count != 1: raise ValueError( - "Must pass only one of err '%s' and details '%s" - % (err, details)) + "Must pass only one of err '%s' and details '%s" % (err, details) + ) def _details_to_exc_info(self, details): """Convert a details dict to an exc_info tuple.""" return ( _StringException, - _StringException(_details_to_str(details, special='traceback')), - None) + _StringException(_details_to_str(details, special="traceback")), + None, + ) @property def current_tags(self): - return getattr( - self.decorated, 'current_tags', self._tags.get_current_tags()) + return getattr(self.decorated, "current_tags", self._tags.get_current_tags()) def done(self): try: @@ -1493,29 +1563,31 @@ def done(self): return def _get_failfast(self): - return getattr(self.decorated, 'failfast', self._failfast) + return getattr(self.decorated, "failfast", self._failfast) def _set_failfast(self, value): - if hasattr(self.decorated, 'failfast'): + if hasattr(self.decorated, "failfast"): self.decorated.failfast = value else: self._failfast = value + failfast = property(_get_failfast, _set_failfast) def progress(self, offset, whence): - method = getattr(self.decorated, 'progress', None) + method = getattr(self.decorated, "progress", None) if method is None: return return method(offset, whence) def _get_shouldStop(self): - return getattr(self.decorated, 'shouldStop', self._shouldStop) + return getattr(self.decorated, "shouldStop", self._shouldStop) def _set_shouldStop(self, value): - if hasattr(self.decorated, 'shouldStop'): + if hasattr(self.decorated, "shouldStop"): self.decorated.shouldStop = value else: self._shouldStop = value + shouldStop = property(_get_shouldStop, _set_shouldStop) def startTest(self, test): @@ -1530,7 +1602,7 @@ def startTestRun(self): return def stop(self): - method = getattr(self.decorated, 'stop', None) + method = getattr(self.decorated, "stop", None) if method: return method() self.shouldStop = True @@ -1546,14 +1618,14 @@ def stopTestRun(self): return def tags(self, new_tags, gone_tags): - method = getattr(self.decorated, 'tags', None) + method = getattr(self.decorated, "tags", None) if method is not None: return method(new_tags, gone_tags) else: self._tags.change_tags(new_tags, gone_tags) def time(self, a_datetime): - method = getattr(self.decorated, 'time', None) + method = getattr(self.decorated, "time", None) if method is None: return return method(a_datetime) @@ -1588,13 +1660,13 @@ def _set_failfast(self, value): self.targets.append(StreamFailFast(self.stop)) else: del self.targets[1:] + failfast = property(_get_failfast, _set_failfast) def startTest(self, test): if not self._started: self.startTestRun() - self.status( - test_id=test.id(), test_status='inprogress', timestamp=self._now()) + self.status(test_id=test.id(), test_status="inprogress", timestamp=self._now()) self._tags = TagContext(self._tags) def stopTest(self, test): @@ -1602,7 +1674,8 @@ def stopTest(self, test): def addError(self, test, err=None, details=None): self._check_args(err, details) - self._convert(test, err, details, 'fail') + self._convert(test, err, details, "fail") + addFailure = addError def _convert(self, test, err, details, status, reason=None): @@ -1613,7 +1686,7 @@ def _convert(self, test, err, details, status, reason=None): if err is not None: if details is None: details = {} - details['traceback'] = TracebackContent(err, test) + details["traceback"] = TracebackContent(err, test) if details is not None: for name, content in details.items(): mime_type = repr(content.content_type) @@ -1621,36 +1694,51 @@ def _convert(self, test, err, details, status, reason=None): for next_bytes in content.iter_bytes(): if file_bytes is not None: self.status( - file_name=name, file_bytes=file_bytes, - mime_type=mime_type, test_id=test_id, - timestamp=now) + file_name=name, + file_bytes=file_bytes, + mime_type=mime_type, + test_id=test_id, + timestamp=now, + ) file_bytes = next_bytes if file_bytes is None: file_bytes = _b("") self.status( - file_name=name, file_bytes=file_bytes, eof=True, - mime_type=mime_type, test_id=test_id, timestamp=now) + file_name=name, + file_bytes=file_bytes, + eof=True, + mime_type=mime_type, + test_id=test_id, + timestamp=now, + ) if reason is not None: self.status( - file_name='reason', file_bytes=reason.encode('utf8'), - eof=True, mime_type="text/plain; charset=utf8", - test_id=test_id, timestamp=now) + file_name="reason", + file_bytes=reason.encode("utf8"), + eof=True, + mime_type="text/plain; charset=utf8", + test_id=test_id, + timestamp=now, + ) self.status( - test_id=test_id, test_status=status, - test_tags=self.current_tags, timestamp=now) + test_id=test_id, + test_status=status, + test_tags=self.current_tags, + timestamp=now, + ) def addExpectedFailure(self, test, err=None, details=None): self._check_args(err, details) - self._convert(test, err, details, 'xfail') + self._convert(test, err, details, "xfail") def addSkip(self, test, reason=None, details=None): - self._convert(test, None, details, 'skip', reason) + self._convert(test, None, details, "skip", reason) def addUnexpectedSuccess(self, test, details=None): - self._convert(test, None, details, 'uxsuccess') + self._convert(test, None, details, "uxsuccess") def addSuccess(self, test, details=None): - self._convert(test, None, details, 'success') + self._convert(test, None, details, "success") def _check_args(self, err, details): param_count = 0 @@ -1660,8 +1748,8 @@ def _check_args(self, err, details): param_count += 1 if param_count != 1: raise ValueError( - "Must pass only one of err '%s' and details '%s" - % (err, details)) + "Must pass only one of err '%s' and details '%s" % (err, details) + ) def startTestRun(self): super().startTestRun() @@ -1722,16 +1810,16 @@ class ResourcedToStreamDecorator(ExtendedToStreamDecorator): """ def startMakeResource(self, resource): - self._convertResourceLifecycle(resource, 'make', 'start') + self._convertResourceLifecycle(resource, "make", "start") def stopMakeResource(self, resource): - self._convertResourceLifecycle(resource, 'make', 'stop') + self._convertResourceLifecycle(resource, "make", "stop") def startCleanResource(self, resource): - self._convertResourceLifecycle(resource, 'clean', 'start') + self._convertResourceLifecycle(resource, "clean", "start") def stopCleanResource(self, resource): - self._convertResourceLifecycle(resource, 'clean', 'stop') + self._convertResourceLifecycle(resource, "clean", "stop") def _convertResourceLifecycle(self, resource, method, phase): """Convert a resource lifecycle report to a stream event.""" @@ -1742,18 +1830,22 @@ def _convertResourceLifecycle(self, resource, method, phase): resource_id = resource.id() else: resource_id = "{}.{}".format( - resource.__class__.__module__, resource.__class__.__name__) + resource.__class__.__module__, resource.__class__.__name__ + ) - test_id = f'{resource_id}.{method}' + test_id = f"{resource_id}.{method}" - if phase == 'start': - test_status = 'inprogress' + if phase == "start": + test_status = "inprogress" else: - test_status = 'success' + test_status = "success" self.status( - test_id=test_id, test_status=test_status, runnable=False, - timestamp=self._now()) + test_id=test_id, + test_status=test_status, + runnable=False, + timestamp=self._now(), + ) class StreamToExtendedDecorator(StreamResult): @@ -1776,10 +1868,9 @@ def __init__(self, decorated): self.hook = _StreamToTestRecord(self._handle_tests) def status(self, test_id=None, test_status=None, *args, **kwargs): - if test_status == 'exists': + if test_status == "exists": return - self.hook.status( - test_id=test_id, test_status=test_status, *args, **kwargs) + self.hook.status(test_id=test_id, test_status=test_status, *args, **kwargs) def startTestRun(self): self.decorated.startTestRun() @@ -1835,20 +1926,39 @@ def __init__(self, queue, routing_code): self.routing_code = routing_code def startTestRun(self): - self.queue.put(dict(event='startTestRun', result=self)) - - def status(self, test_id=None, test_status=None, test_tags=None, - runnable=True, file_name=None, file_bytes=None, eof=False, - mime_type=None, route_code=None, timestamp=None): - self.queue.put(dict( - event='status', test_id=test_id, - test_status=test_status, test_tags=test_tags, runnable=runnable, - file_name=file_name, file_bytes=file_bytes, eof=eof, - mime_type=mime_type, route_code=self.route_code(route_code), - timestamp=timestamp)) + self.queue.put(dict(event="startTestRun", result=self)) + + def status( + self, + test_id=None, + test_status=None, + test_tags=None, + runnable=True, + file_name=None, + file_bytes=None, + eof=False, + mime_type=None, + route_code=None, + timestamp=None, + ): + self.queue.put( + dict( + event="status", + test_id=test_id, + test_status=test_status, + test_tags=test_tags, + runnable=runnable, + file_name=file_name, + file_bytes=file_bytes, + eof=eof, + mime_type=mime_type, + route_code=self.route_code(route_code), + timestamp=timestamp, + ) + ) def stopTestRun(self): - self.queue.put(dict(event='stopTestRun', result=self)) + self.queue.put(dict(event="stopTestRun", result=self)) def route_code(self, route_code): """Adjust route_code on the way through.""" @@ -1978,47 +2088,47 @@ def stopTest(self, test): start_time=self._start_time, stop_time=self._stop_time, tags=tags, - details=self._details) + details=self._details, + ) def _err_to_details(self, test, err, details): if details: return details - return {'traceback': TracebackContent( - err, test, capture_locals=self.tb_locals)} + return {"traceback": TracebackContent(err, test, capture_locals=self.tb_locals)} def addSuccess(self, test, details=None): super().addSuccess(test) - self._status = 'success' + self._status = "success" self._details = details def addFailure(self, test, err=None, details=None): super().addFailure(test, err, details) - self._status = 'failure' + self._status = "failure" self._details = self._err_to_details(test, err, details) def addError(self, test, err=None, details=None): super().addError(test, err, details) - self._status = 'error' + self._status = "error" self._details = self._err_to_details(test, err, details) def addSkip(self, test, reason=None, details=None): super().addSkip(test, reason, details) - self._status = 'skip' + self._status = "skip" if details is None: - details = {'reason': text_content(reason)} + details = {"reason": text_content(reason)} elif reason: # XXX: What if details already has 'reason' key? - details['reason'] = text_content(reason) + details["reason"] = text_content(reason) self._details = details def addExpectedFailure(self, test, err=None, details=None): super().addExpectedFailure(test, err, details) - self._status = 'xfail' + self._status = "xfail" self._details = self._err_to_details(test, err, details) def addUnexpectedSuccess(self, test, details=None): super().addUnexpectedSuccess(test, details) - self._status = 'success' + self._status = "success" self._details = details @@ -2032,11 +2142,10 @@ def __init__(self, target): super().__init__([target]) def status(self, *args, **kwargs): - timestamp = kwargs.pop('timestamp', None) + timestamp = kwargs.pop("timestamp", None) if timestamp is None: timestamp = datetime.datetime.now(utc) - super().status( - *args, timestamp=timestamp, **kwargs) + super().status(*args, timestamp=timestamp, **kwargs) class _StringException(Exception): @@ -2053,7 +2162,7 @@ def __eq__(self, other): def _format_text_attachment(name, text): - if '\n' in text: + if "\n" in text: return f"{name}: {{{{{{\n{text}\n}}}}}}\n" return f"{name}: {{{{{{{text}}}}}}}" @@ -2074,7 +2183,7 @@ def _details_to_str(details, special=None): # sorted is for testing, may want to remove that and use a dict # subclass with defined order for items instead. for key, content in sorted(details.items()): - if content.content_type.type != 'text': + if content.content_type.type != "text": binary_attachments.append((key, content.content_type)) continue text = content.as_text().strip() @@ -2083,23 +2192,23 @@ def _details_to_str(details, special=None): continue # We want the 'special' attachment to be at the bottom. if key == special: - special_content = f'{text}\n' + special_content = f"{text}\n" continue text_attachments.append(_format_text_attachment(key, text)) - if text_attachments and not text_attachments[-1].endswith('\n'): - text_attachments.append('') + if text_attachments and not text_attachments[-1].endswith("\n"): + text_attachments.append("") if special_content: text_attachments.append(special_content) lines = [] if binary_attachments: - lines.append('Binary content:\n') + lines.append("Binary content:\n") for name, content_type in binary_attachments: - lines.append(f' {name} ({content_type})\n') + lines.append(f" {name} ({content_type})\n") if empty_attachments: - lines.append('Empty attachments:\n') + lines.append("Empty attachments:\n") for name in empty_attachments: - lines.append(f' {name}\n') + lines.append(f" {name}\n") if (binary_attachments or empty_attachments) and text_attachments: - lines.append('\n') - lines.append('\n'.join(text_attachments)) - return ''.join(lines) + lines.append("\n") + lines.append("\n".join(text_attachments)) + return "".join(lines) diff --git a/testtools/tests/__init__.py b/testtools/tests/__init__.py index 2bd4d36e..2c24bb47 100644 --- a/testtools/tests/__init__.py +++ b/testtools/tests/__init__.py @@ -25,7 +25,8 @@ def test_suite(): test_testresult, test_testsuite, test_with_with, - ) + ) + modules = [ matchers, twistedsupport, @@ -43,7 +44,7 @@ def test_suite(): test_testresult, test_testsuite, test_with_with, - ] + ] suites = map(lambda x: x.test_suite(), modules) all_tests = TestSuite(suites) return TestSuite(testscenarios.generate_scenarios(all_tests)) diff --git a/testtools/tests/helpers.py b/testtools/tests/helpers.py index 5ff89aaf..4f65ccfe 100644 --- a/testtools/tests/helpers.py +++ b/testtools/tests/helpers.py @@ -3,8 +3,8 @@ """Helpers for tests.""" __all__ = [ - 'LoggingResult', - ] + "LoggingResult", +] import sys @@ -36,51 +36,51 @@ def __init__(self, log): super().__init__() def startTest(self, test): - self._events.append(('startTest', test)) + self._events.append(("startTest", test)) super().startTest(test) def stop(self): - self._events.append('stop') + self._events.append("stop") super().stop() def stopTest(self, test): - self._events.append(('stopTest', test)) + self._events.append(("stopTest", test)) super().stopTest(test) def addFailure(self, test, error): - self._events.append(('addFailure', test, error)) + self._events.append(("addFailure", test, error)) super().addFailure(test, error) def addError(self, test, error): - self._events.append(('addError', test, error)) + self._events.append(("addError", test, error)) super().addError(test, error) def addSkip(self, test, reason): - self._events.append(('addSkip', test, reason)) + self._events.append(("addSkip", test, reason)) super().addSkip(test, reason) def addSuccess(self, test): - self._events.append(('addSuccess', test)) + self._events.append(("addSuccess", test)) super().addSuccess(test) def startTestRun(self): - self._events.append('startTestRun') + self._events.append("startTestRun") super().startTestRun() def stopTestRun(self): - self._events.append('stopTestRun') + self._events.append("stopTestRun") super().stopTestRun() def done(self): - self._events.append('done') + self._events.append("done") super().done() def tags(self, new_tags, gone_tags): - self._events.append(('tags', new_tags, gone_tags)) + self._events.append(("tags", new_tags, gone_tags)) super().tags(new_tags, gone_tags) def time(self, a_datetime): - self._events.append(('time', a_datetime)) + self._events.append(("time", a_datetime)) super().time(a_datetime) @@ -103,11 +103,8 @@ def run_with_stack_hidden(should_hide, f, *args, **kwargs): class FullStackRunTest(runtest.RunTest): - def _run_user(self, fn, *args, **kwargs): - return run_with_stack_hidden( - False, - super()._run_user, fn, *args, **kwargs) + return run_with_stack_hidden(False, super()._run_user, fn, *args, **kwargs) class MatchesEvents: @@ -124,15 +121,14 @@ def __init__(self, *expected): def _make_matcher(self, obj): # This isn't very safe for general use, but is good enough to make # some tests in this module more readable. - if hasattr(obj, 'match'): + if hasattr(obj, "match"): return obj elif isinstance(obj, tuple) or isinstance(obj, list): - return MatchesListwise( - [self._make_matcher(item) for item in obj]) + return MatchesListwise([self._make_matcher(item) for item in obj]) elif isinstance(obj, dict): - return MatchesDict({ - key: self._make_matcher(value) - for key, value in obj.items()}) + return MatchesDict( + {key: self._make_matcher(value) for key, value in obj.items()} + ) else: return Equals(obj) @@ -145,8 +141,7 @@ class AsText(AfterPreprocessing): """Match the text of a Content instance.""" def __init__(self, matcher, annotate=True): - super().__init__( - lambda log: log.as_text(), matcher, annotate=annotate) + super().__init__(lambda log: log.as_text(), matcher, annotate=annotate) def raise_(exception): diff --git a/testtools/tests/matchers/__init__.py b/testtools/tests/matchers/__init__.py index 4573a863..8deaa497 100644 --- a/testtools/tests/matchers/__init__.py +++ b/testtools/tests/matchers/__init__.py @@ -15,8 +15,9 @@ def test_suite(): test_filesystem, test_higherorder, test_impl, - test_warnings - ) + test_warnings, + ) + modules = [ test_basic, test_const, @@ -27,7 +28,7 @@ def test_suite(): test_filesystem, test_higherorder, test_impl, - test_warnings - ] + test_warnings, + ] suites = map(lambda x: x.test_suite(), modules) return TestSuite(suites) diff --git a/testtools/tests/matchers/helpers.py b/testtools/tests/matchers/helpers.py index 8ec282d9..4a018aaf 100644 --- a/testtools/tests/matchers/helpers.py +++ b/testtools/tests/matchers/helpers.py @@ -4,7 +4,6 @@ class TestMatchersInterface: - run_tests_with = FullStackRunTest def test_matches_match(self): @@ -16,11 +15,12 @@ def test_matches_match(self): for candidate in mismatches: mismatch = matcher.match(candidate) self.assertNotEqual(None, mismatch) - self.assertNotEqual(None, getattr(mismatch, 'describe', None)) + self.assertNotEqual(None, getattr(mismatch, "describe", None)) def test__str__(self): # [(expected, object to __str__)]. from testtools.matchers._doctest import DocTestMatches + examples = self.str_examples for expected, matcher in examples: self.assertThat(matcher, DocTestMatches(expected)) diff --git a/testtools/tests/matchers/test_basic.py b/testtools/tests/matchers/test_basic.py index 86ac7be5..07ae59cb 100644 --- a/testtools/tests/matchers/test_basic.py +++ b/testtools/tests/matchers/test_basic.py @@ -6,7 +6,7 @@ from testtools.compat import ( text_repr, _b, - ) +) from testtools.matchers._basic import ( _BinaryMismatch, Contains, @@ -23,7 +23,7 @@ NotEquals, SameMembers, StartsWith, - ) +) from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface @@ -38,11 +38,12 @@ class Test_BinaryMismatch(TestCase): class CustomRepr: def __init__(self, repr_string): self._repr_string = repr_string + def __repr__(self): - return '' + return "" def test_short_objects(self): - o1, o2 = self.CustomRepr('a'), self.CustomRepr('b') + o1, o2 = self.CustomRepr("a"), self.CustomRepr("b") mismatch = _BinaryMismatch(o1, "!~", o2) self.assertEqual(mismatch.describe(), f"{o1!r} !~ {o2!r}") @@ -60,7 +61,7 @@ def test_long_bytes(self): "!~", text_repr(self._long_b, multiline=True), text_repr(one_line_b), - ) + ), ) def test_long_unicode(self): @@ -72,7 +73,7 @@ def test_long_unicode(self): "!~", text_repr(self._long_u, multiline=True), text_repr(one_line_u), - ) + ), ) def test_long_mixed_strings(self): @@ -83,7 +84,7 @@ def test_long_mixed_strings(self): "!~", text_repr(self._long_u, multiline=True), text_repr(self._long_b, multiline=True), - ) + ), ) def test_long_bytes_and_object(self): @@ -95,7 +96,7 @@ def test_long_bytes_and_object(self): "!~", repr(obj), text_repr(self._long_b, multiline=True), - ) + ), ) def test_long_unicode_and_object(self): @@ -107,42 +108,42 @@ def test_long_unicode_and_object(self): "!~", repr(obj), text_repr(self._long_u, multiline=True), - ) + ), ) class TestEqualsInterface(TestCase, TestMatchersInterface): - matches_matcher = Equals(1) matches_matches = [1] matches_mismatches = [2] - str_examples = [("Equals(1)", Equals(1)), ("Equals('1')", Equals('1'))] + str_examples = [("Equals(1)", Equals(1)), ("Equals('1')", Equals("1"))] describe_examples = [ ("2 != 1", 2, Equals(1)), - (("!=:\n" - "reference = 'abcdefghijklmnopqrstuvwxyz0123456789'\n" - "actual = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n"), - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', - Equals('abcdefghijklmnopqrstuvwxyz0123456789')), + ( + ( + "!=:\n" + "reference = 'abcdefghijklmnopqrstuvwxyz0123456789'\n" + "actual = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n" + ), + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", + Equals("abcdefghijklmnopqrstuvwxyz0123456789"), + ), ] class TestNotEqualsInterface(TestCase, TestMatchersInterface): - matches_matcher = NotEquals(1) matches_matches = [2] matches_mismatches = [1] - str_examples = [ - ("NotEquals(1)", NotEquals(1)), ("NotEquals('1')", NotEquals('1'))] + str_examples = [("NotEquals(1)", NotEquals(1)), ("NotEquals('1')", NotEquals("1"))] describe_examples = [("1 == 1", 1, NotEquals(1))] class TestIsInterface(TestCase, TestMatchersInterface): - foo = object() bar = object() @@ -156,7 +157,6 @@ class TestIsInterface(TestCase, TestMatchersInterface): class TestIsInstanceInterface(TestCase, TestMatchersInterface): - class Foo: pass @@ -165,65 +165,64 @@ class Foo: matches_mismatches = [object(), 1, Foo] str_examples = [ - ("IsInstance(str)", IsInstance(str)), - ("IsInstance(str, int)", IsInstance(str, int)), - ] + ("IsInstance(str)", IsInstance(str)), + ("IsInstance(str, int)", IsInstance(str, int)), + ] describe_examples = [ - ("'foo' is not an instance of int", 'foo', IsInstance(int)), - ("'foo' is not an instance of any of (int, type)", 'foo', - IsInstance(int, type)), - ] + ("'foo' is not an instance of int", "foo", IsInstance(int)), + ( + "'foo' is not an instance of any of (int, type)", + "foo", + IsInstance(int, type), + ), + ] class TestLessThanInterface(TestCase, TestMatchersInterface): - matches_matcher = LessThan(4) matches_matches = [-5, 3] matches_mismatches = [4, 5, 5000] str_examples = [ ("LessThan(12)", LessThan(12)), - ] + ] describe_examples = [ - ('5 >= 4', 5, LessThan(4)), - ('4 >= 4', 4, LessThan(4)), - ] + ("5 >= 4", 5, LessThan(4)), + ("4 >= 4", 4, LessThan(4)), + ] class TestGreaterThanInterface(TestCase, TestMatchersInterface): - matches_matcher = GreaterThan(4) matches_matches = [5, 8] matches_mismatches = [-2, 0, 4] str_examples = [ ("GreaterThan(12)", GreaterThan(12)), - ] + ] describe_examples = [ - ('4 <= 5', 4, GreaterThan(5)), - ('4 <= 4', 4, GreaterThan(4)), - ] + ("4 <= 5", 4, GreaterThan(5)), + ("4 <= 4", 4, GreaterThan(4)), + ] class TestContainsInterface(TestCase, TestMatchersInterface): - - matches_matcher = Contains('foo') - matches_matches = ['foo', 'afoo', 'fooa'] - matches_mismatches = ['f', 'fo', 'oo', 'faoo', 'foao'] + matches_matcher = Contains("foo") + matches_matches = ["foo", "afoo", "fooa"] + matches_mismatches = ["f", "fo", "oo", "faoo", "foao"] str_examples = [ ("Contains(1)", Contains(1)), - ("Contains('foo')", Contains('foo')), - ] + ("Contains('foo')", Contains("foo")), + ] describe_examples = [("1 not in 2", 2, Contains(1))] class DoesNotStartWithTests(TestCase): - run_tests_with = FullStackRunTest def test_describe(self): @@ -234,20 +233,21 @@ def test_describe_non_ascii_unicode(self): string = "A\xA7" suffix = "B\xA7" mismatch = DoesNotStartWith(string, suffix) - self.assertEqual("{} does not start with {}.".format( - text_repr(string), text_repr(suffix)), - mismatch.describe()) + self.assertEqual( + "{} does not start with {}.".format(text_repr(string), text_repr(suffix)), + mismatch.describe(), + ) def test_describe_non_ascii_bytes(self): string = _b("A\xA7") suffix = _b("B\xA7") mismatch = DoesNotStartWith(string, suffix) - self.assertEqual(f"{string!r} does not start with {suffix!r}.", - mismatch.describe()) + self.assertEqual( + f"{string!r} does not start with {suffix!r}.", mismatch.describe() + ) class StartsWithTests(TestCase): - run_tests_with = FullStackRunTest def test_str(self): @@ -284,7 +284,6 @@ def test_mismatch_sets_expected(self): class DoesNotEndWithTests(TestCase): - run_tests_with = FullStackRunTest def test_describe(self): @@ -295,20 +294,21 @@ def test_describe_non_ascii_unicode(self): string = "A\xA7" suffix = "B\xA7" mismatch = DoesNotEndWith(string, suffix) - self.assertEqual("{} does not end with {}.".format( - text_repr(string), text_repr(suffix)), - mismatch.describe()) + self.assertEqual( + "{} does not end with {}.".format(text_repr(string), text_repr(suffix)), + mismatch.describe(), + ) def test_describe_non_ascii_bytes(self): string = _b("A\xA7") suffix = _b("B\xA7") mismatch = DoesNotEndWith(string, suffix) - self.assertEqual(f"{string!r} does not end with {suffix!r}.", - mismatch.describe()) + self.assertEqual( + f"{string!r} does not end with {suffix!r}.", mismatch.describe() + ) class EndsWithTests(TestCase): - run_tests_with = FullStackRunTest def test_str(self): @@ -345,79 +345,96 @@ def test_mismatch_sets_expected(self): class TestSameMembers(TestCase, TestMatchersInterface): - - matches_matcher = SameMembers([1, 1, 2, 3, {'foo': 'bar'}]) + matches_matcher = SameMembers([1, 1, 2, 3, {"foo": "bar"}]) matches_matches = [ - [1, 1, 2, 3, {'foo': 'bar'}], - [3, {'foo': 'bar'}, 1, 2, 1], - [3, 2, 1, {'foo': 'bar'}, 1], - (2, {'foo': 'bar'}, 3, 1, 1), - ] + [1, 1, 2, 3, {"foo": "bar"}], + [3, {"foo": "bar"}, 1, 2, 1], + [3, 2, 1, {"foo": "bar"}, 1], + (2, {"foo": "bar"}, 3, 1, 1), + ] matches_mismatches = [ {1, 2, 3}, [1, 1, 2, 3, 5], - [1, 2, 3, {'foo': 'bar'}], - 'foo', - ] + [1, 2, 3, {"foo": "bar"}], + "foo", + ] describe_examples = [ - (("elements differ:\n" - "reference = ['apple', 'orange', 'canteloupe', 'watermelon', 'lemon', 'banana']\n" - "actual = ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe']\n" - ": \n" - "missing: ['watermelon']\n" - "extra: ['sparrow']" - ), - ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe',], - SameMembers( - ['apple', 'orange', 'canteloupe', 'watermelon', - 'lemon', 'banana',])), - ] + ( + ( + "elements differ:\n" + "reference = ['apple', 'orange', 'canteloupe', 'watermelon', 'lemon', 'banana']\n" + "actual = ['orange', 'apple', 'banana', 'sparrow', 'lemon', 'canteloupe']\n" + ": \n" + "missing: ['watermelon']\n" + "extra: ['sparrow']" + ), + [ + "orange", + "apple", + "banana", + "sparrow", + "lemon", + "canteloupe", + ], + SameMembers( + [ + "apple", + "orange", + "canteloupe", + "watermelon", + "lemon", + "banana", + ] + ), + ), + ] str_examples = [ - ('SameMembers([1, 2, 3])', SameMembers([1, 2, 3])), - ] + ("SameMembers([1, 2, 3])", SameMembers([1, 2, 3])), + ] class TestMatchesRegex(TestCase, TestMatchersInterface): - - matches_matcher = MatchesRegex('a|b') - matches_matches = ['a', 'b'] - matches_mismatches = ['c'] + matches_matcher = MatchesRegex("a|b") + matches_matches = ["a", "b"] + matches_mismatches = ["c"] str_examples = [ - ("MatchesRegex('a|b')", MatchesRegex('a|b')), - ("MatchesRegex('a|b', re.M)", MatchesRegex('a|b', re.M)), - ("MatchesRegex('a|b', re.I|re.M)", MatchesRegex('a|b', re.I|re.M)), + ("MatchesRegex('a|b')", MatchesRegex("a|b")), + ("MatchesRegex('a|b', re.M)", MatchesRegex("a|b", re.M)), + ("MatchesRegex('a|b', re.I|re.M)", MatchesRegex("a|b", re.I | re.M)), ("MatchesRegex({!r})".format(_b("\xA7")), MatchesRegex(_b("\xA7"))), ("MatchesRegex({!r})".format("\xA7"), MatchesRegex("\xA7")), - ] + ] describe_examples = [ - ("'c' does not match /a|b/", 'c', MatchesRegex('a|b')), - ("'c' does not match /a\\d/", 'c', MatchesRegex(r'a\d')), - ("{!r} does not match /\\s+\\xa7/".format(_b('c')), - _b('c'), MatchesRegex(_b("\\s+\xA7"))), - ("{!r} does not match /\\s+\\xa7/".format('c'), - 'c', MatchesRegex("\\s+\xA7")), - ] + ("'c' does not match /a|b/", "c", MatchesRegex("a|b")), + ("'c' does not match /a\\d/", "c", MatchesRegex(r"a\d")), + ( + "{!r} does not match /\\s+\\xa7/".format(_b("c")), + _b("c"), + MatchesRegex(_b("\\s+\xA7")), + ), + ("{!r} does not match /\\s+\\xa7/".format("c"), "c", MatchesRegex("\\s+\xA7")), + ] class TestHasLength(TestCase, TestMatchersInterface): - matches_matcher = HasLength(2) matches_matches = [[1, 2]] matches_mismatches = [[], [1], [3, 2, 1]] str_examples = [ ("HasLength(2)", HasLength(2)), - ] + ] describe_examples = [ ("len([]) != 1", [], HasLength(1)), - ] + ] def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_const.py b/testtools/tests/matchers/test_const.py index ec176ac5..de18a29a 100644 --- a/testtools/tests/matchers/test_const.py +++ b/testtools/tests/matchers/test_const.py @@ -7,24 +7,27 @@ class TestAlwaysInterface(TestMatchersInterface, TestCase): """:py:func:`~testtools.matchers.Always` always matches.""" + matches_matcher = Always() - matches_matches = [42, object(), 'hi mom'] + matches_matches = [42, object(), "hi mom"] matches_mismatches = [] - str_examples = [('Always()', Always())] + str_examples = [("Always()", Always())] describe_examples = [] class TestNeverInterface(TestMatchersInterface, TestCase): """:py:func:`~testtools.matchers.Never` never matches.""" + matches_matcher = Never() matches_matches = [] - matches_mismatches = [42, object(), 'hi mom'] + matches_mismatches = [42, object(), "hi mom"] - str_examples = [('Never()', Never())] - describe_examples = [('Inevitable mismatch on 42', 42, Never())] + str_examples = [("Never()", Never())] + describe_examples = [("Inevitable mismatch on 42", 42, Never())] def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_datastructures.py b/testtools/tests/matchers/test_datastructures.py index bb85c773..fa85fadf 100644 --- a/testtools/tests/matchers/test_datastructures.py +++ b/testtools/tests/matchers/test_datastructures.py @@ -12,21 +12,20 @@ LessThan, MatchesRegex, NotEquals, - ) +) from testtools.matchers._datastructures import ( ContainsAll, MatchesListwise, MatchesStructure, MatchesSetwise, - ) +) from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface def run_doctest(obj, name): p = doctest.DocTestParser() - t = p.get_doctest( - obj.__doc__, sys.modules[obj.__module__].__dict__, name, '', 0) + t = p.get_doctest(obj.__doc__, sys.modules[obj.__module__].__dict__, name, "", 0) r = doctest.DocTestRunner() output = io.StringIO() r.run(t, out=output.write) @@ -34,20 +33,17 @@ def run_doctest(obj, name): class TestMatchesListwise(TestCase): - run_tests_with = FullStackRunTest # XXX: Add interface tests. def test_docstring(self): - failure_count, output = run_doctest( - MatchesListwise, "MatchesListwise") + failure_count, output = run_doctest(MatchesListwise, "MatchesListwise") if failure_count: self.fail("Doctest failed with %s" % output) class TestMatchesStructure(TestCase, TestMatchersInterface): - class SimpleClass: def __init__(self, x, y): self.x = x @@ -59,153 +55,184 @@ def __init__(self, x, y): SimpleClass(2, 2), SimpleClass(1, 1), SimpleClass(3, 3), - ] + ] str_examples = [ ("MatchesStructure(x=Equals(1))", MatchesStructure(x=Equals(1))), ("MatchesStructure(y=Equals(2))", MatchesStructure(y=Equals(2))), - ("MatchesStructure(x=Equals(1), y=Equals(2))", - MatchesStructure(x=Equals(1), y=Equals(2))), - ] + ( + "MatchesStructure(x=Equals(1), y=Equals(2))", + MatchesStructure(x=Equals(1), y=Equals(2)), + ), + ] describe_examples = [ - ("""\ + ( + """\ Differences: [ 1 != 3: x -]""", SimpleClass(1, 2), MatchesStructure(x=Equals(3), y=Equals(2))), - ("""\ +]""", + SimpleClass(1, 2), + MatchesStructure(x=Equals(3), y=Equals(2)), + ), + ( + """\ Differences: [ 2 != 3: y -]""", SimpleClass(1, 2), MatchesStructure(x=Equals(1), y=Equals(3))), - ("""\ +]""", + SimpleClass(1, 2), + MatchesStructure(x=Equals(1), y=Equals(3)), + ), + ( + """\ Differences: [ 1 != 0: x 2 != 0: y -]""", SimpleClass(1, 2), MatchesStructure(x=Equals(0), y=Equals(0))), - ] +]""", + SimpleClass(1, 2), + MatchesStructure(x=Equals(0), y=Equals(0)), + ), + ] def test_fromExample(self): self.assertThat( self.SimpleClass(1, 2), - MatchesStructure.fromExample(self.SimpleClass(1, 3), 'x')) + MatchesStructure.fromExample(self.SimpleClass(1, 3), "x"), + ) def test_byEquality(self): - self.assertThat( - self.SimpleClass(1, 2), - MatchesStructure.byEquality(x=1)) + self.assertThat(self.SimpleClass(1, 2), MatchesStructure.byEquality(x=1)) def test_withStructure(self): self.assertThat( - self.SimpleClass(1, 2), - MatchesStructure.byMatcher(LessThan, x=2)) + self.SimpleClass(1, 2), MatchesStructure.byMatcher(LessThan, x=2) + ) def test_update(self): self.assertThat( - self.SimpleClass(1, 2), - MatchesStructure(x=NotEquals(1)).update(x=Equals(1))) + self.SimpleClass(1, 2), MatchesStructure(x=NotEquals(1)).update(x=Equals(1)) + ) def test_update_none(self): self.assertThat( self.SimpleClass(1, 2), - MatchesStructure(x=Equals(1), z=NotEquals(42)).update( - z=None)) + MatchesStructure(x=Equals(1), z=NotEquals(42)).update(z=None), + ) class TestMatchesSetwise(TestCase): - run_tests_with = FullStackRunTest - def assertMismatchWithDescriptionMatching(self, value, matcher, - description_matcher): + def assertMismatchWithDescriptionMatching( + self, value, matcher, description_matcher + ): mismatch = matcher.match(value) if mismatch is None: self.fail(f"{matcher} matched {value}") actual_description = mismatch.describe() self.assertThat( actual_description, - Annotate( - f"{matcher} matching {value}", - description_matcher)) + Annotate(f"{matcher} matching {value}", description_matcher), + ) def test_matches(self): - self.assertIs( - None, MatchesSetwise(Equals(1), Equals(2)).match([2, 1])) + self.assertIs(None, MatchesSetwise(Equals(1), Equals(2)).match([2, 1])) def test_mismatches(self): self.assertMismatchWithDescriptionMatching( - [2, 3], MatchesSetwise(Equals(1), Equals(2)), - MatchesRegex('.*There was 1 mismatch$', re.S)) + [2, 3], + MatchesSetwise(Equals(1), Equals(2)), + MatchesRegex(".*There was 1 mismatch$", re.S), + ) def test_too_many_matchers(self): self.assertMismatchWithDescriptionMatching( - [2, 3], MatchesSetwise(Equals(1), Equals(2), Equals(3)), - Equals('There was 1 matcher left over: Equals(1)')) + [2, 3], + MatchesSetwise(Equals(1), Equals(2), Equals(3)), + Equals("There was 1 matcher left over: Equals(1)"), + ) def test_too_many_values(self): self.assertMismatchWithDescriptionMatching( - [1, 2, 3], MatchesSetwise(Equals(1), Equals(2)), - Equals('There was 1 value left over: [3]')) + [1, 2, 3], + MatchesSetwise(Equals(1), Equals(2)), + Equals("There was 1 value left over: [3]"), + ) def test_two_too_many_matchers(self): self.assertMismatchWithDescriptionMatching( - [3], MatchesSetwise(Equals(1), Equals(2), Equals(3)), + [3], + MatchesSetwise(Equals(1), Equals(2), Equals(3)), MatchesRegex( - r'There were 2 matchers left over: Equals\([12]\), ' - r'Equals\([12]\)')) + r"There were 2 matchers left over: Equals\([12]\), " r"Equals\([12]\)" + ), + ) def test_two_too_many_values(self): self.assertMismatchWithDescriptionMatching( - [1, 2, 3, 4], MatchesSetwise(Equals(1), Equals(2)), - MatchesRegex( - r'There were 2 values left over: \[[34], [34]\]')) + [1, 2, 3, 4], + MatchesSetwise(Equals(1), Equals(2)), + MatchesRegex(r"There were 2 values left over: \[[34], [34]\]"), + ) def test_mismatch_and_too_many_matchers(self): self.assertMismatchWithDescriptionMatching( - [2, 3], MatchesSetwise(Equals(0), Equals(1), Equals(2)), + [2, 3], + MatchesSetwise(Equals(0), Equals(1), Equals(2)), MatchesRegex( - r'.*There was 1 mismatch and 1 extra matcher: Equals\([01]\)', - re.S)) + r".*There was 1 mismatch and 1 extra matcher: Equals\([01]\)", re.S + ), + ) def test_mismatch_and_too_many_values(self): self.assertMismatchWithDescriptionMatching( - [2, 3, 4], MatchesSetwise(Equals(1), Equals(2)), - MatchesRegex( - r'.*There was 1 mismatch and 1 extra value: \[[34]\]', - re.S)) + [2, 3, 4], + MatchesSetwise(Equals(1), Equals(2)), + MatchesRegex(r".*There was 1 mismatch and 1 extra value: \[[34]\]", re.S), + ) def test_mismatch_and_two_too_many_matchers(self): self.assertMismatchWithDescriptionMatching( - [3, 4], MatchesSetwise( - Equals(0), Equals(1), Equals(2), Equals(3)), + [3, 4], + MatchesSetwise(Equals(0), Equals(1), Equals(2), Equals(3)), MatchesRegex( - '.*There was 1 mismatch and 2 extra matchers: ' - r'Equals\([012]\), Equals\([012]\)', re.S)) + ".*There was 1 mismatch and 2 extra matchers: " + r"Equals\([012]\), Equals\([012]\)", + re.S, + ), + ) def test_mismatch_and_two_too_many_values(self): self.assertMismatchWithDescriptionMatching( - [2, 3, 4, 5], MatchesSetwise(Equals(1), Equals(2)), + [2, 3, 4, 5], + MatchesSetwise(Equals(1), Equals(2)), MatchesRegex( - r'.*There was 1 mismatch and 2 extra values: \[[145], [145]\]', - re.S)) + r".*There was 1 mismatch and 2 extra values: \[[145], [145]\]", re.S + ), + ) class TestContainsAllInterface(TestCase, TestMatchersInterface): + matches_matcher = ContainsAll(["foo", "bar"]) + matches_matches = [["foo", "bar"], ["foo", "z", "bar"], ["bar", "foo"]] + matches_mismatches = [["f", "g"], ["foo", "baz"], []] - matches_matcher = ContainsAll(['foo', 'bar']) - matches_matches = [['foo', 'bar'], ['foo', 'z', 'bar'], ['bar', 'foo']] - matches_mismatches = [['f', 'g'], ['foo', 'baz'], []] - - str_examples = [( - "MatchesAll(Contains('foo'), Contains('bar'))", - ContainsAll(['foo', 'bar'])), - ] + str_examples = [ + ("MatchesAll(Contains('foo'), Contains('bar'))", ContainsAll(["foo", "bar"])), + ] - describe_examples = [("""Differences: [ + describe_examples = [ + ( + """Differences: [ 'baz' not in 'foo' ]""", - 'foo', ContainsAll(['foo', 'baz']))] + "foo", + ContainsAll(["foo", "baz"]), + ) + ] def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_dict.py b/testtools/tests/matchers/test_dict.py index cdd1918e..6d159681 100644 --- a/testtools/tests/matchers/test_dict.py +++ b/testtools/tests/matchers/test_dict.py @@ -3,7 +3,7 @@ Equals, NotEquals, Not, - ) +) from testtools.matchers._dict import ( ContainedByDict, ContainsDict, @@ -11,241 +11,263 @@ MatchesAllDict, MatchesDict, _SubDictOf, - ) +) from testtools.tests.matchers.helpers import TestMatchersInterface class TestMatchesAllDictInterface(TestCase, TestMatchersInterface): - - matches_matcher = MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)}) + matches_matcher = MatchesAllDict({"a": NotEquals(1), "b": NotEquals(2)}) matches_matches = [3, 4] matches_mismatches = [1, 2] str_examples = [ - ("MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})", - matches_matcher)] + ("MatchesAllDict({'a': NotEquals(1), 'b': NotEquals(2)})", matches_matcher) + ] describe_examples = [ ("""a: 1 == 1""", 1, matches_matcher), - ] + ] class TestKeysEqualEmpty(TestCase, TestMatchersInterface): - matches_matcher = KeysEqual() matches_matches = [ {}, - ] + ] matches_mismatches = [ - {'foo': 0, 'bar': 1}, - {'foo': 0}, - {'bar': 1}, - {'foo': 0, 'bar': 1, 'baz': 2}, - {'a': None, 'b': None, 'c': None}, - ] + {"foo": 0, "bar": 1}, + {"foo": 0}, + {"bar": 1}, + {"foo": 0, "bar": 1, "baz": 2}, + {"a": None, "b": None, "c": None}, + ] str_examples = [ ("KeysEqual()", KeysEqual()), - ] + ] describe_examples = [ - ("[] does not match {1: 2}: Keys not equal", - {1: 2}, matches_matcher), - ] + ("[] does not match {1: 2}: Keys not equal", {1: 2}, matches_matcher), + ] class TestKeysEqualWithList(TestCase, TestMatchersInterface): - - matches_matcher = KeysEqual('foo', 'bar') + matches_matcher = KeysEqual("foo", "bar") matches_matches = [ - {'foo': 0, 'bar': 1}, - ] + {"foo": 0, "bar": 1}, + ] matches_mismatches = [ {}, - {'foo': 0}, - {'bar': 1}, - {'foo': 0, 'bar': 1, 'baz': 2}, - {'a': None, 'b': None, 'c': None}, - ] + {"foo": 0}, + {"bar": 1}, + {"foo": 0, "bar": 1, "baz": 2}, + {"a": None, "b": None, "c": None}, + ] str_examples = [ - ("KeysEqual('foo', 'bar')", KeysEqual('foo', 'bar')), - ] + ("KeysEqual('foo', 'bar')", KeysEqual("foo", "bar")), + ] describe_examples = [] def test_description(self): - matchee = {'foo': 0, 'bar': 1, 'baz': 2} - mismatch = KeysEqual('foo', 'bar').match(matchee) + matchee = {"foo": 0, "bar": 1, "baz": 2} + mismatch = KeysEqual("foo", "bar").match(matchee) description = mismatch.describe() self.assertThat( - description, Equals( - "['bar', 'foo'] does not match %r: Keys not equal" - % (matchee,))) + description, + Equals("['bar', 'foo'] does not match %r: Keys not equal" % (matchee,)), + ) class TestKeysEqualWithDict(TestKeysEqualWithList): - - matches_matcher = KeysEqual({'foo': 3, 'bar': 4}) + matches_matcher = KeysEqual({"foo": 3, "bar": 4}) class TestSubDictOf(TestCase, TestMatchersInterface): - - matches_matcher = _SubDictOf({'foo': 'bar', 'baz': 'qux'}) + matches_matcher = _SubDictOf({"foo": "bar", "baz": "qux"}) matches_matches = [ - {'foo': 'bar', 'baz': 'qux'}, - {'foo': 'bar'}, - ] + {"foo": "bar", "baz": "qux"}, + {"foo": "bar"}, + ] matches_mismatches = [ - {'foo': 'bar', 'baz': 'qux', 'cat': 'dog'}, - {'foo': 'bar', 'cat': 'dog'}, - ] + {"foo": "bar", "baz": "qux", "cat": "dog"}, + {"foo": "bar", "cat": "dog"}, + ] str_examples = [] describe_examples = [] class TestMatchesDict(TestCase, TestMatchersInterface): - - matches_matcher = MatchesDict( - {'foo': Equals('bar'), 'baz': Not(Equals('qux'))}) + matches_matcher = MatchesDict({"foo": Equals("bar"), "baz": Not(Equals("qux"))}) matches_matches = [ - {'foo': 'bar', 'baz': None}, - {'foo': 'bar', 'baz': 'quux'}, - ] + {"foo": "bar", "baz": None}, + {"foo": "bar", "baz": "quux"}, + ] matches_mismatches = [ {}, - {'foo': 'bar', 'baz': 'qux'}, - {'foo': 'bop', 'baz': 'qux'}, - {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'}, - {'foo': 'bar', 'cat': 'dog'}, - ] + {"foo": "bar", "baz": "qux"}, + {"foo": "bop", "baz": "qux"}, + {"foo": "bar", "baz": "quux", "cat": "dog"}, + {"foo": "bar", "cat": "dog"}, + ] str_examples = [ - ("MatchesDict({{'baz': {}, 'foo': {}}})".format( - Not(Equals('qux')), Equals('bar')), - matches_matcher), - ] + ( + "MatchesDict({{'baz': {}, 'foo': {}}})".format( + Not(Equals("qux")), Equals("bar") + ), + matches_matcher, + ), + ] describe_examples = [ - ("Missing: {\n" - " 'baz': Not(Equals('qux')),\n" - " 'foo': Equals('bar'),\n" - "}", - {}, matches_matcher), - ("Differences: {\n" - " 'baz': 'qux' matches Equals('qux'),\n" - "}", - {'foo': 'bar', 'baz': 'qux'}, matches_matcher), - ("Differences: {\n" - " 'baz': 'qux' matches Equals('qux'),\n" - " 'foo': 'bop' != 'bar',\n" - "}", - {'foo': 'bop', 'baz': 'qux'}, matches_matcher), - ("Extra: {\n" - " 'cat': 'dog',\n" - "}", - {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'}, matches_matcher), - ("Extra: {\n" - " 'cat': 'dog',\n" - "}\n" - "Missing: {\n" - " 'baz': Not(Equals('qux')),\n" - "}", - {'foo': 'bar', 'cat': 'dog'}, matches_matcher), - ] + ( + "Missing: {\n" + " 'baz': Not(Equals('qux')),\n" + " 'foo': Equals('bar'),\n" + "}", + {}, + matches_matcher, + ), + ( + "Differences: {\n" " 'baz': 'qux' matches Equals('qux'),\n" "}", + {"foo": "bar", "baz": "qux"}, + matches_matcher, + ), + ( + "Differences: {\n" + " 'baz': 'qux' matches Equals('qux'),\n" + " 'foo': 'bop' != 'bar',\n" + "}", + {"foo": "bop", "baz": "qux"}, + matches_matcher, + ), + ( + "Extra: {\n" " 'cat': 'dog',\n" "}", + {"foo": "bar", "baz": "quux", "cat": "dog"}, + matches_matcher, + ), + ( + "Extra: {\n" + " 'cat': 'dog',\n" + "}\n" + "Missing: {\n" + " 'baz': Not(Equals('qux')),\n" + "}", + {"foo": "bar", "cat": "dog"}, + matches_matcher, + ), + ] class TestContainsDict(TestCase, TestMatchersInterface): - - matches_matcher = ContainsDict( - {'foo': Equals('bar'), 'baz': Not(Equals('qux'))}) + matches_matcher = ContainsDict({"foo": Equals("bar"), "baz": Not(Equals("qux"))}) matches_matches = [ - {'foo': 'bar', 'baz': None}, - {'foo': 'bar', 'baz': 'quux'}, - {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'}, - ] + {"foo": "bar", "baz": None}, + {"foo": "bar", "baz": "quux"}, + {"foo": "bar", "baz": "quux", "cat": "dog"}, + ] matches_mismatches = [ {}, - {'foo': 'bar', 'baz': 'qux'}, - {'foo': 'bop', 'baz': 'qux'}, - {'foo': 'bar', 'cat': 'dog'}, - {'foo': 'bar'}, - ] + {"foo": "bar", "baz": "qux"}, + {"foo": "bop", "baz": "qux"}, + {"foo": "bar", "cat": "dog"}, + {"foo": "bar"}, + ] str_examples = [ - ("ContainsDict({{'baz': {}, 'foo': {}}})".format( - Not(Equals('qux')), Equals('bar')), - matches_matcher), - ] + ( + "ContainsDict({{'baz': {}, 'foo': {}}})".format( + Not(Equals("qux")), Equals("bar") + ), + matches_matcher, + ), + ] describe_examples = [ - ("Missing: {\n" - " 'baz': Not(Equals('qux')),\n" - " 'foo': Equals('bar'),\n" - "}", - {}, matches_matcher), - ("Differences: {\n" - " 'baz': 'qux' matches Equals('qux'),\n" - "}", - {'foo': 'bar', 'baz': 'qux'}, matches_matcher), - ("Differences: {\n" - " 'baz': 'qux' matches Equals('qux'),\n" - " 'foo': 'bop' != 'bar',\n" - "}", - {'foo': 'bop', 'baz': 'qux'}, matches_matcher), - ("Missing: {\n" - " 'baz': Not(Equals('qux')),\n" - "}", - {'foo': 'bar', 'cat': 'dog'}, matches_matcher), - ] + ( + "Missing: {\n" + " 'baz': Not(Equals('qux')),\n" + " 'foo': Equals('bar'),\n" + "}", + {}, + matches_matcher, + ), + ( + "Differences: {\n" " 'baz': 'qux' matches Equals('qux'),\n" "}", + {"foo": "bar", "baz": "qux"}, + matches_matcher, + ), + ( + "Differences: {\n" + " 'baz': 'qux' matches Equals('qux'),\n" + " 'foo': 'bop' != 'bar',\n" + "}", + {"foo": "bop", "baz": "qux"}, + matches_matcher, + ), + ( + "Missing: {\n" " 'baz': Not(Equals('qux')),\n" "}", + {"foo": "bar", "cat": "dog"}, + matches_matcher, + ), + ] class TestContainedByDict(TestCase, TestMatchersInterface): - - matches_matcher = ContainedByDict( - {'foo': Equals('bar'), 'baz': Not(Equals('qux'))}) + matches_matcher = ContainedByDict({"foo": Equals("bar"), "baz": Not(Equals("qux"))}) matches_matches = [ {}, - {'foo': 'bar'}, - {'foo': 'bar', 'baz': 'quux'}, - {'baz': 'quux'}, - ] + {"foo": "bar"}, + {"foo": "bar", "baz": "quux"}, + {"baz": "quux"}, + ] matches_mismatches = [ - {'foo': 'bar', 'baz': 'quux', 'cat': 'dog'}, - {'foo': 'bar', 'baz': 'qux'}, - {'foo': 'bop', 'baz': 'qux'}, - {'foo': 'bar', 'cat': 'dog'}, - ] + {"foo": "bar", "baz": "quux", "cat": "dog"}, + {"foo": "bar", "baz": "qux"}, + {"foo": "bop", "baz": "qux"}, + {"foo": "bar", "cat": "dog"}, + ] str_examples = [ - ("ContainedByDict({{'baz': {}, 'foo': {}}})".format( - Not(Equals('qux')), Equals('bar')), - matches_matcher), - ] + ( + "ContainedByDict({{'baz': {}, 'foo': {}}})".format( + Not(Equals("qux")), Equals("bar") + ), + matches_matcher, + ), + ] describe_examples = [ - ("Differences: {\n" - " 'baz': 'qux' matches Equals('qux'),\n" - "}", - {'foo': 'bar', 'baz': 'qux'}, matches_matcher), - ("Differences: {\n" - " 'baz': 'qux' matches Equals('qux'),\n" - " 'foo': 'bop' != 'bar',\n" - "}", - {'foo': 'bop', 'baz': 'qux'}, matches_matcher), - ("Extra: {\n" - " 'cat': 'dog',\n" - "}", - {'foo': 'bar', 'cat': 'dog'}, matches_matcher), - ] + ( + "Differences: {\n" " 'baz': 'qux' matches Equals('qux'),\n" "}", + {"foo": "bar", "baz": "qux"}, + matches_matcher, + ), + ( + "Differences: {\n" + " 'baz': 'qux' matches Equals('qux'),\n" + " 'foo': 'bop' != 'bar',\n" + "}", + {"foo": "bop", "baz": "qux"}, + matches_matcher, + ), + ( + "Extra: {\n" " 'cat': 'dog',\n" "}", + {"foo": "bar", "cat": "dog"}, + matches_matcher, + ), + ] def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_doctest.py b/testtools/tests/matchers/test_doctest.py index aac85839..ceceef17 100644 --- a/testtools/tests/matchers/test_doctest.py +++ b/testtools/tests/matchers/test_doctest.py @@ -5,47 +5,53 @@ from testtools import TestCase from testtools.compat import ( _b, - ) +) from testtools.matchers._doctest import DocTestMatches from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface - class TestDocTestMatchesInterface(TestCase, TestMatchersInterface): - matches_matcher = DocTestMatches("Ran 1 test in ...s", doctest.ELLIPSIS) matches_matches = ["Ran 1 test in 0.000s", "Ran 1 test in 1.234s"] matches_mismatches = ["Ran 1 tests in 0.000s", "Ran 2 test in 0.000s"] - str_examples = [("DocTestMatches('Ran 1 test in ...s\\n')", - DocTestMatches("Ran 1 test in ...s")), + str_examples = [ + ( + "DocTestMatches('Ran 1 test in ...s\\n')", + DocTestMatches("Ran 1 test in ...s"), + ), ("DocTestMatches('foo\\n', flags=8)", DocTestMatches("foo", flags=8)), - ] + ] - describe_examples = [('Expected:\n Ran 1 tests in ...s\nGot:\n' - ' Ran 1 test in 0.123s\n', "Ran 1 test in 0.123s", - DocTestMatches("Ran 1 tests in ...s", doctest.ELLIPSIS))] + describe_examples = [ + ( + "Expected:\n Ran 1 tests in ...s\nGot:\n" " Ran 1 test in 0.123s\n", + "Ran 1 test in 0.123s", + DocTestMatches("Ran 1 tests in ...s", doctest.ELLIPSIS), + ) + ] class TestDocTestMatchesInterfaceUnicode(TestCase, TestMatchersInterface): - matches_matcher = DocTestMatches("\xa7...", doctest.ELLIPSIS) matches_matches = ["\xa7", "\xa7 more\n"] matches_mismatches = ["\\xa7", "more \xa7", "\n\xa7"] - str_examples = [("DocTestMatches({!r})".format("\xa7\n"), - DocTestMatches("\xa7")), - ] + str_examples = [ + ("DocTestMatches({!r})".format("\xa7\n"), DocTestMatches("\xa7")), + ] - describe_examples = [( - "Expected:\n \xa7\nGot:\n a\n", - "a", - DocTestMatches("\xa7", doctest.ELLIPSIS))] + describe_examples = [ + ( + "Expected:\n \xa7\nGot:\n a\n", + "a", + DocTestMatches("\xa7", doctest.ELLIPSIS), + ) + ] class TestDocTestMatchesSpecific(TestCase): - run_tests_with = FullStackRunTest def test___init__simple(self): @@ -65,10 +71,10 @@ def test_describe_non_ascii_bytes(self): and under Python 3 using bytes objects will reasonably raise an error. """ header = _b("\x89PNG\r\n\x1a\n...") - self.assertRaises(TypeError, - DocTestMatches, header, doctest.ELLIPSIS) + self.assertRaises(TypeError, DocTestMatches, header, doctest.ELLIPSIS) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_exception.py b/testtools/tests/matchers/test_exception.py index 8431985b..0c329cff 100644 --- a/testtools/tests/matchers/test_exception.py +++ b/testtools/tests/matchers/test_exception.py @@ -6,12 +6,12 @@ from testtools.matchers import ( AfterPreprocessing, Equals, - ) +) from testtools.matchers._exception import ( MatchesException, Raises, raises, - ) +) from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface @@ -24,101 +24,100 @@ def make_error(type, *args, **kwargs): class TestMatchesExceptionInstanceInterface(TestCase, TestMatchersInterface): - matches_matcher = MatchesException(ValueError("foo")) - error_foo = make_error(ValueError, 'foo') - error_bar = make_error(ValueError, 'bar') - error_base_foo = make_error(Exception, 'foo') + error_foo = make_error(ValueError, "foo") + error_bar = make_error(ValueError, "bar") + error_base_foo = make_error(Exception, "foo") matches_matches = [error_foo] matches_mismatches = [error_bar, error_base_foo] if sys.version_info >= (3, 7): # exception's repr has changed - _e = '' + _e = "" else: - _e = ',' + _e = "," str_examples = [ - ("MatchesException(Exception('foo'%s))" % _e, - MatchesException(Exception('foo'))) - ] + ( + "MatchesException(Exception('foo'%s))" % _e, + MatchesException(Exception("foo")), + ) + ] describe_examples = [ - (f"{Exception!r} is not a {ValueError!r}", - error_base_foo, - MatchesException(ValueError("foo"))), - ("ValueError('bar'%s) has different arguments to ValueError('foo'%s)." - % (_e, _e), - error_bar, - MatchesException(ValueError("foo"))), - ] + ( + f"{Exception!r} is not a {ValueError!r}", + error_base_foo, + MatchesException(ValueError("foo")), + ), + ( + "ValueError('bar'%s) has different arguments to ValueError('foo'%s)." + % (_e, _e), + error_bar, + MatchesException(ValueError("foo")), + ), + ] class TestMatchesExceptionTypeInterface(TestCase, TestMatchersInterface): - matches_matcher = MatchesException(ValueError) - error_foo = make_error(ValueError, 'foo') - error_sub = make_error(UnicodeError, 'bar') - error_base_foo = make_error(Exception, 'foo') + error_foo = make_error(ValueError, "foo") + error_sub = make_error(UnicodeError, "bar") + error_base_foo = make_error(Exception, "foo") matches_matches = [error_foo, error_sub] matches_mismatches = [error_base_foo] - str_examples = [ - ("MatchesException(%r)" % Exception, - MatchesException(Exception)) - ] + str_examples = [("MatchesException(%r)" % Exception, MatchesException(Exception))] describe_examples = [ - (f"{Exception!r} is not a {ValueError!r}", - error_base_foo, - MatchesException(ValueError)), - ] + ( + f"{Exception!r} is not a {ValueError!r}", + error_base_foo, + MatchesException(ValueError), + ), + ] class TestMatchesExceptionTypeReInterface(TestCase, TestMatchersInterface): - - matches_matcher = MatchesException(ValueError, 'fo.') - error_foo = make_error(ValueError, 'foo') - error_sub = make_error(UnicodeError, 'foo') - error_bar = make_error(ValueError, 'bar') + matches_matcher = MatchesException(ValueError, "fo.") + error_foo = make_error(ValueError, "foo") + error_sub = make_error(UnicodeError, "foo") + error_bar = make_error(ValueError, "bar") matches_matches = [error_foo, error_sub] matches_mismatches = [error_bar] str_examples = [ - ("MatchesException(%r)" % Exception, - MatchesException(Exception, 'fo.')) - ] + ("MatchesException(%r)" % Exception, MatchesException(Exception, "fo.")) + ] describe_examples = [ - ("'bar' does not match /fo./", - error_bar, MatchesException(ValueError, "fo.")), - ] + ("'bar' does not match /fo./", error_bar, MatchesException(ValueError, "fo.")), + ] class TestMatchesExceptionTypeMatcherInterface(TestCase, TestMatchersInterface): - matches_matcher = MatchesException( - ValueError, AfterPreprocessing(str, Equals('foo'))) - error_foo = make_error(ValueError, 'foo') - error_sub = make_error(UnicodeError, 'foo') - error_bar = make_error(ValueError, 'bar') + ValueError, AfterPreprocessing(str, Equals("foo")) + ) + error_foo = make_error(ValueError, "foo") + error_sub = make_error(UnicodeError, "foo") + error_bar = make_error(ValueError, "bar") matches_matches = [error_foo, error_sub] matches_mismatches = [error_bar] str_examples = [ - ("MatchesException(%r)" % Exception, - MatchesException(Exception, Equals('foo'))) - ] + ("MatchesException(%r)" % Exception, MatchesException(Exception, Equals("foo"))) + ] describe_examples = [ - (f"{error_bar[1]!r} != 5", - error_bar, MatchesException(ValueError, Equals(5))), - ] + (f"{error_bar[1]!r} != 5", error_bar, MatchesException(ValueError, Equals(5))), + ] class TestRaisesInterface(TestCase, TestMatchersInterface): - matches_matcher = Raises() + def boom(): - raise Exception('foo') + raise Exception("foo") + matches_matches = [boom] - matches_mismatches = [lambda:None] + matches_mismatches = [lambda: None] # Tricky to get function objects to render constantly, and the interfaces # helper uses assertEqual rather than (for instance) DocTestMatches. @@ -128,15 +127,16 @@ def boom(): class TestRaisesExceptionMatcherInterface(TestCase, TestMatchersInterface): + matches_matcher = Raises(exception_matcher=MatchesException(Exception("foo"))) - matches_matcher = Raises( - exception_matcher=MatchesException(Exception('foo'))) def boom_bar(): - raise Exception('bar') + raise Exception("bar") + def boom_foo(): - raise Exception('foo') + raise Exception("foo") + matches_matches = [boom_foo] - matches_mismatches = [lambda:None, boom_bar] + matches_mismatches = [lambda: None, boom_bar] # Tricky to get function objects to render constantly, and the interfaces # helper uses assertEqual rather than (for instance) DocTestMatches. @@ -146,11 +146,10 @@ def boom_foo(): class TestRaisesBaseTypes(TestCase): - run_tests_with = FullStackRunTest def raiser(self): - raise KeyboardInterrupt('foo') + raise KeyboardInterrupt("foo") def test_KeyboardInterrupt_matched(self): # When KeyboardInterrupt is matched, it is swallowed. @@ -160,35 +159,41 @@ def test_KeyboardInterrupt_matched(self): def test_KeyboardInterrupt_propagates(self): # The default 'it raised' propagates KeyboardInterrupt. match_keyb = Raises(MatchesException(KeyboardInterrupt)) + def raise_keyb_from_match(): matcher = Raises() matcher.match(self.raiser) + self.assertThat(raise_keyb_from_match, match_keyb) def test_KeyboardInterrupt_match_Exception_propagates(self): # If the raised exception isn't matched, and it is not a subclass of # Exception, it is propagated. match_keyb = Raises(MatchesException(KeyboardInterrupt)) + def raise_keyb_from_match(): matcher = Raises(MatchesException(Exception)) matcher.match(self.raiser) + self.assertThat(raise_keyb_from_match, match_keyb) class TestRaisesConvenience(TestCase): - run_tests_with = FullStackRunTest def test_exc_type(self): - self.assertThat(lambda: 1/0, raises(ZeroDivisionError)) + self.assertThat(lambda: 1 / 0, raises(ZeroDivisionError)) def test_exc_value(self): e = RuntimeError("You lose!") + def raiser(): raise e + self.assertThat(raiser, raises(e)) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_filesystem.py b/testtools/tests/matchers/test_filesystem.py index d1d0ea9b..9f47f30d 100644 --- a/testtools/tests/matchers/test_filesystem.py +++ b/testtools/tests/matchers/test_filesystem.py @@ -10,7 +10,7 @@ Contains, DocTestMatches, Equals, - ) +) from testtools.matchers._filesystem import ( DirContains, DirExists, @@ -20,18 +20,17 @@ PathExists, SamePath, TarballContains, - ) +) class PathHelpers: - def mkdtemp(self): directory = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, directory) return directory - def create_file(self, filename, contents=''): - fp = open(filename, 'w') + def create_file(self, filename, contents=""): + fp = open(filename, "w") try: fp.write(contents) finally: @@ -42,186 +41,186 @@ def touch(self, filename): class TestPathExists(TestCase, PathHelpers): - def test_exists(self): tempdir = self.mkdtemp() self.assertThat(tempdir, PathExists()) def test_not_exists(self): - doesntexist = os.path.join(self.mkdtemp(), 'doesntexist') + doesntexist = os.path.join(self.mkdtemp(), "doesntexist") mismatch = PathExists().match(doesntexist) - self.assertThat( - "%s does not exist." % doesntexist, Equals(mismatch.describe())) + self.assertThat("%s does not exist." % doesntexist, Equals(mismatch.describe())) class TestDirExists(TestCase, PathHelpers): - def test_exists(self): tempdir = self.mkdtemp() self.assertThat(tempdir, DirExists()) def test_not_exists(self): - doesntexist = os.path.join(self.mkdtemp(), 'doesntexist') + doesntexist = os.path.join(self.mkdtemp(), "doesntexist") mismatch = DirExists().match(doesntexist) self.assertThat( - PathExists().match(doesntexist).describe(), - Equals(mismatch.describe())) + PathExists().match(doesntexist).describe(), Equals(mismatch.describe()) + ) def test_not_a_directory(self): - filename = os.path.join(self.mkdtemp(), 'foo') + filename = os.path.join(self.mkdtemp(), "foo") self.touch(filename) mismatch = DirExists().match(filename) self.assertThat( - "%s is not a directory." % filename, Equals(mismatch.describe())) + "%s is not a directory." % filename, Equals(mismatch.describe()) + ) class TestFileExists(TestCase, PathHelpers): - def test_exists(self): tempdir = self.mkdtemp() - filename = os.path.join(tempdir, 'filename') + filename = os.path.join(tempdir, "filename") self.touch(filename) self.assertThat(filename, FileExists()) def test_not_exists(self): - doesntexist = os.path.join(self.mkdtemp(), 'doesntexist') + doesntexist = os.path.join(self.mkdtemp(), "doesntexist") mismatch = FileExists().match(doesntexist) self.assertThat( - PathExists().match(doesntexist).describe(), - Equals(mismatch.describe())) + PathExists().match(doesntexist).describe(), Equals(mismatch.describe()) + ) def test_not_a_file(self): tempdir = self.mkdtemp() mismatch = FileExists().match(tempdir) - self.assertThat( - "%s is not a file." % tempdir, Equals(mismatch.describe())) + self.assertThat("%s is not a file." % tempdir, Equals(mismatch.describe())) class TestDirContains(TestCase, PathHelpers): - def test_empty(self): tempdir = self.mkdtemp() self.assertThat(tempdir, DirContains([])) def test_not_exists(self): - doesntexist = os.path.join(self.mkdtemp(), 'doesntexist') + doesntexist = os.path.join(self.mkdtemp(), "doesntexist") mismatch = DirContains([]).match(doesntexist) self.assertThat( - PathExists().match(doesntexist).describe(), - Equals(mismatch.describe())) + PathExists().match(doesntexist).describe(), Equals(mismatch.describe()) + ) def test_contains_files(self): tempdir = self.mkdtemp() - self.touch(os.path.join(tempdir, 'foo')) - self.touch(os.path.join(tempdir, 'bar')) - self.assertThat(tempdir, DirContains(['bar', 'foo'])) + self.touch(os.path.join(tempdir, "foo")) + self.touch(os.path.join(tempdir, "bar")) + self.assertThat(tempdir, DirContains(["bar", "foo"])) def test_matcher(self): tempdir = self.mkdtemp() - self.touch(os.path.join(tempdir, 'foo')) - self.touch(os.path.join(tempdir, 'bar')) - self.assertThat(tempdir, DirContains(matcher=Contains('bar'))) + self.touch(os.path.join(tempdir, "foo")) + self.touch(os.path.join(tempdir, "bar")) + self.assertThat(tempdir, DirContains(matcher=Contains("bar"))) def test_neither_specified(self): self.assertRaises(AssertionError, DirContains) def test_both_specified(self): self.assertRaises( - AssertionError, DirContains, filenames=[], matcher=Contains('a')) + AssertionError, DirContains, filenames=[], matcher=Contains("a") + ) def test_does_not_contain_files(self): tempdir = self.mkdtemp() - self.touch(os.path.join(tempdir, 'foo')) - mismatch = DirContains(['bar', 'foo']).match(tempdir) + self.touch(os.path.join(tempdir, "foo")) + mismatch = DirContains(["bar", "foo"]).match(tempdir) self.assertThat( - Equals(['bar', 'foo']).match(['foo']).describe(), - Equals(mismatch.describe())) + Equals(["bar", "foo"]).match(["foo"]).describe(), + Equals(mismatch.describe()), + ) class TestFileContains(TestCase, PathHelpers): - def test_not_exists(self): - doesntexist = os.path.join(self.mkdtemp(), 'doesntexist') - mismatch = FileContains('').match(doesntexist) + doesntexist = os.path.join(self.mkdtemp(), "doesntexist") + mismatch = FileContains("").match(doesntexist) self.assertThat( - PathExists().match(doesntexist).describe(), - Equals(mismatch.describe())) + PathExists().match(doesntexist).describe(), Equals(mismatch.describe()) + ) def test_contains(self): tempdir = self.mkdtemp() - filename = os.path.join(tempdir, 'foo') - self.create_file(filename, 'Hello World!') - self.assertThat(filename, FileContains('Hello World!')) + filename = os.path.join(tempdir, "foo") + self.create_file(filename, "Hello World!") + self.assertThat(filename, FileContains("Hello World!")) def test_matcher(self): tempdir = self.mkdtemp() - filename = os.path.join(tempdir, 'foo') - self.create_file(filename, 'Hello World!') - self.assertThat( - filename, FileContains(matcher=DocTestMatches('Hello World!'))) + filename = os.path.join(tempdir, "foo") + self.create_file(filename, "Hello World!") + self.assertThat(filename, FileContains(matcher=DocTestMatches("Hello World!"))) def test_neither_specified(self): self.assertRaises(AssertionError, FileContains) def test_both_specified(self): self.assertRaises( - AssertionError, FileContains, contents=[], matcher=Contains('a')) + AssertionError, FileContains, contents=[], matcher=Contains("a") + ) def test_does_not_contain(self): tempdir = self.mkdtemp() - filename = os.path.join(tempdir, 'foo') - self.create_file(filename, 'Goodbye Cruel World!') - mismatch = FileContains('Hello World!').match(filename) + filename = os.path.join(tempdir, "foo") + self.create_file(filename, "Goodbye Cruel World!") + mismatch = FileContains("Hello World!").match(filename) self.assertThat( - Equals('Hello World!').match('Goodbye Cruel World!').describe(), - Equals(mismatch.describe())) -class TestTarballContains(TestCase, PathHelpers): + Equals("Hello World!").match("Goodbye Cruel World!").describe(), + Equals(mismatch.describe()), + ) + +class TestTarballContains(TestCase, PathHelpers): def test_match(self): tempdir = self.mkdtemp() + def in_temp_dir(x): return os.path.join(tempdir, x) - self.touch(in_temp_dir('a')) - self.touch(in_temp_dir('b')) - tarball = tarfile.open(in_temp_dir('foo.tar.gz'), 'w') - tarball.add(in_temp_dir('a'), 'a') - tarball.add(in_temp_dir('b'), 'b') + + self.touch(in_temp_dir("a")) + self.touch(in_temp_dir("b")) + tarball = tarfile.open(in_temp_dir("foo.tar.gz"), "w") + tarball.add(in_temp_dir("a"), "a") + tarball.add(in_temp_dir("b"), "b") tarball.close() - self.assertThat( - in_temp_dir('foo.tar.gz'), TarballContains(['b', 'a'])) + self.assertThat(in_temp_dir("foo.tar.gz"), TarballContains(["b", "a"])) def test_mismatch(self): tempdir = self.mkdtemp() + def in_temp_dir(x): return os.path.join(tempdir, x) - self.touch(in_temp_dir('a')) - self.touch(in_temp_dir('b')) - tarball = tarfile.open(in_temp_dir('foo.tar.gz'), 'w') - tarball.add(in_temp_dir('a'), 'a') - tarball.add(in_temp_dir('b'), 'b') + + self.touch(in_temp_dir("a")) + self.touch(in_temp_dir("b")) + tarball = tarfile.open(in_temp_dir("foo.tar.gz"), "w") + tarball.add(in_temp_dir("a"), "a") + tarball.add(in_temp_dir("b"), "b") tarball.close() - mismatch = TarballContains(['d', 'c']).match(in_temp_dir('foo.tar.gz')) + mismatch = TarballContains(["d", "c"]).match(in_temp_dir("foo.tar.gz")) self.assertEqual( - mismatch.describe(), - Equals(['c', 'd']).match(['a', 'b']).describe()) + mismatch.describe(), Equals(["c", "d"]).match(["a", "b"]).describe() + ) class TestSamePath(TestCase, PathHelpers): - def test_same_string(self): - self.assertThat('foo', SamePath('foo')) + self.assertThat("foo", SamePath("foo")) def test_relative_and_absolute(self): - path = 'foo' + path = "foo" abspath = os.path.abspath(path) self.assertThat(path, SamePath(abspath)) self.assertThat(abspath, SamePath(path)) def test_real_path(self): tempdir = self.mkdtemp() - source = os.path.join(tempdir, 'source') + source = os.path.join(tempdir, "source") self.touch(source) - target = os.path.join(tempdir, 'target') + target = os.path.join(tempdir, "target") try: os.symlink(source, target) except (AttributeError, NotImplementedError): @@ -231,10 +230,9 @@ def test_real_path(self): class TestHasPermissions(TestCase, PathHelpers): - def test_match(self): tempdir = self.mkdtemp() - filename = os.path.join(tempdir, 'filename') + filename = os.path.join(tempdir, "filename") self.touch(filename) permissions = oct(os.stat(filename).st_mode)[-4:] self.assertThat(filename, HasPermissions(permissions)) @@ -242,4 +240,5 @@ def test_match(self): def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_higherorder.py b/testtools/tests/matchers/test_higherorder.py index 9b4fdd91..44a25e59 100644 --- a/testtools/tests/matchers/test_higherorder.py +++ b/testtools/tests/matchers/test_higherorder.py @@ -8,7 +8,7 @@ MatchesStructure, Mismatch, NotEquals, - ) +) from testtools.matchers._higherorder import ( AfterPreprocessing, AllMatch, @@ -20,72 +20,66 @@ MatchesPredicate, MatchesPredicateWithParams, Not, - ) +) from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface class TestAllMatch(TestCase, TestMatchersInterface): - matches_matcher = AllMatch(LessThan(10)) matches_matches = [ [9, 9, 9], (9, 9), iter([9, 9, 9, 9, 9]), - ] + ] matches_mismatches = [ [11, 9, 9], iter([9, 12, 9, 11]), - ] + ] str_examples = [ ("AllMatch(LessThan(12))", AllMatch(LessThan(12))), - ] + ] describe_examples = [ - ('Differences: [\n' - '11 >= 10\n' - '10 >= 10\n' - ']', - [11, 9, 10], - AllMatch(LessThan(10))), - ] + ( + "Differences: [\n" "11 >= 10\n" "10 >= 10\n" "]", + [11, 9, 10], + AllMatch(LessThan(10)), + ), + ] class TestAnyMatch(TestCase, TestMatchersInterface): - - matches_matcher = AnyMatch(Equals('elephant')) + matches_matcher = AnyMatch(Equals("elephant")) matches_matches = [ - ['grass', 'cow', 'steak', 'milk', 'elephant'], - (13, 'elephant'), - ['elephant', 'elephant', 'elephant'], - {'hippo', 'rhino', 'elephant'}, - ] + ["grass", "cow", "steak", "milk", "elephant"], + (13, "elephant"), + ["elephant", "elephant", "elephant"], + {"hippo", "rhino", "elephant"}, + ] matches_mismatches = [ [], - ['grass', 'cow', 'steak', 'milk'], + ["grass", "cow", "steak", "milk"], (13, 12, 10), - ['element', 'hephalump', 'pachyderm'], - {'hippo', 'rhino', 'diplodocus'}, - ] + ["element", "hephalump", "pachyderm"], + {"hippo", "rhino", "diplodocus"}, + ] str_examples = [ - ("AnyMatch(Equals('elephant'))", AnyMatch(Equals('elephant'))), - ] + ("AnyMatch(Equals('elephant'))", AnyMatch(Equals("elephant"))), + ] describe_examples = [ - ('Differences: [\n' - '11 != 7\n' - '9 != 7\n' - '10 != 7\n' - ']', - [11, 9, 10], - AnyMatch(Equals(7))), - ] + ( + "Differences: [\n" "11 != 7\n" "9 != 7\n" "10 != 7\n" "]", + [11, 9, 10], + AnyMatch(Equals(7)), + ), + ] class TestAfterPreprocessing(TestCase, TestMatchersInterface): - def parity(x): return x % 2 @@ -94,30 +88,37 @@ def parity(x): matches_mismatches = [2] str_examples = [ - ("AfterPreprocessing(, Equals(1))", - AfterPreprocessing(parity, Equals(1))), - ] + ( + "AfterPreprocessing(, Equals(1))", + AfterPreprocessing(parity, Equals(1)), + ), + ] describe_examples = [ - ("0 != 1: after on 2", 2, - AfterPreprocessing(parity, Equals(1))), - ("0 != 1", 2, - AfterPreprocessing(parity, Equals(1), annotate=False)), - ] + ( + "0 != 1: after on 2", + 2, + AfterPreprocessing(parity, Equals(1)), + ), + ("0 != 1", 2, AfterPreprocessing(parity, Equals(1), annotate=False)), + ] class TestMatchersAnyInterface(TestCase, TestMatchersInterface): - matches_matcher = MatchesAny(DocTestMatches("1"), DocTestMatches("2")) matches_matches = ["1", "2"] matches_mismatches = ["3"] - str_examples = [( - "MatchesAny(DocTestMatches('1\\n'), DocTestMatches('2\\n'))", - MatchesAny(DocTestMatches("1"), DocTestMatches("2"))), - ] + str_examples = [ + ( + "MatchesAny(DocTestMatches('1\\n'), DocTestMatches('2\\n'))", + MatchesAny(DocTestMatches("1"), DocTestMatches("2")), + ), + ] - describe_examples = [("""Differences: [ + describe_examples = [ + ( + """Differences: [ Expected: 1 Got: @@ -129,79 +130,87 @@ class TestMatchersAnyInterface(TestCase, TestMatchersInterface): 3 ]""", - "3", MatchesAny(DocTestMatches("1"), DocTestMatches("2")))] + "3", + MatchesAny(DocTestMatches("1"), DocTestMatches("2")), + ) + ] class TestMatchesAllInterface(TestCase, TestMatchersInterface): - matches_matcher = MatchesAll(NotEquals(1), NotEquals(2)) matches_matches = [3, 4] matches_mismatches = [1, 2] str_examples = [ - ("MatchesAll(NotEquals(1), NotEquals(2))", - MatchesAll(NotEquals(1), NotEquals(2)))] + ( + "MatchesAll(NotEquals(1), NotEquals(2))", + MatchesAll(NotEquals(1), NotEquals(2)), + ) + ] describe_examples = [ - ("""Differences: [ + ( + """Differences: [ 1 == 1 ]""", - 1, MatchesAll(NotEquals(1), NotEquals(2))), - ("1 == 1", 1, - MatchesAll(NotEquals(2), NotEquals(1), Equals(3), first_only=True)), - ] + 1, + MatchesAll(NotEquals(1), NotEquals(2)), + ), + ( + "1 == 1", + 1, + MatchesAll(NotEquals(2), NotEquals(1), Equals(3), first_only=True), + ), + ] class TestAnnotate(TestCase, TestMatchersInterface): - matches_matcher = Annotate("foo", Equals(1)) matches_matches = [1] matches_mismatches = [2] - str_examples = [ - ("Annotate('foo', Equals(1))", Annotate("foo", Equals(1)))] + str_examples = [("Annotate('foo', Equals(1))", Annotate("foo", Equals(1)))] - describe_examples = [("2 != 1: foo", 2, Annotate('foo', Equals(1)))] + describe_examples = [("2 != 1: foo", 2, Annotate("foo", Equals(1)))] def test_if_message_no_message(self): # Annotate.if_message returns the given matcher if there is no # message. matcher = Equals(1) - not_annotated = Annotate.if_message('', matcher) + not_annotated = Annotate.if_message("", matcher) self.assertIs(matcher, not_annotated) def test_if_message_given_message(self): # Annotate.if_message returns an annotated version of the matcher if a # message is provided. matcher = Equals(1) - expected = Annotate('foo', matcher) - annotated = Annotate.if_message('foo', matcher) + expected = Annotate("foo", matcher) + annotated = Annotate.if_message("foo", matcher) self.assertThat( - annotated, - MatchesStructure.fromExample(expected, 'annotation', 'matcher')) + annotated, MatchesStructure.fromExample(expected, "annotation", "matcher") + ) class TestAnnotatedMismatch(TestCase): - run_tests_with = FullStackRunTest def test_forwards_details(self): - x = Mismatch('description', {'foo': 'bar'}) + x = Mismatch("description", {"foo": "bar"}) annotated = AnnotatedMismatch("annotation", x) self.assertEqual(x.get_details(), annotated.get_details()) class TestNotInterface(TestCase, TestMatchersInterface): - matches_matcher = Not(Equals(1)) matches_matches = [2] matches_mismatches = [1] str_examples = [ ("Not(Equals(1))", Not(Equals(1))), - ("Not(Equals('1'))", Not(Equals('1')))] + ("Not(Equals('1'))", Not(Equals("1"))), + ] - describe_examples = [('1 matches Equals(1)', 1, Not(Equals(1)))] + describe_examples = [("1 matches Equals(1)", 1, Not(Equals(1)))] def is_even(x): @@ -209,19 +218,20 @@ def is_even(x): class TestMatchesPredicate(TestCase, TestMatchersInterface): - matches_matcher = MatchesPredicate(is_even, "%s is not even") matches_matches = [2, 4, 6, 8] matches_mismatches = [3, 5, 7, 9] str_examples = [ - ("MatchesPredicate({!r}, {!r})".format(is_even, "%s is not even"), - MatchesPredicate(is_even, "%s is not even")), - ] + ( + "MatchesPredicate({!r}, {!r})".format(is_even, "%s is not even"), + MatchesPredicate(is_even, "%s is not even"), + ), + ] describe_examples = [ - ('7 is not even', 7, MatchesPredicate(is_even, "%s is not even")), - ] + ("7 is not even", 7, MatchesPredicate(is_even, "%s is not even")), + ] def between(x, low, high): @@ -229,27 +239,37 @@ def between(x, low, high): class TestMatchesPredicateWithParams(TestCase, TestMatchersInterface): - matches_matcher = MatchesPredicateWithParams( - between, "{0} is not between {1} and {2}")(1, 9) + between, "{0} is not between {1} and {2}" + )(1, 9) matches_matches = [2, 4, 6, 8] matches_mismatches = [0, 1, 9, 10] str_examples = [ - ("MatchesPredicateWithParams({!r}, {!r})({})".format( - between, "{0} is not between {1} and {2}", "1, 2"), - MatchesPredicateWithParams( - between, "{0} is not between {1} and {2}")(1, 2)), - ("Between(1, 2)", MatchesPredicateWithParams( - between, "{0} is not between {1} and {2}", "Between")(1, 2)), - ] + ( + "MatchesPredicateWithParams({!r}, {!r})({})".format( + between, "{0} is not between {1} and {2}", "1, 2" + ), + MatchesPredicateWithParams(between, "{0} is not between {1} and {2}")(1, 2), + ), + ( + "Between(1, 2)", + MatchesPredicateWithParams( + between, "{0} is not between {1} and {2}", "Between" + )(1, 2), + ), + ] describe_examples = [ - ('1 is not between 2 and 3', 1, MatchesPredicateWithParams( - between, "{0} is not between {1} and {2}")(2, 3)), - ] + ( + "1 is not between 2 and 3", + 1, + MatchesPredicateWithParams(between, "{0} is not between {1} and {2}")(2, 3), + ), + ] def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_impl.py b/testtools/tests/matchers/test_impl.py index c2558bf5..942335c7 100644 --- a/testtools/tests/matchers/test_impl.py +++ b/testtools/tests/matchers/test_impl.py @@ -3,22 +3,22 @@ """Tests for matchers.""" from testtools import ( - Matcher, # check that Matcher is exposed at the top level for docs. + Matcher, # check that Matcher is exposed at the top level for docs. TestCase, - ) +) from testtools.compat import ( text_repr, - ) +) from testtools.matchers import ( Equals, MatchesException, Raises, - ) +) from testtools.matchers._impl import ( Mismatch, MismatchDecorator, MismatchError, - ) +) from testtools.tests.helpers import FullStackRunTest # Silence pyflakes. @@ -26,28 +26,28 @@ class TestMismatch(TestCase): - run_tests_with = FullStackRunTest def test_constructor_arguments(self): - mismatch = Mismatch("some description", {'detail': "things"}) + mismatch = Mismatch("some description", {"detail": "things"}) self.assertEqual("some description", mismatch.describe()) - self.assertEqual({'detail': "things"}, mismatch.get_details()) + self.assertEqual({"detail": "things"}, mismatch.get_details()) def test_constructor_no_arguments(self): mismatch = Mismatch() - self.assertThat(mismatch.describe, - Raises(MatchesException(NotImplementedError))) + self.assertThat( + mismatch.describe, Raises(MatchesException(NotImplementedError)) + ) self.assertEqual({}, mismatch.get_details()) class TestMismatchError(TestCase): - def test_is_assertion_error(self): # MismatchError is an AssertionError, so that most of the time, it # looks like a test failure, rather than an error. def raise_mismatch_error(): raise MismatchError(2, Equals(3), Equals(3).match(2)) + self.assertRaises(AssertionError, raise_mismatch_error) def test_default_description_is_mismatch(self): @@ -56,8 +56,8 @@ def test_default_description_is_mismatch(self): self.assertEqual(mismatch.describe(), str(e)) def test_default_description_unicode(self): - matchee = '\xa7' - matcher = Equals('a') + matchee = "\xa7" + matcher = Equals("a") mismatch = matcher.match(matchee) e = MismatchError(matchee, matcher, mismatch) self.assertEqual(mismatch.describe(), str(e)) @@ -67,56 +67,50 @@ def test_verbose_description(self): matcher = Equals(3) mismatch = matcher.match(2) e = MismatchError(matchee, matcher, mismatch, True) - expected = ( - 'Match failed. Matchee: %r\n' - 'Matcher: %s\n' - 'Difference: %s\n' % ( - matchee, - matcher, - matcher.match(matchee).describe(), - )) + expected = "Match failed. Matchee: %r\n" "Matcher: %s\n" "Difference: %s\n" % ( + matchee, + matcher, + matcher.match(matchee).describe(), + ) self.assertEqual(expected, str(e)) def test_verbose_unicode(self): # When assertThat is given matchees or matchers that contain non-ASCII # unicode strings, we can still provide a meaningful error. - matchee = '\xa7' - matcher = Equals('a') + matchee = "\xa7" + matcher = Equals("a") mismatch = matcher.match(matchee) - expected = ( - 'Match failed. Matchee: %s\n' - 'Matcher: %s\n' - 'Difference: %s\n' % ( - text_repr(matchee), - matcher, - mismatch.describe(), - )) + expected = "Match failed. Matchee: %s\n" "Matcher: %s\n" "Difference: %s\n" % ( + text_repr(matchee), + matcher, + mismatch.describe(), + ) e = MismatchError(matchee, matcher, mismatch, True) self.assertEqual(expected, str(e)) class TestMismatchDecorator(TestCase): - run_tests_with = FullStackRunTest def test_forwards_description(self): - x = Mismatch("description", {'foo': 'bar'}) + x = Mismatch("description", {"foo": "bar"}) decorated = MismatchDecorator(x) self.assertEqual(x.describe(), decorated.describe()) def test_forwards_details(self): - x = Mismatch("description", {'foo': 'bar'}) + x = Mismatch("description", {"foo": "bar"}) decorated = MismatchDecorator(x) self.assertEqual(x.get_details(), decorated.get_details()) def test_repr(self): - x = Mismatch("description", {'foo': 'bar'}) + x = Mismatch("description", {"foo": "bar"}) decorated = MismatchDecorator(x) self.assertEqual( - f'', - repr(decorated)) + f"", repr(decorated) + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/matchers/test_warnings.py b/testtools/tests/matchers/test_warnings.py index 74e65287..3c234532 100644 --- a/testtools/tests/matchers/test_warnings.py +++ b/testtools/tests/matchers/test_warnings.py @@ -10,7 +10,7 @@ MatchesListwise, Contains, HasLength, - ) +) from testtools.matchers._warnings import Warnings, IsDeprecated, WarningMessage from testtools.tests.helpers import FullStackRunTest from testtools.tests.matchers.helpers import TestMatchersInterface @@ -22,11 +22,8 @@ def make_warning(warning_type, message): def make_warning_message(message, category, filename=None, lineno=None, line=None): return warnings.WarningMessage( - message=message, - category=category, - filename=filename, - lineno=lineno, - line=line) + message=message, category=category, filename=filename, lineno=lineno, line=line + ) class TestWarningMessageCategoryTypeInterface(TestCase, TestMatchersInterface): @@ -35,10 +32,11 @@ class TestWarningMessageCategoryTypeInterface(TestCase, TestMatchersInterface): In particular matching the ``category_type``. """ + matches_matcher = WarningMessage(category_type=DeprecationWarning) - warning_foo = make_warning_message('foo', DeprecationWarning) - warning_bar = make_warning_message('bar', SyntaxWarning) - warning_base = make_warning_message('base', Warning) + warning_foo = make_warning_message("foo", DeprecationWarning) + warning_bar = make_warning_message("bar", SyntaxWarning) + warning_base = make_warning_message("base", Warning) matches_matches = [warning_foo] matches_mismatches = [warning_bar, warning_base] @@ -52,10 +50,12 @@ class TestWarningMessageMessageInterface(TestCase, TestMatchersInterface): In particular matching the ``message``. """ - matches_matcher = WarningMessage(category_type=DeprecationWarning, - message=Equals('foo')) - warning_foo = make_warning_message('foo', DeprecationWarning) - warning_bar = make_warning_message('bar', DeprecationWarning) + + matches_matcher = WarningMessage( + category_type=DeprecationWarning, message=Equals("foo") + ) + warning_foo = make_warning_message("foo", DeprecationWarning) + warning_bar = make_warning_message("bar", DeprecationWarning) matches_matches = [warning_foo] matches_mismatches = [warning_bar] @@ -69,10 +69,12 @@ class TestWarningMessageFilenameInterface(TestCase, TestMatchersInterface): In particular matching the ``filename``. """ - matches_matcher = WarningMessage(category_type=DeprecationWarning, - filename=Equals('a')) - warning_foo = make_warning_message('foo', DeprecationWarning, filename='a') - warning_bar = make_warning_message('bar', DeprecationWarning, filename='b') + + matches_matcher = WarningMessage( + category_type=DeprecationWarning, filename=Equals("a") + ) + warning_foo = make_warning_message("foo", DeprecationWarning, filename="a") + warning_bar = make_warning_message("bar", DeprecationWarning, filename="b") matches_matches = [warning_foo] matches_mismatches = [warning_bar] @@ -86,10 +88,12 @@ class TestWarningMessageLineNumberInterface(TestCase, TestMatchersInterface): In particular matching the ``lineno``. """ - matches_matcher = WarningMessage(category_type=DeprecationWarning, - lineno=Equals(42)) - warning_foo = make_warning_message('foo', DeprecationWarning, lineno=42) - warning_bar = make_warning_message('bar', DeprecationWarning, lineno=21) + + matches_matcher = WarningMessage( + category_type=DeprecationWarning, lineno=Equals(42) + ) + warning_foo = make_warning_message("foo", DeprecationWarning, lineno=42) + warning_bar = make_warning_message("bar", DeprecationWarning, lineno=21) matches_matches = [warning_foo] matches_mismatches = [warning_bar] @@ -103,10 +107,10 @@ class TestWarningMessageLineInterface(TestCase, TestMatchersInterface): In particular matching the ``line``. """ - matches_matcher = WarningMessage(category_type=DeprecationWarning, - line=Equals('x')) - warning_foo = make_warning_message('foo', DeprecationWarning, line='x') - warning_bar = make_warning_message('bar', DeprecationWarning, line='y') + + matches_matcher = WarningMessage(category_type=DeprecationWarning, line=Equals("x")) + warning_foo = make_warning_message("foo", DeprecationWarning, line="x") + warning_bar = make_warning_message("bar", DeprecationWarning, line="y") matches_matches = [warning_foo] matches_mismatches = [warning_bar] @@ -120,9 +124,12 @@ class TestWarningsInterface(TestCase, TestMatchersInterface): Specifically without the optional argument. """ + matches_matcher = Warnings() + def old_func(): - warnings.warn('old_func is deprecated', DeprecationWarning, 2) + warnings.warn("old_func is deprecated", DeprecationWarning, 2) + matches_matches = [old_func] matches_mismatches = [lambda: None] @@ -139,17 +146,21 @@ class TestWarningsMatcherInterface(TestCase, TestMatchersInterface): Specifically with the optional matcher argument. """ + matches_matcher = Warnings( - warnings_matcher=MatchesListwise([ - MatchesStructure( - message=AfterPreprocessing( - str, Contains('old_func')))])) + warnings_matcher=MatchesListwise( + [MatchesStructure(message=AfterPreprocessing(str, Contains("old_func")))] + ) + ) + def old_func(): - warnings.warn('old_func is deprecated', DeprecationWarning, 2) + warnings.warn("old_func is deprecated", DeprecationWarning, 2) + def older_func(): - warnings.warn('older_func is deprecated', DeprecationWarning, 2) + warnings.warn("older_func is deprecated", DeprecationWarning, 2) + matches_matches = [old_func] - matches_mismatches = [lambda:None, older_func] + matches_mismatches = [lambda: None, older_func] str_examples = [] describe_examples = [] @@ -162,11 +173,15 @@ class TestWarningsMatcherNoWarningsInterface(TestCase, TestMatchersInterface): Specifically with the optional matcher argument matching that there were no warnings. """ + matches_matcher = Warnings(warnings_matcher=HasLength(0)) + def nowarning_func(): pass + def warning_func(): - warnings.warn('warning_func is deprecated', DeprecationWarning, 2) + warnings.warn("warning_func is deprecated", DeprecationWarning, 2) + matches_matches = [nowarning_func] matches_mismatches = [warning_func] @@ -178,26 +193,31 @@ class TestWarningMessage(TestCase): """ Tests for `testtools.matchers._warnings.WarningMessage`. """ + run_tests_with = FullStackRunTest def test_category(self): def old_func(): - warnings.warn('old_func is deprecated', DeprecationWarning, 2) - self.assertThat(old_func, IsDeprecated(Contains('old_func'))) + warnings.warn("old_func is deprecated", DeprecationWarning, 2) + + self.assertThat(old_func, IsDeprecated(Contains("old_func"))) class TestIsDeprecated(TestCase): """ Tests for `testtools.matchers._warnings.IsDeprecated`. """ + run_tests_with = FullStackRunTest def test_warning(self): def old_func(): - warnings.warn('old_func is deprecated', DeprecationWarning, 2) - self.assertThat(old_func, IsDeprecated(Contains('old_func'))) + warnings.warn("old_func is deprecated", DeprecationWarning, 2) + + self.assertThat(old_func, IsDeprecated(Contains("old_func"))) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/samplecases.py b/testtools/tests/samplecases.py index 07447cbe..7a741e5e 100644 --- a/testtools/tests/samplecases.py +++ b/testtools/tests/samplecases.py @@ -17,9 +17,15 @@ ) -def make_test_case(test_method_name, set_up=None, test_body=None, - tear_down=None, cleanups=(), pre_set_up=None, - post_tear_down=None): +def make_test_case( + test_method_name, + set_up=None, + test_body=None, + tear_down=None, + cleanups=(), + pre_set_up=None, + post_tear_down=None, +): """Make a test case with the given behaviors. All callables are unary callables that receive this test as their argument. @@ -41,16 +47,29 @@ def make_test_case(test_method_name, set_up=None, test_body=None, pre_set_up = pre_set_up if pre_set_up else _do_nothing post_tear_down = post_tear_down if post_tear_down else _do_nothing return _ConstructedTest( - test_method_name, set_up, test_body, tear_down, cleanups, - pre_set_up, post_tear_down, + test_method_name, + set_up, + test_body, + tear_down, + cleanups, + pre_set_up, + post_tear_down, ) class _ConstructedTest(TestCase): """A test case defined by arguments, rather than overrides.""" - def __init__(self, test_method_name, set_up, test_body, tear_down, - cleanups, pre_set_up, post_tear_down): + def __init__( + self, + test_method_name, + set_up, + test_body, + tear_down, + cleanups, + pre_set_up, + post_tear_down, + ): """Construct a test case. See ``make_test_case`` for full documentation. @@ -88,32 +107,32 @@ def _do_nothing(case): def _error(case): - 1/0 # arbitrary non-failure exception + 1 / 0 # arbitrary non-failure exception def _failure(case): - case.fail('arbitrary failure') + case.fail("arbitrary failure") def _skip(case): - case.skipTest('arbitrary skip message') + case.skipTest("arbitrary skip message") def _expected_failure(case): - case.expectFailure('arbitrary expected failure', _failure, case) + case.expectFailure("arbitrary expected failure", _failure, case) def _unexpected_success(case): - case.expectFailure('arbitrary unexpected success', _success, case) + case.expectFailure("arbitrary unexpected success", _success, case) behaviors = [ - ('success', _success), - ('fail', _failure), - ('error', _error), - ('skip', _skip), - ('xfail', _expected_failure), - ('uxsuccess', _unexpected_success), + ("success", _success), + ("fail", _failure), + ("error", _error), + ("skip", _skip), + ("xfail", _expected_failure), + ("uxsuccess", _unexpected_success), ] @@ -133,24 +152,23 @@ def _make_behavior_scenarios(stage): Ordering is not consistent. """ return ( - (f'{stage}={behavior}', - {f'{stage}_behavior': function}) + (f"{stage}={behavior}", {f"{stage}_behavior": function}) for (behavior, function) in behaviors ) def make_case_for_behavior_scenario(case): """Given a test with a behavior scenario installed, make a TestCase.""" - cleanup_behavior = getattr(case, 'cleanup_behavior', None) + cleanup_behavior = getattr(case, "cleanup_behavior", None) cleanups = [cleanup_behavior] if cleanup_behavior else [] return make_test_case( case.getUniqueString(), - set_up=getattr(case, 'set_up_behavior', _do_nothing), - test_body=getattr(case, 'body_behavior', _do_nothing), - tear_down=getattr(case, 'tear_down_behavior', _do_nothing), + set_up=getattr(case, "set_up_behavior", _do_nothing), + test_body=getattr(case, "body_behavior", _do_nothing), + tear_down=getattr(case, "tear_down_behavior", _do_nothing), cleanups=cleanups, - pre_set_up=getattr(case, 'pre_set_up_behavior', _do_nothing), - post_tear_down=getattr(case, 'post_tear_down_behavior', _do_nothing), + pre_set_up=getattr(case, "pre_set_up_behavior", _do_nothing), + post_tear_down=getattr(case, "post_tear_down_behavior", _do_nothing), ) @@ -179,13 +197,15 @@ def tearDown(self): @classmethod def make_scenario(cls): - case = cls('test_success') + case = cls("test_success") return { - 'case': case, - 'expected_first_result': _test_error_traceback( - case, Contains('TestCase.tearDown was not called')), - 'expected_second_result': _test_error_traceback( - case, Contains('TestCase.setUp was not called')), + "case": case, + "expected_first_result": _test_error_traceback( + case, Contains("TestCase.tearDown was not called") + ), + "expected_second_result": _test_error_traceback( + case, Contains("TestCase.setUp was not called") + ), } @@ -194,20 +214,26 @@ def _test_error_traceback(case, traceback_matcher): ``traceback_matcher`` is applied to the text of the traceback. """ - return MatchesListwise([ - Equals(('startTest', case)), - MatchesListwise([ - Equals('addError'), - Equals(case), - MatchesDict({ - 'traceback': AfterPreprocessing( - lambda x: x.as_text(), - traceback_matcher, - ) - }) - ]), - Equals(('stopTest', case)), - ]) + return MatchesListwise( + [ + Equals(("startTest", case)), + MatchesListwise( + [ + Equals("addError"), + Equals(case), + MatchesDict( + { + "traceback": AfterPreprocessing( + lambda x: x.as_text(), + traceback_matcher, + ) + } + ), + ] + ), + Equals(("stopTest", case)), + ] + ) """ @@ -215,14 +241,17 @@ def _test_error_traceback(case, traceback_matcher): case that we have. """ deterministic_sample_cases_scenarios = multiply_scenarios( - _make_behavior_scenarios('set_up'), - _make_behavior_scenarios('body'), - _make_behavior_scenarios('tear_down'), - _make_behavior_scenarios('cleanup'), + _make_behavior_scenarios("set_up"), + _make_behavior_scenarios("body"), + _make_behavior_scenarios("tear_down"), + _make_behavior_scenarios("cleanup"), ) + [ - ('tear_down_fails_after_upcall', { - 'post_tear_down_behavior': _error, - }), + ( + "tear_down_fails_after_upcall", + { + "post_tear_down_behavior": _error, + }, + ), ] @@ -231,5 +260,5 @@ def _test_error_traceback(case, traceback_matcher): sample case that we have. """ nondeterministic_sample_cases_scenarios = [ - ('setup-fails-global-state', _SetUpFailsOnGlobalState.make_scenario()), + ("setup-fails-global-state", _SetUpFailsOnGlobalState.make_scenario()), ] diff --git a/testtools/tests/test_assert_that.py b/testtools/tests/test_assert_that.py index d3b6a118..8dae8618 100644 --- a/testtools/tests/test_assert_that.py +++ b/testtools/tests/test_assert_that.py @@ -2,18 +2,18 @@ from testtools import ( TestCase, - ) +) from testtools.assertions import ( assert_that, - ) +) from testtools.content import ( TracebackContent, - ) +) from testtools.matchers import ( Annotate, DocTestMatches, Equals, - ) +) class AssertThatTests: @@ -24,70 +24,76 @@ def assert_that_callable(self, *args, **kwargs): def assertFails(self, message, function, *args, **kwargs): """Assert that function raises a failure with the given message.""" - failure = self.assertRaises( - self.failureException, function, *args, **kwargs) + failure = self.assertRaises(self.failureException, function, *args, **kwargs) self.assert_that_callable(failure, DocTestMatches(message, ELLIPSIS)) def test_assertThat_matches_clean(self): class Matcher: def match(self, foo): return None + self.assert_that_callable("foo", Matcher()) def test_assertThat_mismatch_raises_description(self): calls = [] + class Mismatch: def __init__(self, thing): self.thing = thing + def describe(self): - calls.append(('describe_diff', self.thing)) + calls.append(("describe_diff", self.thing)) return "object is not a thing" + def get_details(self): return {} + class Matcher: def match(self, thing): - calls.append(('match', thing)) + calls.append(("match", thing)) return Mismatch(thing) + def __str__(self): - calls.append(('__str__',)) + calls.append(("__str__",)) return "a description" + class Test(type(self)): def test(self): self.assert_that_callable("foo", Matcher()) + result = Test("test").run() - self.assertEqual([ - ('match', "foo"), - ('describe_diff', "foo"), - ], calls) + self.assertEqual( + [ + ("match", "foo"), + ("describe_diff", "foo"), + ], + calls, + ) self.assertFalse(result.wasSuccessful()) def test_assertThat_output(self): - matchee = 'foo' - matcher = Equals('bar') + matchee = "foo" + matcher = Equals("bar") expected = matcher.match(matchee).describe() self.assertFails(expected, self.assert_that_callable, matchee, matcher) def test_assertThat_message_is_annotated(self): - matchee = 'foo' - matcher = Equals('bar') - expected = Annotate('woo', matcher).match(matchee).describe() - self.assertFails(expected, - self.assert_that_callable, matchee, matcher, 'woo') + matchee = "foo" + matcher = Equals("bar") + expected = Annotate("woo", matcher).match(matchee).describe() + self.assertFails(expected, self.assert_that_callable, matchee, matcher, "woo") def test_assertThat_verbose_output(self): - matchee = 'foo' - matcher = Equals('bar') - expected = ( - 'Match failed. Matchee: %r\n' - 'Matcher: %s\n' - 'Difference: %s\n' % ( - matchee, - matcher, - matcher.match(matchee).describe(), - )) + matchee = "foo" + matcher = Equals("bar") + expected = "Match failed. Matchee: %r\n" "Matcher: %s\n" "Difference: %s\n" % ( + matchee, + matcher, + matcher.match(matchee).describe(), + ) self.assertFails( - expected, - self.assert_that_callable, matchee, matcher, verbose=True) + expected, self.assert_that_callable, matchee, matcher, verbose=True + ) def get_error_string(self, e): """Get the string showing how 'e' would be formatted in test output. @@ -103,47 +109,52 @@ def get_error_string(self, e): """ error = TracebackContent((e.__class__, e, None), self).as_text() # We aren't at all interested in the traceback. - if error.startswith('Traceback (most recent call last):\n'): + if error.startswith("Traceback (most recent call last):\n"): lines = error.splitlines(True)[1:] for i, line in enumerate(lines): - if not line.startswith(' '): + if not line.startswith(" "): break - error = ''.join(lines[i:]) + error = "".join(lines[i:]) # We aren't interested in how the exception type is formatted. - exc_class, error = error.split(': ', 1) + exc_class, error = error.split(": ", 1) return error def test_assertThat_verbose_unicode(self): # When assertThat is given matchees or matchers that contain non-ASCII # unicode strings, we can still provide a meaningful error. - matchee = '\xa7' - matcher = Equals('a') + matchee = "\xa7" + matcher = Equals("a") expected = ( - 'Match failed. Matchee: %s\n' - 'Matcher: %s\n' - 'Difference: %s\n\n' % ( + "Match failed. Matchee: %s\n" + "Matcher: %s\n" + "Difference: %s\n\n" + % ( repr(matchee).replace("\\xa7", matchee), matcher, matcher.match(matchee).describe(), - )) + ) + ) e = self.assertRaises( - self.failureException, self.assert_that_callable, matchee, matcher, - verbose=True) + self.failureException, + self.assert_that_callable, + matchee, + matcher, + verbose=True, + ) self.assertEqual(expected, self.get_error_string(e)) class TestAssertThatFunction(AssertThatTests, TestCase): - def assert_that_callable(self, *args, **kwargs): return assert_that(*args, **kwargs) class TestAssertThatMethod(AssertThatTests, TestCase): - def assert_that_callable(self, *args, **kwargs): return self.assertThat(*args, **kwargs) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_compat.py b/testtools/tests/test_compat.py index 1246ead1..fc121229 100644 --- a/testtools/tests/test_compat.py +++ b/testtools/tests/test_compat.py @@ -14,11 +14,11 @@ reraise, text_repr, unicode_output_stream, - ) +) from testtools.matchers import ( Is, Not, - ) +) class _FakeOutputStream: @@ -85,14 +85,14 @@ def test_io_bytesio(self): bytes_io = io.BytesIO() self.assertThat(bytes_io, Not(Is(unicode_output_stream(bytes_io)))) # Will error if s was not wrapped properly. - unicode_output_stream(bytes_io).write('foo') + unicode_output_stream(bytes_io).write("foo") def test_io_textwrapper(self): # textwrapper is unicode, should be returned as itself. text_io = io.TextIOWrapper(io.BytesIO()) self.assertThat(unicode_output_stream(text_io), Is(text_io)) # To be sure... - unicode_output_stream(text_io).write('foo') + unicode_output_stream(text_io).write("foo") class TestTextRepr(testtools.TestCase): @@ -108,22 +108,21 @@ class TestTextRepr(testtools.TestCase): ("\r", "'\\r'", "'''\\\n\\r'''"), # Quotes and backslash should match normal repr behaviour ('"', "'\"'", "'''\\\n\"'''"), - ("'", "\"'\"", "'''\\\n\\''''"), + ("'", '"\'"', "'''\\\n\\''''"), ("\\", "'\\\\'", "'''\\\n\\\\'''"), # DEL is also unprintable and should be escaped ("\x7F", "'\\x7f'", "'''\\\n\\x7f'''"), - # Character combinations that need double checking ("\r\n", "'\\r\\n'", "'''\\\n\\r\n'''"), ("\"'", "'\"\\''", "'''\\\n\"\\''''"), ("'\"", "'\\'\"'", "'''\\\n'\"'''"), ("\\n", "'\\\\n'", "'''\\\n\\\\n'''"), ("\\\n", "'\\\\\\n'", "'''\\\n\\\\\n'''"), - ("\\' ", "\"\\\\' \"", "'''\\\n\\\\' '''"), - ("\\'\n", "\"\\\\'\\n\"", "'''\\\n\\\\'\n'''"), + ("\\' ", '"\\\\\' "', "'''\\\n\\\\' '''"), + ("\\'\n", '"\\\\\'\\n"', "'''\\\n\\\\'\n'''"), ("\\'\"", "'\\\\\\'\"'", "'''\\\n\\\\'\"'''"), ("\\'''", "\"\\\\'''\"", "'''\\\n\\\\\\'\\'\\''''"), - ) + ) # Bytes with the high bit set should always be escaped bytes_examples = ( @@ -132,7 +131,7 @@ class TestTextRepr(testtools.TestCase): (_b("\xC0"), "'\\xc0'", "'''\\\n\\xc0'''"), (_b("\xFF"), "'\\xff'", "'''\\\n\\xff'''"), (_b("\xC2\xA7"), "'\\xc2\\xa7'", "'''\\\n\\xc2\\xa7'''"), - ) + ) # Unicode doesn't escape printable characters as per the Python 3 model unicode_examples = ( @@ -152,7 +151,7 @@ class TestTextRepr(testtools.TestCase): ("\uD800", "'\\ud800'", "'''\\\n\\ud800'''"), ("\uDFFF", "'\\udfff'", "'''\\\n\\udfff'''"), # Unprintable general categories not fully tested: Cc, Cf, Co, Cn, Zs - ) + ) b_prefix = repr(_b(""))[:-2] u_prefix = repr("")[:-2] @@ -220,7 +219,6 @@ def test_unicode_examples_multiline(self): self.assertEqual(ast.literal_eval(actual), u) - class TestReraise(testtools.TestCase): """Tests for trivial reraise wrapper needed for Python 2/3 changes""" @@ -237,8 +235,9 @@ def test_exc_info(self): self.assertIs(_exc_info[0], _new_exc_info[0]) self.assertIs(_exc_info[1], _new_exc_info[1]) expected_tb = traceback.extract_tb(_exc_info[2]) - self.assertEqual(expected_tb, - traceback.extract_tb(_new_exc_info[2])[-len(expected_tb):]) + self.assertEqual( + expected_tb, traceback.extract_tb(_new_exc_info[2])[-len(expected_tb) :] + ) def test_custom_exception_no_args(self): """Reraising does not require args attribute to contain params""" @@ -259,4 +258,5 @@ def __init__(self, value): def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_content.py b/testtools/tests/test_content.py index 2b8937d7..2829c5fd 100644 --- a/testtools/tests/test_content.py +++ b/testtools/tests/test_content.py @@ -8,7 +8,7 @@ from testtools import TestCase from testtools.compat import ( _b, - ) +) from testtools.content import ( attach_file, Content, @@ -20,17 +20,17 @@ StacktraceContent, TracebackContent, text_content, - ) +) from testtools.content_type import ( ContentType, UTF8_TEXT, - ) +) from testtools.matchers import ( Equals, MatchesException, Raises, raises, - ) +) from testtools.tests.helpers import an_exc_info @@ -38,14 +38,14 @@ class TestContent(TestCase): - def test___init___None_errors(self): self.assertThat(lambda: Content(None, None), raises_value_error) self.assertThat( - lambda: Content(None, lambda: ["traceback"]), raises_value_error) + lambda: Content(None, lambda: ["traceback"]), raises_value_error + ) self.assertThat( - lambda: Content(ContentType("text", "traceback"), None), - raises_value_error) + lambda: Content(ContentType("text", "traceback"), None), raises_value_error + ) def test___init___sets_ivars(self): content_type = ContentType("foo", "bar") @@ -55,10 +55,13 @@ def test___init___sets_ivars(self): def test___eq__(self): content_type = ContentType("foo", "bar") + def one_chunk(): return [_b("bytes")] + def two_chunk(): return [_b("by"), _b("tes")] + content1 = Content(content_type, one_chunk) content2 = Content(content_type, one_chunk) content3 = Content(content_type, two_chunk) @@ -70,8 +73,10 @@ def two_chunk(): self.assertNotEqual(content1, content5) def test___repr__(self): - content = Content(ContentType("application", "octet-stream"), - lambda: [_b("\x00bin"), _b("ary\xff")]) + content = Content( + ContentType("application", "octet-stream"), + lambda: [_b("\x00bin"), _b("ary\xff")], + ) self.assertIn("\\x00binary\\xff", repr(content)) def test_iter_text_not_text_errors(self): @@ -81,8 +86,7 @@ def test_iter_text_not_text_errors(self): def test_iter_text_decodes(self): content_type = ContentType("text", "strange", {"charset": "utf8"}) - content = Content( - content_type, lambda: ["bytes\xea".encode()]) + content = Content(content_type, lambda: ["bytes\xea".encode()]) self.assertEqual(["bytes\xea"], list(content.iter_text())) def test_iter_text_default_charset_iso_8859_1(self): @@ -94,67 +98,65 @@ def test_iter_text_default_charset_iso_8859_1(self): def test_as_text(self): content_type = ContentType("text", "strange", {"charset": "utf8"}) - content = Content( - content_type, lambda: ["bytes\xea".encode()]) + content = Content(content_type, lambda: ["bytes\xea".encode()]) self.assertEqual("bytes\xea", content.as_text()) def test_from_file(self): fd, path = tempfile.mkstemp() self.addCleanup(os.remove, path) - os.write(fd, _b('some data')) + os.write(fd, _b("some data")) os.close(fd) content = content_from_file(path, UTF8_TEXT, chunk_size=2) self.assertThat( list(content.iter_bytes()), - Equals([_b('so'), _b('me'), _b(' d'), _b('at'), _b('a')])) + Equals([_b("so"), _b("me"), _b(" d"), _b("at"), _b("a")]), + ) def test_from_nonexistent_file(self): directory = tempfile.mkdtemp() - nonexistent = os.path.join(directory, 'nonexistent-file') + nonexistent = os.path.join(directory, "nonexistent-file") content = content_from_file(nonexistent) self.assertThat(content.iter_bytes, raises(IOError)) def test_from_file_default_type(self): - content = content_from_file('/nonexistent/path') + content = content_from_file("/nonexistent/path") self.assertThat(content.content_type, Equals(UTF8_TEXT)) def test_from_file_eager_loading(self): fd, path = tempfile.mkstemp() - os.write(fd, _b('some data')) + os.write(fd, _b("some data")) os.close(fd) content = content_from_file(path, UTF8_TEXT, buffer_now=True) os.remove(path) - self.assertThat( - ''.join(content.iter_text()), Equals('some data')) + self.assertThat("".join(content.iter_text()), Equals("some data")) def test_from_file_with_simple_seek(self): f = tempfile.NamedTemporaryFile() - f.write(_b('some data')) + f.write(_b("some data")) f.flush() self.addCleanup(f.close) - content = content_from_file( - f.name, UTF8_TEXT, chunk_size=50, seek_offset=5) - self.assertThat( - list(content.iter_bytes()), Equals([_b('data')])) + content = content_from_file(f.name, UTF8_TEXT, chunk_size=50, seek_offset=5) + self.assertThat(list(content.iter_bytes()), Equals([_b("data")])) def test_from_file_with_whence_seek(self): f = tempfile.NamedTemporaryFile() - f.write(_b('some data')) + f.write(_b("some data")) f.flush() self.addCleanup(f.close) content = content_from_file( - f.name, UTF8_TEXT, chunk_size=50, seek_offset=-4, seek_whence=2) - self.assertThat( - list(content.iter_bytes()), Equals([_b('data')])) + f.name, UTF8_TEXT, chunk_size=50, seek_offset=-4, seek_whence=2 + ) + self.assertThat(list(content.iter_bytes()), Equals([_b("data")])) def test_from_stream(self): - data = io.StringIO('some data') + data = io.StringIO("some data") content = content_from_stream(data, UTF8_TEXT, chunk_size=2) self.assertThat( - list(content.iter_bytes()), Equals(['so', 'me', ' d', 'at', 'a'])) + list(content.iter_bytes()), Equals(["so", "me", " d", "at", "a"]) + ) def test_from_stream_default_type(self): - data = io.StringIO('some data') + data = io.StringIO("some data") content = content_from_stream(data) self.assertThat(content.content_type, Equals(UTF8_TEXT)) @@ -162,31 +164,28 @@ def test_from_stream_eager_loading(self): fd, path = tempfile.mkstemp() self.addCleanup(os.remove, path) self.addCleanup(os.close, fd) - os.write(fd, _b('some data')) - stream = open(path, 'rb') + os.write(fd, _b("some data")) + stream = open(path, "rb") self.addCleanup(stream.close) content = content_from_stream(stream, UTF8_TEXT, buffer_now=True) - os.write(fd, _b('more data')) - self.assertThat( - ''.join(content.iter_text()), Equals('some data')) + os.write(fd, _b("more data")) + self.assertThat("".join(content.iter_text()), Equals("some data")) def test_from_stream_with_simple_seek(self): - data = io.BytesIO(_b('some data')) - content = content_from_stream( - data, UTF8_TEXT, chunk_size=50, seek_offset=5) - self.assertThat( - list(content.iter_bytes()), Equals([_b('data')])) + data = io.BytesIO(_b("some data")) + content = content_from_stream(data, UTF8_TEXT, chunk_size=50, seek_offset=5) + self.assertThat(list(content.iter_bytes()), Equals([_b("data")])) def test_from_stream_with_whence_seek(self): - data = io.BytesIO(_b('some data')) + data = io.BytesIO(_b("some data")) content = content_from_stream( - data, UTF8_TEXT, chunk_size=50, seek_offset=-4, seek_whence=2) - self.assertThat( - list(content.iter_bytes()), Equals([_b('data')])) + data, UTF8_TEXT, chunk_size=50, seek_offset=-4, seek_whence=2 + ) + self.assertThat(list(content.iter_bytes()), Equals([_b("data")])) def test_from_text(self): data = "some data" - expected = Content(UTF8_TEXT, lambda: [data.encode('utf8')]) + expected = Content(UTF8_TEXT, lambda: [data.encode("utf8")]) self.assertEqual(expected, text_content(data)) def test_text_content_raises_TypeError_when_passed_bytes(self): @@ -199,25 +198,28 @@ def test_text_content_raises_TypeError_when_passed_non_text(self): self.assertThat( lambda: text_content(value), raises( - TypeError("text_content must be given text, not '%s'." % - type(value).__name__) + TypeError( + "text_content must be given text, not '%s'." + % type(value).__name__ + ) ), ) def test_json_content(self): - data = {'foo': 'bar'} + data = {"foo": "bar"} expected = Content(JSON, lambda: [_b('{"foo": "bar"}')]) self.assertEqual(expected, json_content(data)) class TestStackLinesContent(TestCase): - def _get_stack_line_and_expected_output(self): stack_lines = [ - ('/path/to/file', 42, 'some_function', 'print("Hello World")'), + ("/path/to/file", 42, "some_function", 'print("Hello World")'), ] - expected = ' File "/path/to/file", line 42, in some_function\n' \ - ' print("Hello World")\n' + expected = ( + ' File "/path/to/file", line 42, in some_function\n' + ' print("Hello World")\n' + ) return stack_lines, expected def test_single_stack_line(self): @@ -228,16 +230,16 @@ def test_single_stack_line(self): def test_prefix_content(self): stack_lines, expected = self._get_stack_line_and_expected_output() - prefix = self.getUniqueString() + '\n' + prefix = self.getUniqueString() + "\n" content = StackLinesContent(stack_lines, prefix_content=prefix) actual = content.as_text() - expected = prefix + expected + expected = prefix + expected self.assertEqual(expected, actual) def test_postfix_content(self): stack_lines, expected = self._get_stack_line_and_expected_output() - postfix = '\n' + self.getUniqueString() + postfix = "\n" + self.getUniqueString() content = StackLinesContent(stack_lines, postfix_content=postfix) actual = content.as_text() expected = expected + postfix @@ -247,34 +249,34 @@ def test_postfix_content(self): def test___init___sets_content_type(self): stack_lines, expected = self._get_stack_line_and_expected_output() content = StackLinesContent(stack_lines) - expected_content_type = ContentType("text", "x-traceback", - {"language": "python", "charset": "utf8"}) + expected_content_type = ContentType( + "text", "x-traceback", {"language": "python", "charset": "utf8"} + ) self.assertEqual(expected_content_type, content.content_type) class TestTracebackContent(TestCase): - def test___init___None_errors(self): - self.assertThat( - lambda: TracebackContent(None, None), raises_value_error) + self.assertThat(lambda: TracebackContent(None, None), raises_value_error) def test___init___sets_ivars(self): content = TracebackContent(an_exc_info, self) - content_type = ContentType("text", "x-traceback", - {"language": "python", "charset": "utf8"}) + content_type = ContentType( + "text", "x-traceback", {"language": "python", "charset": "utf8"} + ) self.assertEqual(content_type, content.content_type) result = unittest.TestResult() expected = result._exc_info_to_string(an_exc_info, self) - self.assertEqual(expected, ''.join(list(content.iter_text()))) + self.assertEqual(expected, "".join(list(content.iter_text()))) class TestStacktraceContent(TestCase): - def test___init___sets_ivars(self): content = StacktraceContent() - content_type = ContentType("text", "x-traceback", - {"language": "python", "charset": "utf8"}) + content_type = ContentType( + "text", "x-traceback", {"language": "python", "charset": "utf8"} + ) self.assertEqual(content_type, content.content_type) def test_prefix_is_used(self): @@ -289,11 +291,10 @@ def test_postfix_is_used(self): def test_top_frame_is_skipped_when_no_stack_is_specified(self): actual = StacktraceContent().as_text() - self.assertNotIn('testtools/content.py', actual) + self.assertNotIn("testtools/content.py", actual) class TestAttachFile(TestCase): - def make_file(self, data): # GZ 2011-04-21: This helper could be useful for methods above trying # to use mkstemp, but should handle write failures and @@ -308,12 +309,13 @@ def test_simple(self): class SomeTest(TestCase): def test_foo(self): pass - test = SomeTest('test_foo') - data = 'some data' + + test = SomeTest("test_foo") + data = "some data" path = self.make_file(data) my_content = text_content(data) - attach_file(test, path, name='foo') - self.assertEqual({'foo': my_content}, test.getDetails()) + attach_file(test, path, name="foo") + self.assertEqual({"foo": my_content}, test.getDetails()) def test_optional_name(self): # If no name is provided, attach_file just uses the base name of the @@ -321,8 +323,9 @@ def test_optional_name(self): class SomeTest(TestCase): def test_foo(self): pass - test = SomeTest('test_foo') - path = self.make_file('some data') + + test = SomeTest("test_foo") + path = self.make_file("some data") base_path = os.path.basename(path) attach_file(test, path) self.assertEqual([base_path], list(test.getDetails())) @@ -331,29 +334,32 @@ def test_lazy_read(self): class SomeTest(TestCase): def test_foo(self): pass - test = SomeTest('test_foo') - path = self.make_file('some data') - attach_file(test, path, name='foo', buffer_now=False) - content = test.getDetails()['foo'] - content_file = open(path, 'w') - content_file.write('new data') + + test = SomeTest("test_foo") + path = self.make_file("some data") + attach_file(test, path, name="foo", buffer_now=False) + content = test.getDetails()["foo"] + content_file = open(path, "w") + content_file.write("new data") content_file.close() - self.assertEqual(''.join(content.iter_text()), 'new data') + self.assertEqual("".join(content.iter_text()), "new data") def test_eager_read_by_default(self): class SomeTest(TestCase): def test_foo(self): pass - test = SomeTest('test_foo') - path = self.make_file('some data') - attach_file(test, path, name='foo') - content = test.getDetails()['foo'] - content_file = open(path, 'w') - content_file.write('new data') + + test = SomeTest("test_foo") + path = self.make_file("some data") + attach_file(test, path, name="foo") + content = test.getDetails()["foo"] + content_file = open(path, "w") + content_file.write("new data") content_file.close() - self.assertEqual(''.join(content.iter_text()), 'some data') + self.assertEqual("".join(content.iter_text()), "some data") def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_content_type.py b/testtools/tests/test_content_type.py index 2d34f95e..63c124a0 100644 --- a/testtools/tests/test_content_type.py +++ b/testtools/tests/test_content_type.py @@ -6,17 +6,15 @@ ContentType, JSON, UTF8_TEXT, - ) +) class TestContentType(TestCase): - def test___init___None_errors(self): raises_value_error = Raises(MatchesException(ValueError)) - self.assertThat(lambda:ContentType(None, None), raises_value_error) - self.assertThat(lambda:ContentType(None, "traceback"), - raises_value_error) - self.assertThat(lambda:ContentType("text", None), raises_value_error) + self.assertThat(lambda: ContentType(None, None), raises_value_error) + self.assertThat(lambda: ContentType(None, "traceback"), raises_value_error) + self.assertThat(lambda: ContentType("text", None), raises_value_error) def test___init___sets_ivars(self): content_type = ContentType("foo", "bar") @@ -36,31 +34,29 @@ def test___eq__(self): self.assertFalse(content_type1.__eq__(content_type3)) def test_basic_repr(self): - content_type = ContentType('text', 'plain') - self.assertThat(repr(content_type), Equals('text/plain')) + content_type = ContentType("text", "plain") + self.assertThat(repr(content_type), Equals("text/plain")) def test_extended_repr(self): - content_type = ContentType( - 'text', 'plain', {'foo': 'bar', 'baz': 'qux'}) - self.assertThat( - repr(content_type), Equals('text/plain; baz="qux"; foo="bar"')) + content_type = ContentType("text", "plain", {"foo": "bar", "baz": "qux"}) + self.assertThat(repr(content_type), Equals('text/plain; baz="qux"; foo="bar"')) class TestBuiltinContentTypes(TestCase): - def test_plain_text(self): # The UTF8_TEXT content type represents UTF-8 encoded text/plain. - self.assertThat(UTF8_TEXT.type, Equals('text')) - self.assertThat(UTF8_TEXT.subtype, Equals('plain')) - self.assertThat(UTF8_TEXT.parameters, Equals({'charset': 'utf8'})) + self.assertThat(UTF8_TEXT.type, Equals("text")) + self.assertThat(UTF8_TEXT.subtype, Equals("plain")) + self.assertThat(UTF8_TEXT.parameters, Equals({"charset": "utf8"})) def test_json_content(self): # The JSON content type represents implictly UTF-8 application/json. - self.assertThat(JSON.type, Equals('application')) - self.assertThat(JSON.subtype, Equals('json')) + self.assertThat(JSON.type, Equals("application")) + self.assertThat(JSON.subtype, Equals("json")) self.assertThat(JSON.parameters, Equals({})) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_fixturesupport.py b/testtools/tests/test_fixturesupport.py index ad3d2574..31a1c5c9 100644 --- a/testtools/tests/test_fixturesupport.py +++ b/testtools/tests/test_fixturesupport.py @@ -6,23 +6,22 @@ TestCase, content, content_type, - ) +) from testtools.compat import _b from testtools.helpers import try_import from testtools.matchers import ( Contains, Equals, - ) +) from testtools.testresult.doubles import ( ExtendedTestResult, - ) +) -fixtures = try_import('fixtures') -LoggingFixture = try_import('fixtures.tests.helpers.LoggingFixture') +fixtures = try_import("fixtures") +LoggingFixture = try_import("fixtures.tests.helpers.LoggingFixture") class TestFixtureSupport(TestCase): - def setUp(self): super().setUp() if fixtures is None or LoggingFixture is None: @@ -30,71 +29,88 @@ def setUp(self): def test_useFixture(self): fixture = LoggingFixture() + class SimpleTest(TestCase): def test_foo(self): self.useFixture(fixture) + result = unittest.TestResult() - SimpleTest('test_foo').run(result) + SimpleTest("test_foo").run(result) self.assertTrue(result.wasSuccessful()) - self.assertEqual(['setUp', 'cleanUp'], fixture.calls) + self.assertEqual(["setUp", "cleanUp"], fixture.calls) def test_useFixture_cleanups_raise_caught(self): calls = [] + def raiser(ignored): - calls.append('called') - raise Exception('foo') - fixture = fixtures.FunctionFixture(lambda:None, raiser) + calls.append("called") + raise Exception("foo") + + fixture = fixtures.FunctionFixture(lambda: None, raiser) + class SimpleTest(TestCase): def test_foo(self): self.useFixture(fixture) + result = unittest.TestResult() - SimpleTest('test_foo').run(result) + SimpleTest("test_foo").run(result) self.assertFalse(result.wasSuccessful()) - self.assertEqual(['called'], calls) + self.assertEqual(["called"], calls) def test_useFixture_details_captured(self): class DetailsFixture(fixtures.Fixture): def setUp(self): fixtures.Fixture.setUp(self) - self.addCleanup(delattr, self, 'content') - self.content = [_b('content available until cleanUp')] - self.addDetail('content', - content.Content(content_type.UTF8_TEXT, self.get_content)) + self.addCleanup(delattr, self, "content") + self.content = [_b("content available until cleanUp")] + self.addDetail( + "content", content.Content(content_type.UTF8_TEXT, self.get_content) + ) + def get_content(self): return self.content + fixture = DetailsFixture() + class SimpleTest(TestCase): def test_foo(self): self.useFixture(fixture) # Add a colliding detail (both should show up) - self.addDetail('content', - content.Content(content_type.UTF8_TEXT, lambda:[_b('foo')])) + self.addDetail( + "content", + content.Content(content_type.UTF8_TEXT, lambda: [_b("foo")]), + ) + result = ExtendedTestResult() - SimpleTest('test_foo').run(result) - self.assertEqual('addSuccess', result._events[-2][0]) + SimpleTest("test_foo").run(result) + self.assertEqual("addSuccess", result._events[-2][0]) details = result._events[-2][2] - self.assertEqual(['content', 'content-1'], sorted(details.keys())) - self.assertEqual('foo', details['content'].as_text()) - self.assertEqual('content available until cleanUp', - details['content-1'].as_text()) + self.assertEqual(["content", "content-1"], sorted(details.keys())) + self.assertEqual("foo", details["content"].as_text()) + self.assertEqual( + "content available until cleanUp", details["content-1"].as_text() + ) def test_useFixture_multiple_details_captured(self): class DetailsFixture(fixtures.Fixture): def setUp(self): fixtures.Fixture.setUp(self) - self.addDetail('aaa', content.text_content("foo")) - self.addDetail('bbb', content.text_content("bar")) + self.addDetail("aaa", content.text_content("foo")) + self.addDetail("bbb", content.text_content("bar")) + fixture = DetailsFixture() + class SimpleTest(TestCase): def test_foo(self): self.useFixture(fixture) + result = ExtendedTestResult() - SimpleTest('test_foo').run(result) - self.assertEqual('addSuccess', result._events[-2][0]) + SimpleTest("test_foo").run(result) + self.assertEqual("addSuccess", result._events[-2][0]) details = result._events[-2][2] - self.assertEqual(['aaa', 'bbb'], sorted(details)) - self.assertEqual('foo', details['aaa'].as_text()) - self.assertEqual('bar', details['bbb'].as_text()) + self.assertEqual(["aaa", "bbb"], sorted(details)) + self.assertEqual("foo", details["aaa"].as_text()) + self.assertEqual("bar", details["bbb"].as_text()) def test_useFixture_details_captured_from_setUp(self): # Details added during fixture set-up are gathered even if setUp() @@ -102,18 +118,21 @@ def test_useFixture_details_captured_from_setUp(self): class BrokenFixture(fixtures.Fixture): def setUp(self): fixtures.Fixture.setUp(self) - self.addDetail('content', content.text_content("foobar")) + self.addDetail("content", content.text_content("foobar")) raise Exception() + fixture = BrokenFixture() + class SimpleTest(TestCase): def test_foo(self): self.useFixture(fixture) + result = ExtendedTestResult() - SimpleTest('test_foo').run(result) - self.assertEqual('addError', result._events[-2][0]) + SimpleTest("test_foo").run(result) + self.assertEqual("addError", result._events[-2][0]) details = result._events[-2][2] - self.assertEqual(['content', 'traceback'], sorted(details)) - self.assertEqual('foobar', ''.join(details['content'].iter_text())) + self.assertEqual(["content", "traceback"], sorted(details)) + self.assertEqual("foobar", "".join(details["content"].iter_text())) def test_useFixture_details_captured_from__setUp(self): # Newer Fixtures deprecates setUp() in favour of _setUp(). @@ -122,32 +141,29 @@ def test_useFixture_details_captured_from__setUp(self): class BrokenFixture(fixtures.Fixture): def _setUp(self): fixtures.Fixture._setUp(self) - self.addDetail('broken', content.text_content("foobar")) + self.addDetail("broken", content.text_content("foobar")) raise Exception("_setUp broke") + fixture = BrokenFixture() + class SimpleTest(TestCase): def test_foo(self): - self.addDetail('foo_content', content.text_content("foo ok")) + self.addDetail("foo_content", content.text_content("foo ok")) self.useFixture(fixture) + result = ExtendedTestResult() - SimpleTest('test_foo').run(result) - self.assertEqual('addError', result._events[-2][0]) + SimpleTest("test_foo").run(result) + self.assertEqual("addError", result._events[-2][0]) details = result._events[-2][2] self.assertEqual( - ['broken', 'foo_content', 'traceback', 'traceback-1'], - sorted(details)) - self.expectThat( - ''.join(details['broken'].iter_text()), - Equals('foobar')) + ["broken", "foo_content", "traceback", "traceback-1"], sorted(details) + ) + self.expectThat("".join(details["broken"].iter_text()), Equals("foobar")) + self.expectThat("".join(details["foo_content"].iter_text()), Equals("foo ok")) self.expectThat( - ''.join(details['foo_content'].iter_text()), - Equals('foo ok')) - self.expectThat( - ''.join(details['traceback'].iter_text()), - Contains('_setUp broke')) - self.expectThat( - ''.join(details['traceback-1'].iter_text()), - Contains('foobar')) + "".join(details["traceback"].iter_text()), Contains("_setUp broke") + ) + self.expectThat("".join(details["traceback-1"].iter_text()), Contains("foobar")) def test_useFixture_original_exception_raised_if_gather_details_fails(self): # In bug #1368440 it was reported that when a fixture fails setUp @@ -156,26 +172,31 @@ def test_useFixture_original_exception_raised_if_gather_details_fails(self): class BrokenFixture(fixtures.Fixture): def getDetails(self): raise AttributeError("getDetails broke") + def setUp(self): fixtures.Fixture.setUp(self) raise Exception("setUp broke") + fixture = BrokenFixture() + class SimpleTest(TestCase): def test_foo(self): self.useFixture(fixture) + result = ExtendedTestResult() - SimpleTest('test_foo').run(result) - self.assertEqual('addError', result._events[-2][0]) + SimpleTest("test_foo").run(result) + self.assertEqual("addError", result._events[-2][0]) details = result._events[-2][2] - self.assertEqual(['traceback', 'traceback-1'], sorted(details)) + self.assertEqual(["traceback", "traceback-1"], sorted(details)) self.assertThat( - ''.join(details['traceback'].iter_text()), - Contains('setUp broke')) + "".join(details["traceback"].iter_text()), Contains("setUp broke") + ) self.assertThat( - ''.join(details['traceback-1'].iter_text()), - Contains('getDetails broke')) + "".join(details["traceback-1"].iter_text()), Contains("getDetails broke") + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_helpers.py b/testtools/tests/test_helpers.py index d53956e7..67bed8ab 100644 --- a/testtools/tests/test_helpers.py +++ b/testtools/tests/test_helpers.py @@ -5,11 +5,10 @@ FullStackRunTest, hide_testtools_stack, is_stack_hidden, - ) +) class TestStackHiding(TestCase): - run_tests_with = FullStackRunTest def setUp(self): @@ -27,4 +26,5 @@ def test_is_stack_hidden_consistent_false(self): def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_monkey.py b/testtools/tests/test_monkey.py index 2196be37..cf68abdd 100644 --- a/testtools/tests/test_monkey.py +++ b/testtools/tests/test_monkey.py @@ -9,11 +9,10 @@ class TestObj: - def __init__(self): - self.foo = 'foo value' - self.bar = 'bar value' - self.baz = 'baz value' + self.foo = "foo value" + self.bar = "bar value" + self.baz = "baz value" class MonkeyPatcherTest(TestCase): @@ -40,49 +39,50 @@ def test_empty(self): def test_construct_with_patches(self): # Constructing a 'MonkeyPatcher' with patches adds all of the given # patches to the patch list. - patcher = MonkeyPatcher((self.test_object, 'foo', 'haha'), - (self.test_object, 'bar', 'hehe')) + patcher = MonkeyPatcher( + (self.test_object, "foo", "haha"), (self.test_object, "bar", "hehe") + ) patcher.patch() - self.assertEqual('haha', self.test_object.foo) - self.assertEqual('hehe', self.test_object.bar) + self.assertEqual("haha", self.test_object.foo) + self.assertEqual("hehe", self.test_object.bar) self.assertEqual(self.original_object.baz, self.test_object.baz) def test_patch_existing(self): # Patching an attribute that exists sets it to the value defined in the # patch. - self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') + self.monkey_patcher.add_patch(self.test_object, "foo", "haha") self.monkey_patcher.patch() - self.assertEqual(self.test_object.foo, 'haha') + self.assertEqual(self.test_object.foo, "haha") def test_patch_non_existing(self): # Patching a non-existing attribute sets it to the value defined in # the patch. - self.monkey_patcher.add_patch(self.test_object, 'doesntexist', 'value') + self.monkey_patcher.add_patch(self.test_object, "doesntexist", "value") self.monkey_patcher.patch() - self.assertEqual(self.test_object.doesntexist, 'value') + self.assertEqual(self.test_object.doesntexist, "value") def test_restore_non_existing(self): # Restoring a value that didn't exist before the patch deletes the # value. - self.monkey_patcher.add_patch(self.test_object, 'doesntexist', 'value') + self.monkey_patcher.add_patch(self.test_object, "doesntexist", "value") self.monkey_patcher.patch() self.monkey_patcher.restore() marker = object() - self.assertIs(marker, getattr(self.test_object, 'doesntexist', marker)) + self.assertIs(marker, getattr(self.test_object, "doesntexist", marker)) def test_patch_already_patched(self): # Adding a patch for an object and attribute that already have a patch # overrides the existing patch. - self.monkey_patcher.add_patch(self.test_object, 'foo', 'blah') - self.monkey_patcher.add_patch(self.test_object, 'foo', 'BLAH') + self.monkey_patcher.add_patch(self.test_object, "foo", "blah") + self.monkey_patcher.add_patch(self.test_object, "foo", "BLAH") self.monkey_patcher.patch() - self.assertEqual(self.test_object.foo, 'BLAH') + self.assertEqual(self.test_object.foo, "BLAH") self.monkey_patcher.restore() self.assertEqual(self.test_object.foo, self.original_object.foo) def test_restore_twice_is_a_no_op(self): # Restoring an already-restored monkey patch is a no-op. - self.monkey_patcher.add_patch(self.test_object, 'foo', 'blah') + self.monkey_patcher.add_patch(self.test_object, "foo", "blah") self.monkey_patcher.patch() self.monkey_patcher.restore() self.assertEqual(self.test_object.foo, self.original_object.foo) @@ -96,35 +96,32 @@ def test_run_with_patches_decoration(self): def f(a, b, c=None): log.append((a, b, c)) - return 'foo' + return "foo" result = self.monkey_patcher.run_with_patches(f, 1, 2, c=10) - self.assertEqual('foo', result) + self.assertEqual("foo", result) self.assertEqual([(1, 2, 10)], log) def test_repeated_run_with_patches(self): # We can call the same function with run_with_patches more than # once. All patches apply for each call. def f(): - return (self.test_object.foo, self.test_object.bar, - self.test_object.baz) + return (self.test_object.foo, self.test_object.bar, self.test_object.baz) - self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') + self.monkey_patcher.add_patch(self.test_object, "foo", "haha") result = self.monkey_patcher.run_with_patches(f) self.assertEqual( - ('haha', self.original_object.bar, self.original_object.baz), - result + ("haha", self.original_object.bar, self.original_object.baz), result ) result = self.monkey_patcher.run_with_patches(f) self.assertEqual( - ('haha', self.original_object.bar, self.original_object.baz), - result + ("haha", self.original_object.bar, self.original_object.baz), result ) def test_run_with_patches_restores(self): # run_with_patches restores the original values after the function has # executed. - self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') + self.monkey_patcher.add_patch(self.test_object, "foo", "haha") self.assertEqual(self.original_object.foo, self.test_object.foo) self.monkey_patcher.run_with_patches(lambda: None) self.assertEqual(self.original_object.foo, self.test_object.foo) @@ -133,25 +130,26 @@ def test_run_with_patches_restores_on_exception(self): # run_with_patches restores the original values even when the function # raises an exception. def _(): - self.assertEqual(self.test_object.foo, 'haha') - self.assertEqual(self.test_object.bar, 'blahblah') + self.assertEqual(self.test_object.foo, "haha") + self.assertEqual(self.test_object.bar, "blahblah") raise RuntimeError("Something went wrong!") - self.monkey_patcher.add_patch(self.test_object, 'foo', 'haha') - self.monkey_patcher.add_patch(self.test_object, 'bar', 'blahblah') + self.monkey_patcher.add_patch(self.test_object, "foo", "haha") + self.monkey_patcher.add_patch(self.test_object, "bar", "blahblah") - self.assertThat(lambda:self.monkey_patcher.run_with_patches(_), - Raises(MatchesException(RuntimeError("Something went wrong!")))) + self.assertThat( + lambda: self.monkey_patcher.run_with_patches(_), + Raises(MatchesException(RuntimeError("Something went wrong!"))), + ) self.assertEqual(self.test_object.foo, self.original_object.foo) self.assertEqual(self.test_object.bar, self.original_object.bar) class TestPatchHelper(TestCase): - def test_patch_patches(self): # patch(obj, name, value) sets obj.name to value. test_object = TestObj() - patch(test_object, 'foo', 42) + patch(test_object, "foo", 42) self.assertEqual(42, test_object.foo) def test_patch_returns_cleanup(self): @@ -159,11 +157,12 @@ def test_patch_returns_cleanup(self): # to its original state when run. test_object = TestObj() original = test_object.foo - cleanup = patch(test_object, 'foo', 42) + cleanup = patch(test_object, "foo", 42) cleanup() self.assertEqual(original, test_object.foo) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_run.py b/testtools/tests/test_run.py index cd89d5b4..542ade7c 100644 --- a/testtools/tests/test_run.py +++ b/testtools/tests/test_run.py @@ -19,11 +19,12 @@ MatchesRegex, ) -fixtures = try_import('fixtures') -testresources = try_import('testresources') +fixtures = try_import("fixtures") +testresources = try_import("testresources") if fixtures: + class SampleTestFixture(fixtures.Fixture): """Creates testtools.runexample temporarily.""" @@ -33,7 +34,8 @@ def __init__(self, broken=False): :param broken: If True, the sample file will not be importable. """ if not broken: - init_contents = _b("""\ + init_contents = _b( + """\ from testtools import TestCase class TestFoo(TestCase): @@ -44,28 +46,36 @@ def test_quux(self): def test_suite(): from unittest import TestLoader return TestLoader().loadTestsFromName(__name__) -""") +""" + ) else: init_contents = b"class not in\n" self.package = fixtures.PythonPackage( - 'runexample', [('__init__.py', init_contents)]) + "runexample", [("__init__.py", init_contents)] + ) def setUp(self): super().setUp() self.useFixture(self.package) testtools.__path__.append(self.package.base) self.addCleanup(testtools.__path__.remove, self.package.base) - self.addCleanup(sys.modules.pop, 'testtools.runexample', None) + self.addCleanup(sys.modules.pop, "testtools.runexample", None) if fixtures and testresources: + class SampleResourcedFixture(fixtures.Fixture): """Creates a test suite that uses testresources.""" def __init__(self): super().__init__() self.package = fixtures.PythonPackage( - 'resourceexample', [('__init__.py', _b(""" + "resourceexample", + [ + ( + "__init__.py", + _b( + """ from fixtures import Fixture from testresources import ( FixtureResource, @@ -96,7 +106,11 @@ def test_quux(self): def test_suite(): from unittest import TestLoader return OptimisingTestSuite(TestLoader().loadTestsFromName(__name__)) -"""))]) +""" + ), + ) + ], + ) def setUp(self): super().setUp() @@ -106,13 +120,19 @@ def setUp(self): if fixtures: + class SampleLoadTestsPackage(fixtures.Fixture): """Creates a test suite package using load_tests.""" def __init__(self): super().__init__() self.package = fixtures.PythonPackage( - 'discoverexample', [('__init__.py', _b(""" + "discoverexample", + [ + ( + "__init__.py", + _b( + """ from testtools import TestCase, clone_test_with_new_id class TestExample(TestCase): @@ -122,7 +142,11 @@ def test_foo(self): def load_tests(loader, tests, pattern): tests.addTest(clone_test_with_new_id(tests._tests[1]._tests[0], "fred")) return tests -"""))]) +""" + ), + ) + ], + ) def setUp(self): super().setUp() @@ -131,7 +155,6 @@ def setUp(self): class TestRun(TestCase): - def setUp(self): super().setUp() if fixtures is None: @@ -140,53 +163,80 @@ def setUp(self): def test_run_custom_list(self): self.useFixture(SampleTestFixture()) tests = [] + class CaptureList(run.TestToolsTestRunner): def list(self, test): - tests.append({case.id() for case - in testtools.testsuite.iterate_tests(test)}) + tests.append( + {case.id() for case in testtools.testsuite.iterate_tests(test)} + ) + out = io.StringIO() try: run.TestProgram( - argv=['prog', '-l', 'testtools.runexample.test_suite'], - stdout=out, testRunner=CaptureList) + argv=["prog", "-l", "testtools.runexample.test_suite"], + stdout=out, + testRunner=CaptureList, + ) except SystemExit: exc_info = sys.exc_info() raise AssertionError("-l tried to exit. %r" % exc_info[1]) - self.assertEqual([{'testtools.runexample.TestFoo.test_bar', - 'testtools.runexample.TestFoo.test_quux'}], tests) + self.assertEqual( + [ + { + "testtools.runexample.TestFoo.test_bar", + "testtools.runexample.TestFoo.test_quux", + } + ], + tests, + ) def test_run_list_with_loader(self): # list() is attempted with a loader first. self.useFixture(SampleTestFixture()) tests = [] + class CaptureList(run.TestToolsTestRunner): def list(self, test, loader=None): - tests.append({case.id() for case - in testtools.testsuite.iterate_tests(test)}) + tests.append( + {case.id() for case in testtools.testsuite.iterate_tests(test)} + ) tests.append(loader) + out = io.StringIO() try: program = run.TestProgram( - argv=['prog', '-l', 'testtools.runexample.test_suite'], - stdout=out, testRunner=CaptureList) + argv=["prog", "-l", "testtools.runexample.test_suite"], + stdout=out, + testRunner=CaptureList, + ) except SystemExit: exc_info = sys.exc_info() raise AssertionError("-l tried to exit. %r" % exc_info[1]) - self.assertEqual([{'testtools.runexample.TestFoo.test_bar', - 'testtools.runexample.TestFoo.test_quux'}, program.testLoader], - tests) + self.assertEqual( + [ + { + "testtools.runexample.TestFoo.test_bar", + "testtools.runexample.TestFoo.test_quux", + }, + program.testLoader, + ], + tests, + ) def test_run_list(self): self.useFixture(SampleTestFixture()) out = io.StringIO() try: - run.main(['prog', '-l', 'testtools.runexample.test_suite'], out) + run.main(["prog", "-l", "testtools.runexample.test_suite"], out) except SystemExit: exc_info = sys.exc_info() raise AssertionError("-l tried to exit. %r" % exc_info[1]) - self.assertEqual("""testtools.runexample.TestFoo.test_bar + self.assertEqual( + """testtools.runexample.TestFoo.test_bar testtools.runexample.TestFoo.test_quux -""", out.getvalue()) +""", + out.getvalue(), + ) def test_run_list_failed_import(self): broken = self.useFixture(SampleTestFixture(broken=True)) @@ -195,9 +245,15 @@ def test_run_list_failed_import(self): unittest.defaultTestLoader._top_level_dir = None exc = self.assertRaises( SystemExit, - run.main, ['prog', 'discover', '-l', broken.package.base, '*.py'], out) + run.main, + ["prog", "discover", "-l", broken.package.base, "*.py"], + out, + ) self.assertEqual(2, exc.args[0]) - self.assertThat(out.getvalue(), DocTestMatches("""\ + self.assertThat( + out.getvalue(), + DocTestMatches( + """\ unittest.loader._FailedTest.runexample Failed to import test module: runexample Traceback (most recent call last): @@ -210,7 +266,10 @@ class not in ...^... SyntaxError: invalid syntax -""", doctest.ELLIPSIS)) +""", + doctest.ELLIPSIS, + ), + ) def test_run_orders_tests(self): self.useFixture(SampleTestFixture()) @@ -219,24 +278,38 @@ def test_run_orders_tests(self): # should get the one that exists and neither the one that doesn't nor # the unmentioned one that does. tempdir = self.useFixture(fixtures.TempDir()) - tempname = tempdir.path + '/tests.list' - f = open(tempname, 'wb') + tempname = tempdir.path + "/tests.list" + f = open(tempname, "wb") try: - f.write(_b(""" + f.write( + _b( + """ testtools.runexample.TestFoo.test_bar testtools.runexample.missingtest -""")) +""" + ) + ) finally: f.close() try: - run.main(['prog', '-l', '--load-list', tempname, - 'testtools.runexample.test_suite'], out) + run.main( + [ + "prog", + "-l", + "--load-list", + tempname, + "testtools.runexample.test_suite", + ], + out, + ) except SystemExit: exc_info = sys.exc_info() - raise AssertionError( - "-l --load-list tried to exit. %r" % exc_info[1]) - self.assertEqual("""testtools.runexample.TestFoo.test_bar -""", out.getvalue()) + raise AssertionError("-l --load-list tried to exit. %r" % exc_info[1]) + self.assertEqual( + """testtools.runexample.TestFoo.test_bar +""", + out.getvalue(), + ) def test_run_load_list(self): self.useFixture(SampleTestFixture()) @@ -245,24 +318,38 @@ def test_run_load_list(self): # should get the one that exists and neither the one that doesn't nor # the unmentioned one that does. tempdir = self.useFixture(fixtures.TempDir()) - tempname = tempdir.path + '/tests.list' - f = open(tempname, 'wb') + tempname = tempdir.path + "/tests.list" + f = open(tempname, "wb") try: - f.write(_b(""" + f.write( + _b( + """ testtools.runexample.TestFoo.test_bar testtools.runexample.missingtest -""")) +""" + ) + ) finally: f.close() try: - run.main(['prog', '-l', '--load-list', tempname, - 'testtools.runexample.test_suite'], out) + run.main( + [ + "prog", + "-l", + "--load-list", + tempname, + "testtools.runexample.test_suite", + ], + out, + ) except SystemExit: exc_info = sys.exc_info() - raise AssertionError( - "-l --load-list tried to exit. %r" % exc_info[1]) - self.assertEqual("""testtools.runexample.TestFoo.test_bar -""", out.getvalue()) + raise AssertionError("-l --load-list tried to exit. %r" % exc_info[1]) + self.assertEqual( + """testtools.runexample.TestFoo.test_bar +""", + out.getvalue(), + ) def test_load_list_preserves_custom_suites(self): if testresources is None: @@ -271,66 +358,84 @@ def test_load_list_preserves_custom_suites(self): # We load two tests, not loading one. Both share a resource, so we # should see just one resource setup occur. tempdir = self.useFixture(fixtures.TempDir()) - tempname = tempdir.path + '/tests.list' - f = open(tempname, 'wb') + tempname = tempdir.path + "/tests.list" + f = open(tempname, "wb") try: - f.write(_b(""" + f.write( + _b( + """ testtools.resourceexample.TestFoo.test_bar testtools.resourceexample.TestFoo.test_foo -""")) +""" + ) + ) finally: f.close() - stdout = self.useFixture(fixtures.StringStream('stdout')) - with fixtures.MonkeyPatch('sys.stdout', stdout.stream): + stdout = self.useFixture(fixtures.StringStream("stdout")) + with fixtures.MonkeyPatch("sys.stdout", stdout.stream): try: - run.main(['prog', '--load-list', tempname, - 'testtools.resourceexample.test_suite'], stdout.stream) + run.main( + [ + "prog", + "--load-list", + tempname, + "testtools.resourceexample.test_suite", + ], + stdout.stream, + ) except SystemExit: # Evil resides in TestProgram. pass - out = stdout.getDetails()['stdout'].as_text() - self.assertEqual(1, out.count('Setting up Printer'), "%r" % out) + out = stdout.getDetails()["stdout"].as_text() + self.assertEqual(1, out.count("Setting up Printer"), "%r" % out) def test_run_failfast(self): - stdout = self.useFixture(fixtures.StringStream('stdout')) + stdout = self.useFixture(fixtures.StringStream("stdout")) class Failing(TestCase): def test_a(self): - self.fail('a') + self.fail("a") + def test_b(self): - self.fail('b') - with fixtures.MonkeyPatch('sys.stdout', stdout.stream): + self.fail("b") + + with fixtures.MonkeyPatch("sys.stdout", stdout.stream): runner = run.TestToolsTestRunner(failfast=True) - runner.run(TestSuite([Failing('test_a'), Failing('test_b')])) - self.assertThat( - stdout.getDetails()['stdout'].as_text(), Contains('Ran 1 test')) + runner.run(TestSuite([Failing("test_a"), Failing("test_b")])) + self.assertThat(stdout.getDetails()["stdout"].as_text(), Contains("Ran 1 test")) def test_run_locals(self): - stdout = self.useFixture(fixtures.StringStream('stdout')) + stdout = self.useFixture(fixtures.StringStream("stdout")) class Failing(TestCase): def test_a(self): a = 1 # noqa: F841 - self.fail('a') + self.fail("a") + runner = run.TestToolsTestRunner(tb_locals=True, stdout=stdout.stream) - runner.run(Failing('test_a')) - self.assertThat( - stdout.getDetails()['stdout'].as_text(), Contains('a = 1')) + runner.run(Failing("test_a")) + self.assertThat(stdout.getDetails()["stdout"].as_text(), Contains("a = 1")) def test_stdout_honoured(self): self.useFixture(SampleTestFixture()) out = io.StringIO() - exc = self.assertRaises(SystemExit, run.main, - argv=['prog', 'testtools.runexample.test_suite'], - stdout=out) + exc = self.assertRaises( + SystemExit, + run.main, + argv=["prog", "testtools.runexample.test_suite"], + stdout=out, + ) self.assertEqual((0,), exc.args) self.assertThat( out.getvalue(), - MatchesRegex("""Tests running... + MatchesRegex( + """Tests running... Ran 2 tests in \\d.\\d\\d\\ds OK -""")) +""" + ), + ) @skipUnless(fixtures, "fixtures not present") def test_issue_16662(self): @@ -342,14 +447,21 @@ def test_issue_16662(self): out = io.StringIO() # XXX: http://bugs.python.org/issue22811 unittest.defaultTestLoader._top_level_dir = None - self.assertEqual(None, run.main( - ['prog', 'discover', '-l', pkg.package.base], out)) - self.assertEqual(dedent("""\ + self.assertEqual( + None, run.main(["prog", "discover", "-l", pkg.package.base], out) + ) + self.assertEqual( + dedent( + """\ discoverexample.TestExample.test_foo fred - """), out.getvalue()) + """ + ), + out.getvalue(), + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_runtest.py b/testtools/tests/test_runtest.py index 1ec7fbd9..37afecb5 100644 --- a/testtools/tests/test_runtest.py +++ b/testtools/tests/test_runtest.py @@ -8,21 +8,21 @@ RunTest, TestCase, TestResult, - ) +) from testtools.matchers import HasLength, MatchesException, Is, Raises from testtools.testresult.doubles import ExtendedTestResult from testtools.tests.helpers import FullStackRunTest class TestRunTest(TestCase): - run_tests_with = FullStackRunTest def make_case(self): class Case(TestCase): def test(self): pass - return Case('test') + + return Case("test") def test___init___short(self): run = RunTest("bar") @@ -43,10 +43,12 @@ def test__init____handlers_last_resort(self): def test_run_with_result(self): # test.run passes result down to _run_test_method. log = [] + class Case(TestCase): def _run_test_method(self, result): log.append(result) - case = Case('_run_test_method') + + case = Case("_run_test_method") run = RunTest(case, lambda x: log.append(x)) result = TestResult() run.run(result) @@ -63,40 +65,48 @@ def test__run_core_called(self): case = self.make_case() log = [] run = RunTest(case, lambda x: x) - run._run_core = lambda: log.append('foo') + run._run_core = lambda: log.append("foo") run.run() - self.assertEqual(['foo'], log) + self.assertEqual(["foo"], log) def test__run_prepared_result_does_not_mask_keyboard(self): tearDownRuns = [] + class Case(TestCase): def test(self): raise KeyboardInterrupt("go") + def _run_teardown(self, result): tearDownRuns.append(self) return super()._run_teardown(result) - case = Case('test') + + case = Case("test") run = RunTest(case) run.result = ExtendedTestResult() - self.assertThat(lambda: run._run_prepared_result(run.result), - Raises(MatchesException(KeyboardInterrupt))) - self.assertEqual( - [('startTest', case), ('stopTest', case)], run.result._events) + self.assertThat( + lambda: run._run_prepared_result(run.result), + Raises(MatchesException(KeyboardInterrupt)), + ) + self.assertEqual([("startTest", case), ("stopTest", case)], run.result._events) # tearDown is still run though! self.assertThat(tearDownRuns, HasLength(1)) def test__run_user_calls_onException(self): case = self.make_case() log = [] + def handler(exc_info): log.append("got it") self.assertEqual(3, len(exc_info)) self.assertIsInstance(exc_info[1], KeyError) self.assertIs(KeyError, exc_info[0]) + case.addOnException(handler) - e = KeyError('Yo') + e = KeyError("Yo") + def raises(): raise e + run = RunTest(case, [(KeyError, None)]) run.result = ExtendedTestResult() status = run._run_user(raises) @@ -106,9 +116,11 @@ def raises(): def test__run_user_can_catch_Exception(self): case = self.make_case() - e = Exception('Yo') + e = Exception("Yo") + def raises(): raise e + log = [] run = RunTest(case, [(Exception, None)]) run.result = ExtendedTestResult() @@ -118,20 +130,25 @@ def raises(): self.assertEqual([], log) def test__run_prepared_result_uncaught_Exception_raised(self): - e = KeyError('Yo') + e = KeyError("Yo") + class Case(TestCase): def test(self): raise e - case = Case('test') + + case = Case("test") log = [] + def log_exc(self, result, err): log.append((result, err)) + run = RunTest(case, [(ValueError, log_exc)]) run.result = ExtendedTestResult() - self.assertThat(lambda: run._run_prepared_result(run.result), - Raises(MatchesException(KeyError))) - self.assertEqual( - [('startTest', case), ('stopTest', case)], run.result._events) + self.assertThat( + lambda: run._run_prepared_result(run.result), + Raises(MatchesException(KeyError)), + ) + self.assertEqual([("startTest", case), ("stopTest", case)], run.result._events) self.assertEqual([], log) def test__run_prepared_result_uncaught_Exception_triggers_error(self): @@ -139,45 +156,59 @@ def test__run_prepared_result_uncaught_Exception_triggers_error(self): # When something isn't handled, the test that was # executing has errored, one way or another. e = SystemExit(0) + class Case(TestCase): def test(self): raise e - case = Case('test') + + case = Case("test") log = [] + def log_exc(self, result, err): log.append((result, err)) + run = RunTest(case, [], log_exc) run.result = ExtendedTestResult() - self.assertThat(lambda: run._run_prepared_result(run.result), - Raises(MatchesException(SystemExit))) - self.assertEqual( - [('startTest', case), ('stopTest', case)], run.result._events) + self.assertThat( + lambda: run._run_prepared_result(run.result), + Raises(MatchesException(SystemExit)), + ) + self.assertEqual([("startTest", case), ("stopTest", case)], run.result._events) self.assertEqual([(run.result, e)], log) def test__run_user_uncaught_Exception_from_exception_handler_raised(self): case = self.make_case() + def broken_handler(exc_info): # ValueError because thats what we know how to catch - and must # not. - raise ValueError('boo') + raise ValueError("boo") + case.addOnException(broken_handler) - e = KeyError('Yo') + e = KeyError("Yo") + def raises(): raise e + log = [] + def log_exc(self, result, err): log.append((result, err)) + run = RunTest(case, [(ValueError, log_exc)]) run.result = ExtendedTestResult() - self.assertThat(lambda: run._run_user(raises), - Raises(MatchesException(ValueError))) + self.assertThat( + lambda: run._run_user(raises), Raises(MatchesException(ValueError)) + ) self.assertEqual([], run.result._events) self.assertEqual([], log) def test__run_user_returns_result(self): case = self.make_case() + def returns(): return 1 + run = RunTest(case) run.result = ExtendedTestResult() self.assertEqual(1, run._run_user(returns)) @@ -185,44 +216,54 @@ def returns(): def test__run_one_decorates_result(self): log = [] + class Run(RunTest): def _run_prepared_result(self, result): log.append(result) return result + run = Run(self.make_case(), lambda x: x) - result = run._run_one('foo') + result = run._run_one("foo") self.assertEqual([result], log) self.assertIsInstance(log[0], ExtendedToOriginalDecorator) - self.assertEqual('foo', result.decorated) + self.assertEqual("foo", result.decorated) def test__run_prepared_result_calls_start_and_stop_test(self): result = ExtendedTestResult() case = self.make_case() run = RunTest(case, lambda x: x) run.run(result) - self.assertEqual([ - ('startTest', case), - ('addSuccess', case), - ('stopTest', case), - ], result._events) + self.assertEqual( + [ + ("startTest", case), + ("addSuccess", case), + ("stopTest", case), + ], + result._events, + ) def test__run_prepared_result_calls_stop_test_always(self): result = ExtendedTestResult() case = self.make_case() + def inner(): raise Exception("foo") + run = RunTest(case, lambda x: x) run._run_core = inner - self.assertThat(lambda: run.run(result), - Raises(MatchesException(Exception("foo")))) - self.assertEqual([ - ('startTest', case), - ('stopTest', case), - ], result._events) + self.assertThat( + lambda: run.run(result), Raises(MatchesException(Exception("foo"))) + ) + self.assertEqual( + [ + ("startTest", case), + ("stopTest", case), + ], + result._events, + ) class CustomRunTest(RunTest): - marker = object() def run(self, result=None): @@ -230,23 +271,25 @@ def run(self, result=None): class TestTestCaseSupportForRunTest(TestCase): - def test_pass_custom_run_test(self): class SomeCase(TestCase): def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo', runTest=CustomRunTest) + case = SomeCase("test_foo", runTest=CustomRunTest) from_run_test = case.run(result) self.assertThat(from_run_test, Is(CustomRunTest.marker)) def test_default_is_runTest_class_variable(self): class SomeCase(TestCase): run_tests_with = CustomRunTest + def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo') + case = SomeCase("test_foo") from_run_test = case.run(result) self.assertThat(from_run_test, Is(CustomRunTest.marker)) @@ -254,15 +297,19 @@ def test_constructor_argument_overrides_class_variable(self): # If a 'runTest' argument is passed to the test's constructor, that # overrides the class variable. marker = object() + class DifferentRunTest(RunTest): def run(self, result=None): return marker + class SomeCase(TestCase): run_tests_with = CustomRunTest + def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo', runTest=DifferentRunTest) + case = SomeCase("test_foo", runTest=DifferentRunTest) from_run_test = case.run(result) self.assertThat(from_run_test, Is(marker)) @@ -272,8 +319,9 @@ class SomeCase(TestCase): @run_test_with(CustomRunTest) def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo') + case = SomeCase("test_foo") from_run_test = case.run(result) self.assertThat(from_run_test, Is(CustomRunTest.marker)) @@ -282,18 +330,22 @@ def test_extended_decorator_for_run_test(self): # Extra arguments can be passed to the decorator which will then be # passed on to the RunTest object. marker = object() + class FooRunTest(RunTest): def __init__(self, case, handlers=None, bar=None): super().__init__(case, handlers) self.bar = bar + def run(self, result=None): return self.bar + class SomeCase(TestCase): @run_test_with(FooRunTest, bar=marker) def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo') + case = SomeCase("test_foo") from_run_test = case.run(result) self.assertThat(from_run_test, Is(marker)) @@ -302,18 +354,22 @@ def test_works_as_inner_decorator(self): # respected. def wrapped(function): """Silly, trivial decorator.""" + def decorated(*args, **kwargs): return function(*args, **kwargs) + decorated.__name__ = function.__name__ decorated.__dict__.update(function.__dict__) return decorated + class SomeCase(TestCase): @wrapped @run_test_with(CustomRunTest) def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo') + case = SomeCase("test_foo") from_run_test = case.run(result) self.assertThat(from_run_test, Is(CustomRunTest.marker)) @@ -321,19 +377,23 @@ def test_constructor_overrides_decorator(self): # If a 'runTest' argument is passed to the test's constructor, that # overrides the decorator. marker = object() + class DifferentRunTest(RunTest): def run(self, result=None): return marker + class SomeCase(TestCase): @run_test_with(CustomRunTest) def test_foo(self): pass + result = TestResult() - case = SomeCase('test_foo', runTest=DifferentRunTest) + case = SomeCase("test_foo", runTest=DifferentRunTest) from_run_test = case.run(result) self.assertThat(from_run_test, Is(marker)) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_tags.py b/testtools/tests/test_tags.py index ee681709..e2bc70c0 100644 --- a/testtools/tests/test_tags.py +++ b/testtools/tests/test_tags.py @@ -8,7 +8,6 @@ class TestTags(TestCase): - def test_no_tags(self): # A tag context has no tags initially. tag_context = TagContext() @@ -17,68 +16,67 @@ def test_no_tags(self): def test_add_tag(self): # A tag added with change_tags appears in get_current_tags. tag_context = TagContext() - tag_context.change_tags({'foo'}, set()) - self.assertEqual({'foo'}, tag_context.get_current_tags()) + tag_context.change_tags({"foo"}, set()) + self.assertEqual({"foo"}, tag_context.get_current_tags()) def test_add_tag_twice(self): # Calling change_tags twice to add tags adds both tags to the current # tags. tag_context = TagContext() - tag_context.change_tags({'foo'}, set()) - tag_context.change_tags({'bar'}, set()) - self.assertEqual( - {'foo', 'bar'}, tag_context.get_current_tags()) + tag_context.change_tags({"foo"}, set()) + tag_context.change_tags({"bar"}, set()) + self.assertEqual({"foo", "bar"}, tag_context.get_current_tags()) def test_change_tags_returns_tags(self): # change_tags returns the current tags. This is a convenience. tag_context = TagContext() - tags = tag_context.change_tags({'foo'}, set()) - self.assertEqual({'foo'}, tags) + tags = tag_context.change_tags({"foo"}, set()) + self.assertEqual({"foo"}, tags) def test_remove_tag(self): # change_tags can remove tags from the context. tag_context = TagContext() - tag_context.change_tags({'foo'}, set()) - tag_context.change_tags(set(), {'foo'}) + tag_context.change_tags({"foo"}, set()) + tag_context.change_tags(set(), {"foo"}) self.assertEqual(set(), tag_context.get_current_tags()) def test_child_context(self): # A TagContext can have a parent. If so, its tags are the tags of the # parent at the moment of construction. parent = TagContext() - parent.change_tags({'foo'}, set()) + parent.change_tags({"foo"}, set()) child = TagContext(parent) - self.assertEqual( - parent.get_current_tags(), child.get_current_tags()) + self.assertEqual(parent.get_current_tags(), child.get_current_tags()) def test_add_to_child(self): # Adding a tag to the child context doesn't affect the parent. parent = TagContext() - parent.change_tags({'foo'}, set()) + parent.change_tags({"foo"}, set()) child = TagContext(parent) - child.change_tags({'bar'}, set()) - self.assertEqual({'foo', 'bar'}, child.get_current_tags()) - self.assertEqual({'foo'}, parent.get_current_tags()) + child.change_tags({"bar"}, set()) + self.assertEqual({"foo", "bar"}, child.get_current_tags()) + self.assertEqual({"foo"}, parent.get_current_tags()) def test_remove_in_child(self): # A tag that was in the parent context can be removed from the child # context without affect the parent. parent = TagContext() - parent.change_tags({'foo'}, set()) + parent.change_tags({"foo"}, set()) child = TagContext(parent) - child.change_tags(set(), {'foo'}) + child.change_tags(set(), {"foo"}) self.assertEqual(set(), child.get_current_tags()) - self.assertEqual({'foo'}, parent.get_current_tags()) + self.assertEqual({"foo"}, parent.get_current_tags()) def test_parent(self): # The parent can be retrieved from a child context. parent = TagContext() - parent.change_tags({'foo'}, set()) + parent.change_tags({"foo"}, set()) child = TagContext(parent) - child.change_tags(set(), {'foo'}) + child.change_tags(set(), {"foo"}) self.assertEqual(parent, child.parent) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_testcase.py b/testtools/tests/test_testcase.py index 69c5213d..37dc825d 100644 --- a/testtools/tests/test_testcase.py +++ b/testtools/tests/test_testcase.py @@ -20,14 +20,14 @@ skipIf, skipUnless, testcase, - ) +) from testtools.compat import ( _b, - ) +) from testtools.content import ( text_content, TracebackContent, - ) +) from testtools.matchers import ( Annotate, ContainsAll, @@ -36,18 +36,18 @@ HasLength, MatchesException, Raises, - ) +) from testtools.testcase import ( attr, Nullary, WithAttributes, TestSkipped, - ) +) from testtools.testresult.doubles import ( Python26TestResult, Python27TestResult, ExtendedTestResult, - ) +) from testtools.tests.helpers import ( an_exc_info, AsText, @@ -55,7 +55,7 @@ LoggingResult, MatchesEvents, raise_, - ) +) from testtools.tests.samplecases import ( deterministic_sample_cases_scenarios, make_case_for_behavior_scenario, @@ -65,7 +65,6 @@ class TestPlaceHolder(TestCase): - run_test_with = FullStackRunTest def makePlaceHolder(self, test_id="foo", short_description=None): @@ -99,21 +98,26 @@ def test_repr_just_id(self): # repr(placeholder) shows you how the object was constructed. test = PlaceHolder("test id") self.assertEqual( - "" % repr( - test.id()), repr(test)) + "" % repr(test.id()), + repr(test), + ) def test_repr_with_description(self): # repr(placeholder) shows you how the object was constructed. test = PlaceHolder("test id", "description") self.assertEqual( "".format( - test.id(), test.shortDescription()), repr(test)) + test.id(), test.shortDescription() + ), + repr(test), + ) def test_repr_custom_outcome(self): - test = PlaceHolder("test id", outcome='addSkip') + test = PlaceHolder("test id", outcome="addSkip") self.assertEqual( - "" % ( - test.id()), repr(test)) + "" % (test.id()), + repr(test), + ) def test_counts_as_one_test(self): # A placeholder test counts as one test. @@ -131,38 +135,48 @@ def test_runs_as_success(self): log = [] test.run(LoggingResult(log)) self.assertEqual( - [('tags', set(), set()), ('startTest', test), ('addSuccess', test), - ('stopTest', test), ('tags', set(), set()),], - log) + [ + ("tags", set(), set()), + ("startTest", test), + ("addSuccess", test), + ("stopTest", test), + ("tags", set(), set()), + ], + log, + ) def test_supplies_details(self): - details = {'quux':None} - test = PlaceHolder('foo', details=details) + details = {"quux": None} + test = PlaceHolder("foo", details=details) result = ExtendedTestResult() test.run(result) self.assertEqual( - [('tags', set(), set()), - ('startTest', test), - ('addSuccess', test, details), - ('stopTest', test), - ('tags', set(), set()), - ], - result._events) + [ + ("tags", set(), set()), + ("startTest", test), + ("addSuccess", test, details), + ("stopTest", test), + ("tags", set(), set()), + ], + result._events, + ) def test_supplies_timestamps(self): - test = PlaceHolder('foo', details={}, timestamps=["A", "B"]) + test = PlaceHolder("foo", details={}, timestamps=["A", "B"]) result = ExtendedTestResult() test.run(result) self.assertEqual( - [('time', "A"), - ('tags', set(), set()), - ('startTest', test), - ('time', "B"), - ('addSuccess', test), - ('stopTest', test), - ('tags', set(), set()), - ], - result._events) + [ + ("time", "A"), + ("tags", set(), set()), + ("startTest", test), + ("time", "B"), + ("addSuccess", test), + ("stopTest", test), + ("tags", set(), set()), + ], + result._events, + ) def test_call_is_run(self): # A PlaceHolder can be called, in which case it behaves like run. @@ -184,16 +198,19 @@ def test_debug(self): def test_supports_tags(self): result = ExtendedTestResult() - tags = {'foo', 'bar'} + tags = {"foo", "bar"} case = PlaceHolder("foo", tags=tags) case.run(result) - self.assertEqual([ - ('tags', tags, set()), - ('startTest', case), - ('addSuccess', case), - ('stopTest', case), - ('tags', set(), tags), - ], result._events) + self.assertEqual( + [ + ("tags", tags, set()), + ("startTest", case), + ("addSuccess", case), + ("stopTest", case), + ("tags", set(), tags), + ], + result._events, + ) class TestErrorHolder(TestCase): @@ -208,8 +225,7 @@ def makeException(self): except BaseException: return sys.exc_info() - def makePlaceHolder(self, test_id="foo", error=None, - short_description=None): + def makePlaceHolder(self, test_id="foo", error=None, short_description=None): if error is None: error = self.makeException() return ErrorHolder(test_id, error, short_description) @@ -248,11 +264,15 @@ def test_runs_as_error(self): log = result._events test.run(result) self.assertEqual( - [('tags', set(), set()), - ('startTest', test), - ('addError', test, test._details), - ('stopTest', test), - ('tags', set(), set())], log) + [ + ("tags", set(), set()), + ("startTest", test), + ("addError", test, test._details), + ("stopTest", test), + ("tags", set(), set()), + ], + log, + ) def test_call_is_run(self): # A PlaceHolder can be called, in which case it behaves like run. @@ -284,8 +304,7 @@ def test_identicalIsEqual(self): def test_nonIdenticalInUnequal(self): # TestCase's are not equal if they are not identical. - self.assertNotEqual(TestCase(methodName='run'), - TestCase(methodName='skip')) + self.assertNotEqual(TestCase(methodName="run"), TestCase(methodName="skip")) class TestAssertions(TestCase): @@ -300,16 +319,19 @@ def test_formatTypes_single(self): # Given a single class, _formatTypes returns the name. class Foo: pass - self.assertEqual('Foo', self._formatTypes(Foo)) + + self.assertEqual("Foo", self._formatTypes(Foo)) def test_formatTypes_multiple(self): # Given multiple types, _formatTypes returns the names joined by # commas. class Foo: pass + class Bar: pass - self.assertEqual('Foo, Bar', self._formatTypes([Foo, Bar])) + + self.assertEqual("Foo, Bar", self._formatTypes([Foo, Bar])) def test_assertRaises(self): # assertRaises asserts that a callable raises a particular exception. @@ -319,8 +341,8 @@ def test_assertRaises_exception_w_metaclass(self): # assertRaises works when called for exceptions with custom metaclasses class MyExMeta(type): def __init__(cls, name, bases, dct): - """ Do some dummy metaclass stuff """ - dct.update({'answer': 42}) + """Do some dummy metaclass stuff""" + dct.update({"answer": 42}) type.__init__(cls, name, bases, dct) class MyEx(Exception): @@ -331,16 +353,20 @@ class MyEx(Exception): def test_assertRaises_fails_when_no_error_raised(self): # assertRaises raises self.failureException when it's passed a # callable that raises no error. - ret = ('orange', 42) + ret = ("orange", 42) self.assertFails( " at ...> returned ('orange', 42)", - self.assertRaises, RuntimeError, lambda: ret) + self.assertRaises, + RuntimeError, + lambda: ret, + ) def test_assertRaises_fails_when_different_error_raised(self): # assertRaises re-raises an exception that it didn't expect. - self.assertThat(lambda: self.assertRaises(RuntimeError, - self.raiseError, ZeroDivisionError), - Raises(MatchesException(ZeroDivisionError))) + self.assertThat( + lambda: self.assertRaises(RuntimeError, self.raiseError, ZeroDivisionError), + Raises(MatchesException(ZeroDivisionError)), + ) def test_assertRaises_returns_the_raised_exception(self): # assertRaises returns the exception object that was raised. This is @@ -349,9 +375,10 @@ def test_assertRaises_returns_the_raised_exception(self): # This contraption stores the raised exception, so we can compare it # to the return value of assertRaises. raisedExceptions = [] + def raiseError(): try: - raise RuntimeError('Deliberate error') + raise RuntimeError("Deliberate error") except RuntimeError: raisedExceptions.append(sys.exc_info()[1]) raise @@ -361,17 +388,15 @@ def raiseError(): self.assertIs( exception, raisedExceptions[0], - '{!r} is not {!r}'.format(exception, raisedExceptions[0]) + "{!r} is not {!r}".format(exception, raisedExceptions[0]), ) def test_assertRaises_with_multiple_exceptions(self): # assertRaises((ExceptionOne, ExceptionTwo), function) asserts that # function raises one of ExceptionTwo or ExceptionOne. expectedExceptions = (RuntimeError, ZeroDivisionError) - self.assertRaises( - expectedExceptions, self.raiseError, expectedExceptions[0]) - self.assertRaises( - expectedExceptions, self.raiseError, expectedExceptions[1]) + self.assertRaises(expectedExceptions, self.raiseError, expectedExceptions[0]) + self.assertRaises(expectedExceptions, self.raiseError, expectedExceptions[1]) def test_assertRaises_with_multiple_exceptions_failure_mode(self): # If assertRaises is called expecting one of a group of exceptions and @@ -379,10 +404,14 @@ def test_assertRaises_with_multiple_exceptions_failure_mode(self): # appropriate error message. expectedExceptions = (RuntimeError, ZeroDivisionError) self.assertRaises( - self.failureException, - self.assertRaises, expectedExceptions, lambda: None) - self.assertFails(' at ...> returned None', - self.assertRaises, expectedExceptions, lambda: None) + self.failureException, self.assertRaises, expectedExceptions, lambda: None + ) + self.assertFails( + " at ...> returned None", + self.assertRaises, + expectedExceptions, + lambda: None, + ) def test_assertRaises_function_repr_in_exception(self): # When assertRaises fails, it includes the repr of the invoked @@ -390,87 +419,117 @@ def test_assertRaises_function_repr_in_exception(self): def foo(): """An arbitrary function.""" pass + self.assertThat( lambda: self.assertRaises(Exception, foo), - Raises( - MatchesException(self.failureException, f'.*{foo!r}.*'))) + Raises(MatchesException(self.failureException, f".*{foo!r}.*")), + ) def test_assertRaisesRegex(self): # assertRaisesRegex asserts that function raises particular exception # with particular message. - self.assertRaisesRegex(RuntimeError, r"M\w*e", self.raiseError, - RuntimeError, "Message") + self.assertRaisesRegex( + RuntimeError, r"M\w*e", self.raiseError, RuntimeError, "Message" + ) def test_assertRaisesRegex_wrong_error_type(self): # If function raises an exception of unexpected type, # assertRaisesRegex re-raises it. - self.assertRaises(ValueError, self.assertRaisesRegex, RuntimeError, - r"M\w*e", self.raiseError, ValueError, "Message") + self.assertRaises( + ValueError, + self.assertRaisesRegex, + RuntimeError, + r"M\w*e", + self.raiseError, + ValueError, + "Message", + ) def test_assertRaisesRegex_wrong_message(self): # If function raises an exception with unexpected message # assertRaisesRegex fails. self.assertFails( '"Expected" does not match "Observed"', - self.assertRaisesRegex, RuntimeError, "Expected", - self.raiseError, RuntimeError, "Observed") + self.assertRaisesRegex, + RuntimeError, + "Expected", + self.raiseError, + RuntimeError, + "Observed", + ) def assertFails(self, message, function, *args, **kwargs): """Assert that function raises a failure with the given message.""" - failure = self.assertRaises( - self.failureException, function, *args, **kwargs) + failure = self.assertRaises(self.failureException, function, *args, **kwargs) self.assertThat(failure, DocTestMatches(message, ELLIPSIS)) def test_assertIn_success(self): # assertIn(needle, haystack) asserts that 'needle' is in 'haystack'. self.assertIn(3, range(10)) - self.assertIn('foo', 'foo bar baz') - self.assertIn('foo', 'foo bar baz'.split()) + self.assertIn("foo", "foo bar baz") + self.assertIn("foo", "foo bar baz".split()) def test_assertIn_failure(self): # assertIn(needle, haystack) fails the test when 'needle' is not in # 'haystack'. - self.assertFails('3 not in [0, 1, 2]', self.assertIn, 3, [0, 1, 2]) + self.assertFails("3 not in [0, 1, 2]", self.assertIn, 3, [0, 1, 2]) self.assertFails( - '{!r} not in {!r}'.format('qux', 'foo bar baz'), - self.assertIn, 'qux', 'foo bar baz') + "{!r} not in {!r}".format("qux", "foo bar baz"), + self.assertIn, + "qux", + "foo bar baz", + ) def test_assertIn_failure_with_message(self): # assertIn(needle, haystack) fails the test when 'needle' is not in # 'haystack'. - self.assertFails('3 not in [0, 1, 2]: foo bar', self.assertIn, 3, - [0, 1, 2], 'foo bar') self.assertFails( - '{!r} not in {!r}: foo bar'.format('qux', 'foo bar baz'), - self.assertIn, 'qux', 'foo bar baz', 'foo bar') - + "3 not in [0, 1, 2]: foo bar", self.assertIn, 3, [0, 1, 2], "foo bar" + ) + self.assertFails( + "{!r} not in {!r}: foo bar".format("qux", "foo bar baz"), + self.assertIn, + "qux", + "foo bar baz", + "foo bar", + ) def test_assertNotIn_success(self): # assertNotIn(needle, haystack) asserts that 'needle' is not in # 'haystack'. self.assertNotIn(3, [0, 1, 2]) - self.assertNotIn('qux', 'foo bar baz') + self.assertNotIn("qux", "foo bar baz") def test_assertNotIn_failure(self): # assertNotIn(needle, haystack) fails the test when 'needle' is in # 'haystack'. - self.assertFails('[1, 2, 3] matches Contains(3)', self.assertNotIn, - 3, [1, 2, 3]) + self.assertFails( + "[1, 2, 3] matches Contains(3)", self.assertNotIn, 3, [1, 2, 3] + ) self.assertFails( "'foo bar baz' matches Contains('foo')", - self.assertNotIn, 'foo', 'foo bar baz') - + self.assertNotIn, + "foo", + "foo bar baz", + ) def test_assertNotIn_failure_with_message(self): # assertNotIn(needle, haystack) fails the test when 'needle' is in # 'haystack'. - self.assertFails('[1, 2, 3] matches Contains(3): foo bar', self.assertNotIn, - 3, [1, 2, 3], 'foo bar') + self.assertFails( + "[1, 2, 3] matches Contains(3): foo bar", + self.assertNotIn, + 3, + [1, 2, 3], + "foo bar", + ) self.assertFails( "'foo bar baz' matches Contains('foo'): foo bar", - self.assertNotIn, 'foo', 'foo bar baz', "foo bar") - - + self.assertNotIn, + "foo", + "foo bar baz", + "foo bar", + ) def test_assertIsInstance(self): # assertIsInstance asserts that an object is an instance of a class. @@ -504,7 +563,10 @@ class Foo: self.assertFails( "'42' is not an instance of %s" % self._formatTypes(Foo), - self.assertIsInstance, 42, Foo) + self.assertIsInstance, + 42, + Foo, + ) def test_assertIsInstance_failure_multiple_classes(self): # assertIsInstance(obj, (klass1, klass2)) fails the test when obj is @@ -518,12 +580,16 @@ class Bar: self.assertFails( "'42' is not an instance of any of (%s)" % self._formatTypes([Foo, Bar]), - self.assertIsInstance, 42, (Foo, Bar)) + self.assertIsInstance, + 42, + (Foo, Bar), + ) def test_assertIsInstance_overridden_message(self): # assertIsInstance(obj, klass, msg) permits a custom message. - self.assertFails("'42' is not an instance of str: foo", - self.assertIsInstance, 42, str, "foo") + self.assertFails( + "'42' is not an instance of str: foo", self.assertIsInstance, 42, str, "foo" + ) def test_assertIs(self): # assertIs asserts that an object is identical to another object. @@ -536,14 +602,13 @@ def test_assertIs(self): def test_assertIs_fails(self): # assertIs raises assertion errors if one object is not identical to # another. - self.assertFails('42 is not None', self.assertIs, None, 42) - self.assertFails('[42] is not [42]', self.assertIs, [42], [42]) + self.assertFails("42 is not None", self.assertIs, None, 42) + self.assertFails("[42] is not [42]", self.assertIs, [42], [42]) def test_assertIs_fails_with_message(self): # assertIs raises assertion errors if one object is not identical to # another, and includes a user-supplied message, if it's provided. - self.assertFails( - '42 is not None: foo bar', self.assertIs, None, 42, 'foo bar') + self.assertFails("42 is not None: foo bar", self.assertIs, None, 42, "foo bar") def test_assertIsNot(self): # assertIsNot asserts that an object is not identical to another @@ -555,96 +620,108 @@ def test_assertIsNot(self): def test_assertIsNot_fails(self): # assertIsNot raises assertion errors if one object is identical to # another. - self.assertFails('None matches Is(None)', self.assertIsNot, None, None) + self.assertFails("None matches Is(None)", self.assertIsNot, None, None) some_list = [42] self.assertFails( - '[42] matches Is([42])', self.assertIsNot, some_list, some_list) + "[42] matches Is([42])", self.assertIsNot, some_list, some_list + ) def test_assertIsNot_fails_with_message(self): # assertIsNot raises assertion errors if one object is identical to # another, and includes a user-supplied message if it's provided. self.assertFails( - 'None matches Is(None): foo bar', self.assertIsNot, None, None, - "foo bar") + "None matches Is(None): foo bar", self.assertIsNot, None, None, "foo bar" + ) def test_assertThat_matches_clean(self): class Matcher: def match(self, foo): return None + self.assertThat("foo", Matcher()) def test_assertThat_mismatch_raises_description(self): calls = [] + class Mismatch: def __init__(self, thing): self.thing = thing + def describe(self): - calls.append(('describe_diff', self.thing)) + calls.append(("describe_diff", self.thing)) return "object is not a thing" + def get_details(self): return {} + class Matcher: def match(self, thing): - calls.append(('match', thing)) + calls.append(("match", thing)) return Mismatch(thing) + def __str__(self): - calls.append(('__str__',)) + calls.append(("__str__",)) return "a description" + class Test(TestCase): def test(self): self.assertThat("foo", Matcher()) + result = Test("test").run() - self.assertEqual([ - ('match', "foo"), - ('describe_diff', "foo"), - ], calls) + self.assertEqual( + [ + ("match", "foo"), + ("describe_diff", "foo"), + ], + calls, + ) self.assertFalse(result.wasSuccessful()) def test_assertThat_output(self): - matchee = 'foo' - matcher = Equals('bar') + matchee = "foo" + matcher = Equals("bar") expected = matcher.match(matchee).describe() self.assertFails(expected, self.assertThat, matchee, matcher) def test_assertThat_message_is_annotated(self): - matchee = 'foo' - matcher = Equals('bar') - expected = Annotate('woo', matcher).match(matchee).describe() - self.assertFails(expected, self.assertThat, matchee, matcher, 'woo') + matchee = "foo" + matcher = Equals("bar") + expected = Annotate("woo", matcher).match(matchee).describe() + self.assertFails(expected, self.assertThat, matchee, matcher, "woo") def test_assertThat_verbose_output(self): - matchee = 'foo' - matcher = Equals('bar') - expected = ( - 'Match failed. Matchee: %r\n' - 'Matcher: %s\n' - 'Difference: %s\n' % ( - matchee, - matcher, - matcher.match(matchee).describe(), - )) - self.assertFails( - expected, self.assertThat, matchee, matcher, verbose=True) + matchee = "foo" + matcher = Equals("bar") + expected = "Match failed. Matchee: %r\n" "Matcher: %s\n" "Difference: %s\n" % ( + matchee, + matcher, + matcher.match(matchee).describe(), + ) + self.assertFails(expected, self.assertThat, matchee, matcher, verbose=True) def test_expectThat_matches_clean(self): class Matcher: def match(self, foo): return None + self.expectThat("foo", Matcher()) def test_expectThat_mismatch_fails_test(self): class Test(TestCase): def test(self): self.expectThat("foo", Equals("bar")) + result = Test("test").run() self.assertFalse(result.wasSuccessful()) def test_expectThat_does_not_exit_test(self): class Test(TestCase): marker = False + def test(self): self.expectThat("foo", Equals("bar")) Test.marker = True + result = Test("test").run() self.assertFalse(result.wasSuccessful()) self.assertTrue(Test.marker) @@ -653,17 +730,19 @@ def test_expectThat_adds_detail(self): class Test(TestCase): def test(self): self.expectThat("foo", Equals("bar")) + test = Test("test") test.run() details = test.getDetails() - self.assertIn('Failed expectation', details) + self.assertIn("Failed expectation", details) def test__force_failure_fails_test(self): class Test(TestCase): def test_foo(self): self.force_failure = True self.remaining_code_run = True - test = Test('test_foo') + + test = Test("test_foo") result = test.run() self.assertFalse(result.wasSuccessful()) self.assertTrue(test.remaining_code_run) @@ -682,47 +761,53 @@ def get_error_string(self, e): """ error = TracebackContent((e.__class__, e, None), self).as_text() # We aren't at all interested in the traceback. - if error.startswith('Traceback (most recent call last):\n'): + if error.startswith("Traceback (most recent call last):\n"): lines = error.splitlines(True)[1:] for i, line in enumerate(lines): - if not line.startswith(' '): + if not line.startswith(" "): break - error = ''.join(lines[i:]) + error = "".join(lines[i:]) # We aren't interested in how the exception type is formatted. - exc_class, error = error.split(': ', 1) + exc_class, error = error.split(": ", 1) return error def test_assertThat_verbose_unicode(self): # When assertThat is given matchees or matchers that contain non-ASCII # unicode strings, we can still provide a meaningful error. - matchee = '\xa7' - matcher = Equals('a') + matchee = "\xa7" + matcher = Equals("a") expected = ( - 'Match failed. Matchee: %s\n' - 'Matcher: %s\n' - 'Difference: %s\n\n' % ( + "Match failed. Matchee: %s\n" + "Matcher: %s\n" + "Difference: %s\n\n" + % ( repr(matchee).replace("\\xa7", matchee), matcher, matcher.match(matchee).describe(), - )) + ) + ) e = self.assertRaises( - self.failureException, self.assertThat, matchee, matcher, - verbose=True) + self.failureException, self.assertThat, matchee, matcher, verbose=True + ) self.assertEqual(expected, self.get_error_string(e)) def test_assertEqual_nice_formatting(self): message = "These things ought not be equal." - a = ['apple', 'banana', 'cherry'] - b = {'Thatcher': 'One who mends roofs of straw', - 'Major': 'A military officer, ranked below colonel', - 'Blair': 'To shout loudly', - 'Brown': 'The colour of healthy human faeces'} - expected_error = '\n'.join([ - '!=:', - 'reference = %s' % pformat(a), - 'actual = %s' % pformat(b), - ': ' + message, - ]) + a = ["apple", "banana", "cherry"] + b = { + "Thatcher": "One who mends roofs of straw", + "Major": "A military officer, ranked below colonel", + "Blair": "To shout loudly", + "Brown": "The colour of healthy human faeces", + } + expected_error = "\n".join( + [ + "!=:", + "reference = %s" % pformat(a), + "actual = %s" % pformat(b), + ": " + message, + ] + ) self.assertFails(expected_error, self.assertEqual, a, b, message) self.assertFails(expected_error, self.assertEquals, a, b, message) self.assertFails(expected_error, self.failUnlessEqual, a, b, message) @@ -739,41 +824,42 @@ def test_assertEqual_non_ascii_str_with_newlines(self): message = "Be careful mixing unicode and bytes" a = "a\n\xa7\n" b = "Just a longish string so the more verbose output form is used." - expected_error = '\n'.join([ - '!=:', - "reference = '''\\", - 'a', - repr('\xa7')[1:-1], - "'''", - f'actual = {b!r}', - ': ' + message, - ]) + expected_error = "\n".join( + [ + "!=:", + "reference = '''\\", + "a", + repr("\xa7")[1:-1], + "'''", + f"actual = {b!r}", + ": " + message, + ] + ) self.assertFails(expected_error, self.assertEqual, a, b, message) def test_assertIsNone(self): self.assertIsNone(None) - expected_error = '0 is not None' + expected_error = "0 is not None" self.assertFails(expected_error, self.assertIsNone, 0) def test_assertIsNotNone(self): self.assertIsNotNone(0) self.assertIsNotNone("0") - expected_error = 'None matches Is(None)' + expected_error = "None matches Is(None)" self.assertFails(expected_error, self.assertIsNotNone, None) - def test_fail_preserves_traceback_detail(self): class Test(TestCase): def test(self): - self.addDetail('traceback', text_content('foo')) - self.fail('bar') - test = Test('test') + self.addDetail("traceback", text_content("foo")) + self.fail("bar") + + test = Test("test") result = ExtendedTestResult() test.run(result) - self.assertEqual({'traceback', 'traceback-1'}, - set(result._events[1][2].keys())) + self.assertEqual({"traceback", "traceback-1"}, set(result._events[1][2].keys())) class TestAddCleanup(TestCase): @@ -787,14 +873,13 @@ def test_cleanup_run_after_tearDown(self): log = [] test = make_test_case( self.getUniqueString(), - set_up=lambda _: log.append('setUp'), - test_body=lambda _: log.append('runTest'), - tear_down=lambda _: log.append('tearDown'), - cleanups=[lambda _: log.append('cleanup')], + set_up=lambda _: log.append("setUp"), + test_body=lambda _: log.append("runTest"), + tear_down=lambda _: log.append("tearDown"), + cleanups=[lambda _: log.append("cleanup")], ) test.run() - self.assertThat( - log, Equals(['setUp', 'runTest', 'tearDown', 'cleanup'])) + self.assertThat(log, Equals(["setUp", "runTest", "tearDown", "cleanup"])) def test_add_cleanup_called_if_setUp_fails(self): # Cleanup functions added with 'addCleanup' are called even if setUp @@ -803,18 +888,18 @@ def test_add_cleanup_called_if_setUp_fails(self): log = [] def broken_set_up(ignored): - log.append('brokenSetUp') - raise RuntimeError('Deliberate broken setUp') + log.append("brokenSetUp") + raise RuntimeError("Deliberate broken setUp") test = make_test_case( self.getUniqueString(), set_up=broken_set_up, - test_body=lambda _: log.append('runTest'), - tear_down=lambda _: log.append('tearDown'), - cleanups=[lambda _: log.append('cleanup')], + test_body=lambda _: log.append("runTest"), + tear_down=lambda _: log.append("tearDown"), + cleanups=[lambda _: log.append("cleanup")], ) test.run() - self.assertThat(log, Equals(['brokenSetUp', 'cleanup'])) + self.assertThat(log, Equals(["brokenSetUp", "cleanup"])) def test_addCleanup_called_in_reverse_order(self): # Cleanup functions added with 'addCleanup' are called in reverse @@ -831,73 +916,84 @@ def test_addCleanup_called_in_reverse_order(self): log = [] test = make_test_case( self.getUniqueString(), - set_up=lambda _: log.append('setUp'), - test_body=lambda _: log.append('runTest'), - tear_down=lambda _: log.append('tearDown'), + set_up=lambda _: log.append("setUp"), + test_body=lambda _: log.append("runTest"), + tear_down=lambda _: log.append("tearDown"), cleanups=[ - lambda _: log.append('first'), - lambda _: log.append('second'), + lambda _: log.append("first"), + lambda _: log.append("second"), ], ) test.run() self.assertThat( - log, Equals(['setUp', 'runTest', 'tearDown', 'second', 'first'])) + log, Equals(["setUp", "runTest", "tearDown", "second", "first"]) + ) def test_tearDown_runs_on_cleanup_failure(self): # tearDown runs even if a cleanup function fails. log = [] test = make_test_case( self.getUniqueString(), - set_up=lambda _: log.append('setUp'), - test_body=lambda _: log.append('runTest'), - tear_down=lambda _: log.append('tearDown'), - cleanups=[lambda _: 1/0], + set_up=lambda _: log.append("setUp"), + test_body=lambda _: log.append("runTest"), + tear_down=lambda _: log.append("tearDown"), + cleanups=[lambda _: 1 / 0], ) test.run() - self.assertThat(log, Equals(['setUp', 'runTest', 'tearDown'])) + self.assertThat(log, Equals(["setUp", "runTest", "tearDown"])) def test_cleanups_continue_running_after_error(self): # All cleanups are always run, even if one or two of them fail. log = [] test = make_test_case( self.getUniqueString(), - set_up=lambda _: log.append('setUp'), - test_body=lambda _: log.append('runTest'), - tear_down=lambda _: log.append('tearDown'), + set_up=lambda _: log.append("setUp"), + test_body=lambda _: log.append("runTest"), + tear_down=lambda _: log.append("tearDown"), cleanups=[ - lambda _: log.append('first'), - lambda _: 1/0, - lambda _: log.append('second'), + lambda _: log.append("first"), + lambda _: 1 / 0, + lambda _: log.append("second"), ], ) test.run() self.assertThat( - log, Equals(['setUp', 'runTest', 'tearDown', 'second', 'first'])) + log, Equals(["setUp", "runTest", "tearDown", "second", "first"]) + ) def test_error_in_cleanups_are_captured(self): # If a cleanup raises an error, we want to record it and fail the the # test, even though we go on to run other cleanups. - test = make_test_case(self.getUniqueString(), cleanups=[lambda _: 1/0]) + test = make_test_case(self.getUniqueString(), cleanups=[lambda _: 1 / 0]) log = [] test.run(ExtendedTestResult(log)) self.assertThat( - log, MatchesEvents( - ('startTest', test), - ('addError', test, { - 'traceback': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - }), - ('stopTest', test), - ) + log, + MatchesEvents( + ("startTest", test), + ( + "addError", + test, + { + "traceback": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + }, + ), + ("stopTest", test), + ), ) def test_keyboard_interrupt_not_caught(self): # If a cleanup raises KeyboardInterrupt, it gets reraised. test = make_test_case( - self.getUniqueString(), cleanups=[ - lambda _: raise_(KeyboardInterrupt())]) + self.getUniqueString(), cleanups=[lambda _: raise_(KeyboardInterrupt())] + ) self.assertThat(test.run, Raises(MatchesException(KeyboardInterrupt))) def test_all_errors_from_MultipleExceptions_reported(self): @@ -905,11 +1001,11 @@ def test_all_errors_from_MultipleExceptions_reported(self): # reported. def raise_many(ignored): try: - 1/0 + 1 / 0 except Exception: exc_info1 = sys.exc_info() try: - 1/0 + 1 / 0 except Exception: exc_info2 = sys.exc_info() raise MultipleExceptions(exc_info1, exc_info2) @@ -918,45 +1014,74 @@ def raise_many(ignored): log = [] test.run(ExtendedTestResult(log)) self.assertThat( - log, MatchesEvents( - ('startTest', test), - ('addError', test, { - 'traceback': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - 'traceback-1': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - }), - ('stopTest', test), - ) + log, + MatchesEvents( + ("startTest", test), + ( + "addError", + test, + { + "traceback": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + "traceback-1": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + }, + ), + ("stopTest", test), + ), ) def test_multipleCleanupErrorsReported(self): # Errors from all failing cleanups are reported as separate backtraces. - test = make_test_case(self.getUniqueString(), cleanups=[ - lambda _: 1/0, - lambda _: 1/0, - ]) + test = make_test_case( + self.getUniqueString(), + cleanups=[ + lambda _: 1 / 0, + lambda _: 1 / 0, + ], + ) log = [] test.run(ExtendedTestResult(log)) self.assertThat( - log, MatchesEvents( - ('startTest', test), - ('addError', test, { - 'traceback': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - 'traceback-1': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - }), - ('stopTest', test), - ) + log, + MatchesEvents( + ("startTest", test), + ( + "addError", + test, + { + "traceback": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + "traceback-1": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + }, + ), + ("stopTest", test), + ), ) def test_multipleErrorsCoreAndCleanupReported(self): @@ -964,43 +1089,59 @@ def test_multipleErrorsCoreAndCleanupReported(self): # startTest inserted. test = make_test_case( self.getUniqueString(), - test_body=lambda _: raise_( - RuntimeError('Deliberately broken test')), + test_body=lambda _: raise_(RuntimeError("Deliberately broken test")), cleanups=[ - lambda _: 1/0, - lambda _: 1/0, - ] + lambda _: 1 / 0, + lambda _: 1 / 0, + ], ) log = [] test.run(ExtendedTestResult(log)) self.assertThat( - log, MatchesEvents( - ('startTest', test), - ('addError', test, { - 'traceback': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'RuntimeError: Deliberately broken test', - ])), - 'traceback-1': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - 'traceback-2': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - }), - ('stopTest', test), - ) + log, + MatchesEvents( + ("startTest", test), + ( + "addError", + test, + { + "traceback": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "RuntimeError: Deliberately broken test", + ] + ) + ), + "traceback-1": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + "traceback-2": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + }, + ), + ("stopTest", test), + ), ) class TestRunTestUsage(TestCase): - def test_last_resort_in_place(self): class TestBase(TestCase): def test_base_exception(self): raise SystemExit(0) + result = ExtendedTestResult() test = TestBase("test_base_exception") self.assertRaises(SystemExit, test.run, result) @@ -1008,7 +1149,6 @@ def test_base_exception(self): class TestWithDetails(TestCase): - run_test_with = FullStackRunTest def assertDetailsProvided(self, case, expected_outcome, expected_keys): @@ -1022,22 +1162,20 @@ def assertDetailsProvided(self, case, expected_outcome, expected_keys): case.run(result) case = result._events[0][1] expected = [ - ('startTest', case), + ("startTest", case), (expected_outcome, case), - ('stopTest', case), - ] + ("stopTest", case), + ] self.assertEqual(3, len(result._events)) self.assertEqual(expected[0], result._events[0]) self.assertEqual(expected[1], result._events[1][0:2]) # Checking the TB is right is rather tricky. doctest line matching # would help, but 'meh'. - self.assertEqual(sorted(expected_keys), - sorted(result._events[1][2].keys())) + self.assertEqual(sorted(expected_keys), sorted(result._events[1][2].keys())) self.assertEqual(expected[-1], result._events[-1]) def get_content(self): - return content.Content( - content.ContentType("text", "foo"), lambda: [_b('foo')]) + return content.Content(content.ContentType("text", "foo"), lambda: [_b("foo")]) class TestExpectedFailure(TestWithDetails): @@ -1049,7 +1187,8 @@ def make_unexpected_case(self): class Case(TestCase): def test(self): raise testcase._UnexpectedSuccess - case = Case('test') + + case = Case("test") return case def test_raising__UnexpectedSuccess_py27(self): @@ -1057,52 +1196,60 @@ def test_raising__UnexpectedSuccess_py27(self): result = Python27TestResult() case.run(result) case = result._events[0][1] - self.assertEqual([ - ('startTest', case), - ('addUnexpectedSuccess', case), - ('stopTest', case), - ], result._events) + self.assertEqual( + [ + ("startTest", case), + ("addUnexpectedSuccess", case), + ("stopTest", case), + ], + result._events, + ) def test_raising__UnexpectedSuccess_extended(self): case = self.make_unexpected_case() result = ExtendedTestResult() case.run(result) case = result._events[0][1] - self.assertEqual([ - ('startTest', case), - ('addUnexpectedSuccess', case, {}), - ('stopTest', case), - ], result._events) + self.assertEqual( + [ + ("startTest", case), + ("addUnexpectedSuccess", case, {}), + ("stopTest", case), + ], + result._events, + ) def make_xfail_case_xfails(self): content = self.get_content() + class Case(TestCase): def test(self): self.addDetail("foo", content) - self.expectFailure("we are sad", self.assertEqual, - 1, 0) - case = Case('test') + self.expectFailure("we are sad", self.assertEqual, 1, 0) + + case = Case("test") return case def make_xfail_case_succeeds(self): content = self.get_content() + class Case(TestCase): def test(self): self.addDetail("foo", content) - self.expectFailure("we are sad", self.assertEqual, - 1, 1) - case = Case('test') + self.expectFailure("we are sad", self.assertEqual, 1, 1) + + case = Case("test") return case def test_expectFailure_KnownFailure_extended(self): case = self.make_xfail_case_xfails() - self.assertDetailsProvided(case, "addExpectedFailure", - ["foo", "traceback", "reason"]) + self.assertDetailsProvided( + case, "addExpectedFailure", ["foo", "traceback", "reason"] + ) def test_expectFailure_KnownFailure_unexpected_success(self): case = self.make_xfail_case_succeeds() - self.assertDetailsProvided(case, "addUnexpectedSuccess", - ["foo", "reason"]) + self.assertDetailsProvided(case, "addUnexpectedSuccess", ["foo", "reason"]) def test_unittest_expectedFailure_decorator_works_with_failure(self): class ReferenceTest(TestCase): @@ -1110,7 +1257,7 @@ class ReferenceTest(TestCase): def test_fails_expectedly(self): self.assertEqual(1, 0) - test = ReferenceTest('test_fails_expectedly') + test = ReferenceTest("test_fails_expectedly") result = test.run() self.assertEqual(True, result.wasSuccessful()) @@ -1120,7 +1267,7 @@ class ReferenceTest(TestCase): def test_passes_unexpectedly(self): self.assertEqual(1, 1) - test = ReferenceTest('test_passes_unexpectedly') + test = ReferenceTest("test_passes_unexpectedly") result = test.run() self.assertEqual(False, result.wasSuccessful()) @@ -1142,17 +1289,17 @@ def test_getUniqueString(self): # getUniqueString returns the current test id followed by a unique # integer. name_one = self.getUniqueString() - self.assertEqual('%s-%d' % (self.id(), 1), name_one) + self.assertEqual("%s-%d" % (self.id(), 1), name_one) name_two = self.getUniqueString() - self.assertEqual('%s-%d' % (self.id(), 2), name_two) + self.assertEqual("%s-%d" % (self.id(), 2), name_two) def test_getUniqueString_prefix(self): # If getUniqueString is given an argument, it uses that argument as # the prefix of the unique string, rather than the test id. - name_one = self.getUniqueString('foo') - self.assertThat(name_one, Equals('foo-1')) - name_two = self.getUniqueString('bar') - self.assertThat(name_two, Equals('bar-2')) + name_one = self.getUniqueString("foo") + self.assertThat(name_one, Equals("foo-1")) + name_two = self.getUniqueString("bar") + self.assertThat(name_two, Equals("bar-2")) def test_unique_text_generator(self): # unique_text_generator yields the prefix's id followed by unique @@ -1160,12 +1307,10 @@ def test_unique_text_generator(self): prefix = self.getUniqueString() unique_text_generator = testcase.unique_text_generator(prefix) first_result = next(unique_text_generator) - self.assertEqual('{}-{}'.format(prefix, '\u1e00'), - first_result) + self.assertEqual("{}-{}".format(prefix, "\u1e00"), first_result) # The next value yielded by unique_text_generator is different. second_result = next(unique_text_generator) - self.assertEqual('{}-{}'.format(prefix, '\u1e01'), - second_result) + self.assertEqual("{}-{}".format(prefix, "\u1e01"), second_result) def test_mods(self): # given a number and max, generate a list that's the mods. @@ -1187,17 +1332,18 @@ def test_mods(self): def test_unique_text(self): self.assertEqual( - '\u1e00', - testcase._unique_text(base_cp=0x1e00, cp_range=5, index=0)) + "\u1e00", testcase._unique_text(base_cp=0x1E00, cp_range=5, index=0) + ) self.assertEqual( - '\u1e01', - testcase._unique_text(base_cp=0x1e00, cp_range=5, index=1)) + "\u1e01", testcase._unique_text(base_cp=0x1E00, cp_range=5, index=1) + ) self.assertEqual( - '\u1e00\u1e01', - testcase._unique_text(base_cp=0x1e00, cp_range=5, index=5)) + "\u1e00\u1e01", testcase._unique_text(base_cp=0x1E00, cp_range=5, index=5) + ) self.assertEqual( - '\u1e03\u1e02\u1e01', - testcase._unique_text(base_cp=0x1e00, cp_range=5, index=38)) + "\u1e03\u1e02\u1e01", + testcase._unique_text(base_cp=0x1E00, cp_range=5, index=38), + ) class TestCloneTestWithNewId(TestCase): @@ -1209,29 +1355,31 @@ def test_clone_test_with_new_id(self): class FooTestCase(TestCase): def test_foo(self): pass - test = FooTestCase('test_foo') + + test = FooTestCase("test_foo") oldName = test.id() newName = self.getUniqueString() newTest = clone_test_with_new_id(test, newName) self.assertEqual(newName, newTest.id()) - self.assertEqual(oldName, test.id(), - "the original test instance should be unchanged.") + self.assertEqual( + oldName, test.id(), "the original test instance should be unchanged." + ) def test_cloned_testcase_does_not_share_details(self): """A cloned TestCase does not share the details dict.""" + class Test(TestCase): def test_foo(self): - self.addDetail( - 'foo', content.Content('text/plain', lambda: 'foo')) - orig_test = Test('test_foo') + self.addDetail("foo", content.Content("text/plain", lambda: "foo")) + + orig_test = Test("test_foo") cloned_test = clone_test_with_new_id(orig_test, self.getUniqueString()) orig_test.run(unittest.TestResult()) - self.assertEqual('foo', orig_test.getDetails()['foo'].iter_bytes()) - self.assertEqual(None, cloned_test.getDetails().get('foo')) + self.assertEqual("foo", orig_test.getDetails()["foo"].iter_bytes()) + self.assertEqual(None, cloned_test.getDetails().get("foo")) class TestDetailsProvided(TestWithDetails): - run_test_with = FullStackRunTest def test_addDetail(self): @@ -1244,25 +1392,25 @@ def test_addError(self): class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) - 1/0 - self.assertDetailsProvided(Case("test"), "addError", - ["foo", "traceback"]) + 1 / 0 + + self.assertDetailsProvided(Case("test"), "addError", ["foo", "traceback"]) def test_addFailure(self): class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) - self.fail('yo') - self.assertDetailsProvided(Case("test"), "addFailure", - ["foo", "traceback"]) + self.fail("yo") + + self.assertDetailsProvided(Case("test"), "addFailure", ["foo", "traceback"]) def test_addSkip(self): class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) - self.skipTest('yo') - self.assertDetailsProvided(Case("test"), "addSkip", - ["foo", "reason"]) + self.skipTest("yo") + + self.assertDetailsProvided(Case("test"), "addSkip", ["foo", "reason"]) def test_addSkip_different_exception(self): # No traceback is included if the skip exception is changed and a skip @@ -1272,144 +1420,178 @@ class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) - this.skipTest('yo') + this.skipTest("yo") + self.assertDetailsProvided(Case("test"), "addSkip", ["foo", "reason"]) def test_addSucccess(self): class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) - self.assertDetailsProvided(Case("test"), "addSuccess", - ["foo"]) + + self.assertDetailsProvided(Case("test"), "addSuccess", ["foo"]) def test_addUnexpectedSuccess(self): class Case(TestCase): def test(this): this.addDetail("foo", self.get_content()) raise testcase._UnexpectedSuccess() - self.assertDetailsProvided(Case("test"), "addUnexpectedSuccess", - ["foo"]) + + self.assertDetailsProvided(Case("test"), "addUnexpectedSuccess", ["foo"]) def test_addDetails_from_Mismatch(self): content = self.get_content() + class Mismatch: def describe(self): return "Mismatch" + def get_details(self): return {"foo": content} + class Matcher: def match(self, thing): return Mismatch() + def __str__(self): return "a description" + class Case(TestCase): def test(self): self.assertThat("foo", Matcher()) - self.assertDetailsProvided(Case("test"), "addFailure", - ["foo", "traceback"]) + + self.assertDetailsProvided(Case("test"), "addFailure", ["foo", "traceback"]) def test_multiple_addDetails_from_Mismatch(self): content = self.get_content() + class Mismatch: def describe(self): return "Mismatch" + def get_details(self): return {"foo": content, "bar": content} + class Matcher: def match(self, thing): return Mismatch() + def __str__(self): return "a description" + class Case(TestCase): def test(self): self.assertThat("foo", Matcher()) - self.assertDetailsProvided(Case("test"), "addFailure", - ["bar", "foo", "traceback"]) + + self.assertDetailsProvided( + Case("test"), "addFailure", ["bar", "foo", "traceback"] + ) def test_addDetails_with_same_name_as_key_from_get_details(self): content = self.get_content() + class Mismatch: def describe(self): return "Mismatch" + def get_details(self): return {"foo": content} + class Matcher: def match(self, thing): return Mismatch() + def __str__(self): return "a description" + class Case(TestCase): def test(self): self.addDetail("foo", content) self.assertThat("foo", Matcher()) - self.assertDetailsProvided(Case("test"), "addFailure", - ["foo", "foo-1", "traceback"]) + + self.assertDetailsProvided( + Case("test"), "addFailure", ["foo", "foo-1", "traceback"] + ) def test_addDetailUniqueName_works(self): content = self.get_content() + class Case(TestCase): def test(self): self.addDetailUniqueName("foo", content) self.addDetailUniqueName("foo", content) - self.assertDetailsProvided(Case("test"), "addSuccess", - ["foo", "foo-1"]) + self.assertDetailsProvided(Case("test"), "addSuccess", ["foo", "foo-1"]) -class TestSetupTearDown(TestCase): +class TestSetupTearDown(TestCase): run_test_with = FullStackRunTest def test_setUpCalledTwice(self): class CallsTooMuch(TestCase): def test_method(self): self.setUp() + result = unittest.TestResult() - CallsTooMuch('test_method').run(result) + CallsTooMuch("test_method").run(result) self.assertThat(result.errors, HasLength(1)) - self.assertThat(result.errors[0][1], + self.assertThat( + result.errors[0][1], DocTestMatches( - "...ValueError...File...testtools/tests/test_testcase.py...", - ELLIPSIS)) + "...ValueError...File...testtools/tests/test_testcase.py...", ELLIPSIS + ), + ) def test_setUpNotCalled(self): class DoesnotcallsetUp(TestCase): def setUp(self): pass + def test_method(self): pass + result = unittest.TestResult() - DoesnotcallsetUp('test_method').run(result) + DoesnotcallsetUp("test_method").run(result) self.assertThat(result.errors, HasLength(1)) - self.assertThat(result.errors[0][1], + self.assertThat( + result.errors[0][1], DocTestMatches( - "...ValueError...File...testtools/tests/test_testcase.py...", - ELLIPSIS)) + "...ValueError...File...testtools/tests/test_testcase.py...", ELLIPSIS + ), + ) def test_tearDownCalledTwice(self): class CallsTooMuch(TestCase): def test_method(self): self.tearDown() + result = unittest.TestResult() - CallsTooMuch('test_method').run(result) + CallsTooMuch("test_method").run(result) self.assertThat(result.errors, HasLength(1)) - self.assertThat(result.errors[0][1], + self.assertThat( + result.errors[0][1], DocTestMatches( - "...ValueError...File...testtools/tests/test_testcase.py...", - ELLIPSIS)) + "...ValueError...File...testtools/tests/test_testcase.py...", ELLIPSIS + ), + ) def test_tearDownNotCalled(self): class DoesnotcalltearDown(TestCase): def test_method(self): pass + def tearDown(self): pass + result = unittest.TestResult() - DoesnotcalltearDown('test_method').run(result) + DoesnotcalltearDown("test_method").run(result) self.assertThat(result.errors, HasLength(1)) - self.assertThat(result.errors[0][1], + self.assertThat( + result.errors[0][1], DocTestMatches( - "...ValueError...File...testtools/tests/test_testcase.py...", - ELLIPSIS)) + "...ValueError...File...testtools/tests/test_testcase.py...", ELLIPSIS + ), + ) class TestRunTwiceDeterminstic(TestCase): @@ -1451,10 +1633,8 @@ def test_runTwice(self): test.run(first_result) second_result = ExtendedTestResult() test.run(second_result) - self.expectThat( - first_result._events, self.expected_first_result) - self.assertThat( - second_result._events, self.expected_second_result) + self.expectThat(first_result._events, self.expected_first_result) + self.assertThat(second_result._events, self.expected_second_result) class TestSkipping(TestCase): @@ -1465,24 +1645,25 @@ class TestSkipping(TestCase): def test_skip_causes_skipException(self): self.assertThat( lambda: self.skipTest("Skip this test"), - Raises(MatchesException(self.skipException))) + Raises(MatchesException(self.skipException)), + ) def test_can_use_skipTest(self): self.assertThat( lambda: self.skipTest("Skip this test"), - Raises(MatchesException(self.skipException))) + Raises(MatchesException(self.skipException)), + ) def test_skip_without_reason_works(self): class Test(TestCase): def test(self): raise self.skipException() + case = Test("test") result = ExtendedTestResult() case.run(result) - self.assertEqual('addSkip', result._events[1][0]) - self.assertEqual( - 'no reason given.', - result._events[1][2]['reason'].as_text()) + self.assertEqual("addSkip", result._events[1][0]) + self.assertEqual("no reason given.", result._events[1][2]["reason"].as_text()) def test_skipException_in_setup_calls_result_addSkip(self): class TestThatRaisesInSetUp(TestCase): @@ -1492,31 +1673,39 @@ def setUp(self): def test_that_passes(self): pass + calls = [] result = LoggingResult(calls) test = TestThatRaisesInSetUp("test_that_passes") test.run(result) case = result._events[0][1] self.assertEqual( - [('startTest', case), - ('addSkip', case, "skipping this test"), - ('stopTest', case)], - calls) + [ + ("startTest", case), + ("addSkip", case, "skipping this test"), + ("stopTest", case), + ], + calls, + ) def test_skipException_in_test_method_calls_result_addSkip(self): class SkippingTest(TestCase): def test_that_raises_skipException(self): self.skipTest("skipping this test") + events = [] result = Python27TestResult(events) test = SkippingTest("test_that_raises_skipException") test.run(result) case = result._events[0][1] self.assertEqual( - [('startTest', case), - ('addSkip', case, "skipping this test"), - ('stopTest', case)], - events) + [ + ("startTest", case), + ("addSkip", case, "skipping this test"), + ("stopTest", case), + ], + events, + ) def test_different_skipException_in_test_method_calls_result_addSkip(self): class SkippingTest(TestCase): @@ -1531,13 +1720,15 @@ def test_that_raises_skipException(self): test.run(result) case = result._events[0][1] self.assertEqual( - [('startTest', case), - ('addSkip', case, "skipping this test"), - ('stopTest', case)], - events) + [ + ("startTest", case), + ("addSkip", case, "skipping this test"), + ("stopTest", case), + ], + events, + ) def test_skip__in_setup_with_old_result_object_calls_addSuccess(self): - class SkippingTest(TestCase): def setUp(self): TestCase.setUp(self) @@ -1550,54 +1741,58 @@ def test_that_raises_skipException(self): result = Python26TestResult(events) test = SkippingTest("test_that_raises_skipException") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skip_with_old_result_object_calls_addError(self): class SkippingTest(TestCase): def test_that_raises_skipException(self): raise self.skipException("skipping this test") + events = [] result = Python26TestResult(events) test = SkippingTest("test_that_raises_skipException") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skip_decorator(self): class SkippingTest(TestCase): @skip("skipping this test") def test_that_is_decorated_with_skip(self): self.fail() + events = [] result = Python26TestResult(events) test = SkippingTest("test_that_is_decorated_with_skip") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skipIf_decorator(self): class SkippingTest(TestCase): @skipIf(True, "skipping this test") def test_that_is_decorated_with_skipIf(self): self.fail() + events = [] result = Python26TestResult(events) test = SkippingTest("test_that_is_decorated_with_skipIf") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skipUnless_decorator(self): class SkippingTest(TestCase): @skipUnless(False, "skipping this test") def test_that_is_decorated_with_skipUnless(self): self.fail() + events = [] result = Python26TestResult(events) test = SkippingTest("test_that_is_decorated_with_skipUnless") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skip_decorator_shared(self): def shared(testcase): - testcase.fail('nope') + testcase.fail("nope") class SkippingTest(TestCase): test_skip = skipIf(True, "skipping this test")(shared) @@ -1609,59 +1804,61 @@ class NotSkippingTest(TestCase): result = Python26TestResult(events) test = SkippingTest("test_skip") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) events2 = [] result2 = Python26TestResult(events2) test2 = NotSkippingTest("test_no_skip") test2.run(result2) - self.assertEqual('addFailure', events2[1][0]) + self.assertEqual("addFailure", events2[1][0]) def test_skip_class_decorator(self): @skip("skipping this testcase") class SkippingTest(TestCase): def test_that_is_decorated_with_skip(self): self.fail() + events = [] result = Python26TestResult(events) try: test = SkippingTest("test_that_is_decorated_with_skip") except TestSkipped: - self.fail('TestSkipped raised') + self.fail("TestSkipped raised") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skipIf_class_decorator(self): @skipIf(True, "skipping this testcase") class SkippingTest(TestCase): def test_that_is_decorated_with_skipIf(self): self.fail() + events = [] result = Python26TestResult(events) try: test = SkippingTest("test_that_is_decorated_with_skipIf") except TestSkipped: - self.fail('TestSkipped raised') + self.fail("TestSkipped raised") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def test_skipUnless_class_decorator(self): @skipUnless(False, "skipping this testcase") class SkippingTest(TestCase): def test_that_is_decorated_with_skipUnless(self): self.fail() + events = [] result = Python26TestResult(events) try: test = SkippingTest("test_that_is_decorated_with_skipUnless") except TestSkipped: - self.fail('TestSkipped raised') + self.fail("TestSkipped raised") test.run(result) - self.assertEqual('addSuccess', events[1][0]) + self.assertEqual("addSuccess", events[1][0]) def check_skip_decorator_does_not_run_setup(self, decorator, reason): class SkippingTest(TestCase): - setup_ran = False def setUp(self): @@ -1673,13 +1870,12 @@ def setUp(self): def test_skipped(self): self.fail() - test = SkippingTest('test_skipped') + test = SkippingTest("test_skipped") self.check_test_does_not_run_setup(test, reason) # Use the decorator passed to us: @decorator class SkippingTestCase(TestCase): - setup_ran = False def setUp(self): @@ -1690,9 +1886,9 @@ def test_skipped(self): self.fail() try: - test = SkippingTestCase('test_skipped') + test = SkippingTestCase("test_skipped") except TestSkipped: - self.fail('TestSkipped raised') + self.fail("TestSkipped raised") self.check_test_does_not_run_setup(test, reason) def check_test_does_not_run_setup(self, test, reason): @@ -1703,85 +1899,77 @@ def check_test_does_not_run_setup(self, test, reason): def test_testtools_skip_decorator_does_not_run_setUp(self): reason = self.getUniqueString() - self.check_skip_decorator_does_not_run_setup( - skip(reason), - reason - ) + self.check_skip_decorator_does_not_run_setup(skip(reason), reason) def test_testtools_skipIf_decorator_does_not_run_setUp(self): reason = self.getUniqueString() - self.check_skip_decorator_does_not_run_setup( - skipIf(True, reason), - reason - ) + self.check_skip_decorator_does_not_run_setup(skipIf(True, reason), reason) def test_testtools_skipUnless_decorator_does_not_run_setUp(self): reason = self.getUniqueString() - self.check_skip_decorator_does_not_run_setup( - skipUnless(False, reason), - reason - ) + self.check_skip_decorator_does_not_run_setup(skipUnless(False, reason), reason) def test_unittest_skip_decorator_does_not_run_setUp(self): reason = self.getUniqueString() - self.check_skip_decorator_does_not_run_setup( - unittest.skip(reason), - reason - ) + self.check_skip_decorator_does_not_run_setup(unittest.skip(reason), reason) def test_unittest_skipIf_decorator_does_not_run_setUp(self): reason = self.getUniqueString() self.check_skip_decorator_does_not_run_setup( - unittest.skipIf(True, reason), - reason + unittest.skipIf(True, reason), reason ) def test_unittest_skipUnless_decorator_does_not_run_setUp(self): reason = self.getUniqueString() self.check_skip_decorator_does_not_run_setup( - unittest.skipUnless(False, reason), - reason + unittest.skipUnless(False, reason), reason ) class TestOnException(TestCase): - run_test_with = FullStackRunTest def test_default_works(self): events = [] + class Case(TestCase): def method(self): self.onException(an_exc_info) events.append(True) + case = Case("method") case.run() self.assertThat(events, Equals([True])) def test_added_handler_works(self): events = [] + class Case(TestCase): def method(self): self.addOnException(events.append) self.onException(an_exc_info) + case = Case("method") case.run() self.assertThat(events, Equals([an_exc_info])) def test_handler_that_raises_is_not_caught(self): events = [] + class Case(TestCase): def method(self): self.addOnException(events.index) - self.assertThat(lambda: self.onException(an_exc_info), - Raises(MatchesException(ValueError))) + self.assertThat( + lambda: self.onException(an_exc_info), + Raises(MatchesException(ValueError)), + ) + case = Case("method") case.run() self.assertThat(events, Equals([])) class TestPatchSupport(TestCase): - run_tests_with = FullStackRunTest class Case(TestCase): @@ -1794,88 +1982,95 @@ def run_test(self, test_body): :return: Whatever ``test_body`` returns. """ log = [] + def wrapper(case): log.append(test_body(case)) + case = make_test_case(self.getUniqueString(), test_body=wrapper) case.run() return log[0] def test_patch(self): # TestCase.patch masks obj.attribute with the new value. - self.foo = 'original' + self.foo = "original" + def test_body(case): - case.patch(self, 'foo', 'patched') + case.patch(self, "foo", "patched") return self.foo result = self.run_test(test_body) - self.assertThat(result, Equals('patched')) + self.assertThat(result, Equals("patched")) def test_patch_restored_after_run(self): # TestCase.patch masks obj.attribute with the new value, but restores # the original value after the test is finished. - self.foo = 'original' - self.run_test(lambda case: case.patch(self, 'foo', 'patched')) - self.assertThat(self.foo, Equals('original')) + self.foo = "original" + self.run_test(lambda case: case.patch(self, "foo", "patched")) + self.assertThat(self.foo, Equals("original")) def test_successive_patches_apply(self): # TestCase.patch can be called multiple times per test. Each time you # call it, it overrides the original value. - self.foo = 'original' + self.foo = "original" + def test_body(case): - case.patch(self, 'foo', 'patched') - case.patch(self, 'foo', 'second') + case.patch(self, "foo", "patched") + case.patch(self, "foo", "second") return self.foo result = self.run_test(test_body) - self.assertThat(result, Equals('second')) + self.assertThat(result, Equals("second")) def test_successive_patches_restored_after_run(self): # TestCase.patch restores the original value, no matter how many times # it was called. - self.foo = 'original' + self.foo = "original" + def test_body(case): - case.patch(self, 'foo', 'patched') - case.patch(self, 'foo', 'second') + case.patch(self, "foo", "patched") + case.patch(self, "foo", "second") return self.foo self.run_test(test_body) - self.assertThat(self.foo, Equals('original')) + self.assertThat(self.foo, Equals("original")) def test_patch_nonexistent_attribute(self): # TestCase.patch can be used to patch a non-existent attribute. def test_body(case): - case.patch(self, 'doesntexist', 'patched') + case.patch(self, "doesntexist", "patched") return self.doesntexist result = self.run_test(test_body) - self.assertThat(result, Equals('patched')) + self.assertThat(result, Equals("patched")) def test_restore_nonexistent_attribute(self): # TestCase.patch can be used to patch a non-existent attribute, after # the test run, the attribute is then removed from the object. def test_body(case): - case.patch(self, 'doesntexist', 'patched') + case.patch(self, "doesntexist", "patched") return self.doesntexist self.run_test(test_body) marker = object() - value = getattr(self, 'doesntexist', marker) + value = getattr(self, "doesntexist", marker) self.assertIs(marker, value) class TestTestCaseSuper(TestCase): - run_test_with = FullStackRunTest def test_setup_uses_super(self): class OtherBaseCase(unittest.TestCase): setup_called = False + def setUp(self): self.setup_called = True super().setUp() + class OurCase(TestCase, OtherBaseCase): def runTest(self): pass + test = OurCase() test.setUp() test.tearDown() @@ -1884,12 +2079,15 @@ def runTest(self): def test_teardown_uses_super(self): class OtherBaseCase(unittest.TestCase): teardown_called = False + def tearDown(self): self.teardown_called = True super().tearDown() + class OurCase(TestCase, OtherBaseCase): def runTest(self): pass + test = OurCase() test.setUp() test.tearDown() @@ -1897,12 +2095,12 @@ def runTest(self): class TestNullary(TestCase): - def test_repr(self): # The repr() of nullary is the same as the repr() of the wrapped # function. def foo(): pass + wrapped = Nullary(foo) self.assertEqual(repr(wrapped), repr(foo)) @@ -1910,11 +2108,13 @@ def test_called_with_arguments(self): # The function is called with the arguments given to Nullary's # constructor. line = [] + def foo(*args, **kwargs): line.append((args, kwargs)) + wrapped = Nullary(foo, 1, 2, a="b") wrapped() - self.assertEqual(line, [((1, 2), {'a': 'b'})]) + self.assertEqual(line, [((1, 2), {"a": "b"})]) def test_returns_wrapped(self): # Calling Nullary returns whatever the function returns. @@ -1924,133 +2124,147 @@ def test_returns_wrapped(self): def test_raises(self): # If the function raises, so does Nullary when called. - wrapped = Nullary(lambda: 1/0) + wrapped = Nullary(lambda: 1 / 0) self.assertRaises(ZeroDivisionError, wrapped) class Attributes(WithAttributes, TestCase): - @attr('foo') + @attr("foo") def simple(self): pass # Not sorted here, forward or backwards. - @attr('foo', 'quux', 'bar') + @attr("foo", "quux", "bar") def many(self): pass # Not sorted here, forward or backwards. - @attr('bar') - @attr('quux') - @attr('foo') + @attr("bar") + @attr("quux") + @attr("foo") def decorated(self): pass class TestAttributes(TestCase): - def test_simple_attr(self): # Adding an attr to a test changes its id(). - case = Attributes('simple') + case = Attributes("simple") self.assertEqual( - 'testtools.tests.test_testcase.Attributes.simple[foo]', - case.id()) + "testtools.tests.test_testcase.Attributes.simple[foo]", case.id() + ) def test_multiple_attributes(self): - case = Attributes('many') + case = Attributes("many") self.assertEqual( - 'testtools.tests.test_testcase.Attributes.many[bar,foo,quux]', - case.id()) + "testtools.tests.test_testcase.Attributes.many[bar,foo,quux]", case.id() + ) def test_multiple_attr_decorators(self): - case = Attributes('decorated') + case = Attributes("decorated") self.assertEqual( - 'testtools.tests.test_testcase.Attributes.decorated[bar,foo,quux]', - case.id()) + "testtools.tests.test_testcase.Attributes.decorated[bar,foo,quux]", + case.id(), + ) class TestDecorateTestCaseResult(TestCase): - def setUp(self): super().setUp() self.log = [] def make_result(self, result): - self.log.append(('result', result)) + self.log.append(("result", result)) return LoggingResult(self.log) def test___call__(self): - case = DecorateTestCaseResult(PlaceHolder('foo'), self.make_result) + case = DecorateTestCaseResult(PlaceHolder("foo"), self.make_result) case(None) - case('something') - self.assertEqual([('result', None), - ('tags', set(), set()), - ('startTest', case.decorated), - ('addSuccess', case.decorated), - ('stopTest', case.decorated), - ('tags', set(), set()), - ('result', 'something'), - ('tags', set(), set()), - ('startTest', case.decorated), - ('addSuccess', case.decorated), - ('stopTest', case.decorated), - ('tags', set(), set()) - ], self.log) + case("something") + self.assertEqual( + [ + ("result", None), + ("tags", set(), set()), + ("startTest", case.decorated), + ("addSuccess", case.decorated), + ("stopTest", case.decorated), + ("tags", set(), set()), + ("result", "something"), + ("tags", set(), set()), + ("startTest", case.decorated), + ("addSuccess", case.decorated), + ("stopTest", case.decorated), + ("tags", set(), set()), + ], + self.log, + ) def test_run(self): - case = DecorateTestCaseResult(PlaceHolder('foo'), self.make_result) + case = DecorateTestCaseResult(PlaceHolder("foo"), self.make_result) case.run(None) - case.run('something') - self.assertEqual([('result', None), - ('tags', set(), set()), - ('startTest', case.decorated), - ('addSuccess', case.decorated), - ('stopTest', case.decorated), - ('tags', set(), set()), - ('result', 'something'), - ('tags', set(), set()), - ('startTest', case.decorated), - ('addSuccess', case.decorated), - ('stopTest', case.decorated), - ('tags', set(), set()) - ], self.log) + case.run("something") + self.assertEqual( + [ + ("result", None), + ("tags", set(), set()), + ("startTest", case.decorated), + ("addSuccess", case.decorated), + ("stopTest", case.decorated), + ("tags", set(), set()), + ("result", "something"), + ("tags", set(), set()), + ("startTest", case.decorated), + ("addSuccess", case.decorated), + ("stopTest", case.decorated), + ("tags", set(), set()), + ], + self.log, + ) def test_before_after_hooks(self): - case = DecorateTestCaseResult(PlaceHolder('foo'), self.make_result, - before_run=lambda result: self.log.append('before'), - after_run=lambda result: self.log.append('after')) + case = DecorateTestCaseResult( + PlaceHolder("foo"), + self.make_result, + before_run=lambda result: self.log.append("before"), + after_run=lambda result: self.log.append("after"), + ) case.run(None) case(None) - self.assertEqual([ - ('result', None), - 'before', - ('tags', set(), set()), - ('startTest', case.decorated), - ('addSuccess', case.decorated), - ('stopTest', case.decorated), - ('tags', set(), set()), - 'after', - ('result', None), - 'before', - ('tags', set(), set()), - ('startTest', case.decorated), - ('addSuccess', case.decorated), - ('stopTest', case.decorated), - ('tags', set(), set()), - 'after', - ], self.log) + self.assertEqual( + [ + ("result", None), + "before", + ("tags", set(), set()), + ("startTest", case.decorated), + ("addSuccess", case.decorated), + ("stopTest", case.decorated), + ("tags", set(), set()), + "after", + ("result", None), + "before", + ("tags", set(), set()), + ("startTest", case.decorated), + ("addSuccess", case.decorated), + ("stopTest", case.decorated), + ("tags", set(), set()), + "after", + ], + self.log, + ) def test_other_attribute(self): - orig = PlaceHolder('foo') - orig.thing = 'fred' + orig = PlaceHolder("foo") + orig.thing = "fred" case = DecorateTestCaseResult(orig, self.make_result) - self.assertEqual('fred', case.thing) - self.assertRaises(AttributeError, getattr, case, 'other') - case.other = 'barbara' - self.assertEqual('barbara', orig.other) + self.assertEqual("fred", case.thing) + self.assertRaises(AttributeError, getattr, case, "other") + case.other = "barbara" + self.assertEqual("barbara", orig.other) del case.thing - self.assertRaises(AttributeError, getattr, orig, 'thing') + self.assertRaises(AttributeError, getattr, orig, "thing") def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_testresult.py b/testtools/tests/test_testresult.py index 59fabf4a..9790ad74 100644 --- a/testtools/tests/test_testresult.py +++ b/testtools/tests/test_testresult.py @@ -44,17 +44,17 @@ ThreadsafeForwardingResult, TimestampingStreamResult, testresult, - ) +) from testtools.compat import ( _b, _get_exception_encoding, - ) +) from testtools.content import ( Content, content_from_stream, text_content, TracebackContent, - ) +) from testtools.content_type import ContentType, UTF8_TEXT from testtools.helpers import try_import from testtools.matchers import ( @@ -67,34 +67,35 @@ MatchesException, MatchesRegex, Raises, - ) +) from testtools.tests.helpers import ( an_exc_info, FullStackRunTest, LoggingResult, run_with_stack_hidden, - ) +) from testtools.testresult.doubles import ( Python26TestResult, Python27TestResult, ExtendedTestResult, StreamResult as LoggingStreamResult, TwistedTestResult, - ) +) from testtools.testresult.real import ( _details_to_str, _merge_tags, utc, - ) +) -testresources = try_import('testresources') +testresources = try_import("testresources") def make_erroring_test(): class Test(TestCase): def error(self): a = 1 # noqa: F841 - 1/0 + 1 / 0 + return Test("error") @@ -102,6 +103,7 @@ def make_failing_test(): class Test(TestCase): def failed(self): self.fail("yo!") + return Test("failed") @@ -109,6 +111,7 @@ def make_mismatching_test(): class Test(TestCase): def mismatch(self): self.assertEqual(1, 2) + return Test("mismatch") @@ -116,6 +119,7 @@ def make_unexpectedly_successful_test(): class Test(TestCase): def succeeded(self): self.expectFailure("yo!", lambda: None) + return Test("succeeded") @@ -123,6 +127,7 @@ def make_test(): class Test(TestCase): def test(self): pass + return Test("test") @@ -149,7 +154,6 @@ def test_stop_sets_shouldStop(self): class Python26Contract(TestControlContract): - def test_fresh_result_is_successful(self): # A result is considered successful before any tests are run. result = self.makeResult() @@ -181,7 +185,6 @@ def test_addSuccess_is_success(self): class Python27Contract(Python26Contract): - def test_addExpectedFailure(self): # Calling addExpectedFailure(test, exc_info) completes ok. result = self.makeResult() @@ -233,12 +236,15 @@ def test_startStopTestRun(self): def test_failfast(self): result = self.makeResult() result.failfast = True + class Failing(TestCase): def test_a(self): - self.fail('a') + self.fail("a") + def test_b(self): - self.fail('b') - TestSuite([Failing('test_a'), Failing('test_b')]).run(result) + self.fail("b") + + TestSuite([Failing("test_a"), Failing("test_b")]).run(result) self.assertEqual(1, result.testsRun) @@ -259,22 +265,22 @@ def test_adding_tags(self): # 'current_tags'. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) - self.assertEqual({'foo'}, result.current_tags) + result.tags({"foo"}, set()) + self.assertEqual({"foo"}, result.current_tags) def test_removing_tags(self): # Tags are removed using 'tags'. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) - result.tags(set(), {'foo'}) + result.tags({"foo"}, set()) + result.tags(set(), {"foo"}) self.assertEqual(set(), result.current_tags) def test_startTestRun_resets_tags(self): # startTestRun makes a new test run, and thus clears all the tags. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.startTestRun() self.assertEqual(set(), result.current_tags) @@ -282,42 +288,42 @@ def test_add_tags_within_test(self): # Tags can be added after a test has run. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.startTest(self) - result.tags({'bar'}, set()) - self.assertEqual({'foo', 'bar'}, result.current_tags) + result.tags({"bar"}, set()) + self.assertEqual({"foo", "bar"}, result.current_tags) def test_tags_added_in_test_are_reverted(self): # Tags added during a test run are then reverted once that test has # finished. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.startTest(self) - result.tags({'bar'}, set()) + result.tags({"bar"}, set()) result.addSuccess(self) result.stopTest(self) - self.assertEqual({'foo'}, result.current_tags) + self.assertEqual({"foo"}, result.current_tags) def test_tags_removed_in_test(self): # Tags can be removed during tests. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.startTest(self) - result.tags(set(), {'foo'}) + result.tags(set(), {"foo"}) self.assertEqual(set(), result.current_tags) def test_tags_removed_in_test_are_restored(self): # Tags removed during tests are restored once that test has finished. result = self.makeResult() result.startTestRun() - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.startTest(self) - result.tags(set(), {'foo'}) + result.tags(set(), {"foo"}) result.addSuccess(self) result.stopTest(self) - self.assertEqual({'foo'}, result.current_tags) + self.assertEqual({"foo"}, result.current_tags) class DetailsContract(TagsContract): @@ -408,7 +414,6 @@ def test_startTestRun_resets_errors(self): class TestTestResultContract(TestCase, StartTestRunContract): - run_test_with = FullStackRunTest def makeResult(self): @@ -416,7 +421,6 @@ def makeResult(self): class TestMultiTestResultContract(TestCase, StartTestRunContract): - run_test_with = FullStackRunTest def makeResult(self): @@ -424,7 +428,6 @@ def makeResult(self): class TestTextTestResultContract(TestCase, StartTestRunContract): - run_test_with = FullStackRunTest def makeResult(self): @@ -432,7 +435,6 @@ def makeResult(self): class TestThreadSafeForwardingResultContract(TestCase, StartTestRunContract): - run_test_with = FullStackRunTest def makeResult(self): @@ -442,49 +444,41 @@ def makeResult(self): class TestExtendedTestResultContract(TestCase, StartTestRunContract): - def makeResult(self): return ExtendedTestResult() class TestPython26TestResultContract(TestCase, Python26Contract): - def makeResult(self): return Python26TestResult() class TestAdaptedPython26TestResultContract(TestCase, FallbackContract): - def makeResult(self): return ExtendedToOriginalDecorator(Python26TestResult()) class TestPython27TestResultContract(TestCase, Python27Contract): - def makeResult(self): return Python27TestResult() class TestAdaptedPython27TestResultContract(TestCase, DetailsContract): - def makeResult(self): return ExtendedToOriginalDecorator(Python27TestResult()) class TestAdaptedTwistedTestResultContract(TestCase, DetailsContract): - def makeResult(self): return ExtendedToOriginalDecorator(TwistedTestResult()) class TestAdaptedStreamResult(TestCase, DetailsContract): - def makeResult(self): return ExtendedToStreamDecorator(StreamResult()) class TestTestResultDecoratorContract(TestCase, StartTestRunContract): - run_test_with = FullStackRunTest def makeResult(self): @@ -494,14 +488,13 @@ def makeResult(self): # DetailsContract because ExtendedToStreamDecorator follows Python for # uxsuccess handling. class TestStreamToExtendedContract(TestCase, DetailsContract): - def makeResult(self): return ExtendedToStreamDecorator( - StreamToExtendedDecorator(ExtendedTestResult())) + StreamToExtendedDecorator(ExtendedTestResult()) + ) class TestStreamResultContract: - def _make_result(self): raise NotImplementedError(self._make_result) @@ -516,13 +509,15 @@ def test_files(self): result.startTestRun() self.addCleanup(result.stopTestRun) now = datetime.datetime.now(utc) - inputs = list(dict( - eof=True, - mime_type="text/plain", - route_code="1234", - test_id="foo", - timestamp=now, - ).items()) + inputs = list( + dict( + eof=True, + mime_type="text/plain", + route_code="1234", + test_id="foo", + timestamp=now, + ).items() + ) param_dicts = self._power_set(inputs) for kwargs in param_dicts: result.status(file_name="foo", file_bytes=_b(""), **kwargs) @@ -534,21 +529,26 @@ def test_test_status(self): result.startTestRun() self.addCleanup(result.stopTestRun) now = datetime.datetime.now(utc) - args = [["foo", s] for s in [ - 'exists', - 'inprogress', - 'xfail', - 'uxsuccess', - 'success', - 'fail', - 'skip', - ]] - inputs = list(dict( - runnable=False, - test_tags={'quux'}, - route_code="1234", - timestamp=now, - ).items()) + args = [ + ["foo", s] + for s in [ + "exists", + "inprogress", + "xfail", + "uxsuccess", + "success", + "fail", + "skip", + ] + ] + inputs = list( + dict( + runnable=False, + test_tags={"quux"}, + route_code="1234", + timestamp=now, + ).items() + ) param_dicts = self._power_set(inputs) for kwargs in param_dicts: for arg in args: @@ -565,116 +565,145 @@ def _power_set(self, iterable): class TestBaseStreamResultContract(TestCase, TestStreamResultContract): - def _make_result(self): return StreamResult() class TestCopyStreamResultContract(TestCase, TestStreamResultContract): - def _make_result(self): return CopyStreamResult([StreamResult(), StreamResult()]) class TestDoubleStreamResultContract(TestCase, TestStreamResultContract): - def _make_result(self): return LoggingStreamResult() class TestExtendedToStreamDecoratorContract(TestCase, TestStreamResultContract): - def _make_result(self): return ExtendedToStreamDecorator(StreamResult()) class TestResourcedToStreamDecoratorContract(TestCase, TestStreamResultContract): - def _make_result(self): return ResourcedToStreamDecorator(StreamResult()) class TestStreamSummaryResultContract(TestCase, TestStreamResultContract): - def _make_result(self): return StreamSummary() class TestStreamTaggerContract(TestCase, TestStreamResultContract): - def _make_result(self): return StreamTagger([StreamResult()], add=set(), discard=set()) class TestStreamToDictContract(TestCase, TestStreamResultContract): - def _make_result(self): - return StreamToDict(lambda x:None) + return StreamToDict(lambda x: None) class TestStreamToExtendedDecoratorContract(TestCase, TestStreamResultContract): - def _make_result(self): return StreamToExtendedDecorator(ExtendedTestResult()) class TestStreamToQueueContract(TestCase, TestStreamResultContract): - def _make_result(self): queue = Queue() return StreamToQueue(queue, "foo") class TestStreamFailFastContract(TestCase, TestStreamResultContract): - def _make_result(self): - return StreamFailFast(lambda:None) + return StreamFailFast(lambda: None) class TestStreamResultRouterContract(TestCase, TestStreamResultContract): - def _make_result(self): return StreamResultRouter(StreamResult()) class TestDoubleStreamResultEvents(TestCase): - def test_startTestRun(self): result = LoggingStreamResult() result.startTestRun() - self.assertEqual([('startTestRun',)], result._events) + self.assertEqual([("startTestRun",)], result._events) def test_stopTestRun(self): result = LoggingStreamResult() result.startTestRun() result.stopTestRun() - self.assertEqual([('startTestRun',), ('stopTestRun',)], result._events) + self.assertEqual([("startTestRun",), ("stopTestRun",)], result._events) def test_file(self): result = LoggingStreamResult() result.startTestRun() now = datetime.datetime.now(utc) - result.status(file_name="foo", file_bytes="bar", eof=True, mime_type="text/json", - test_id="id", route_code='abc', timestamp=now) + result.status( + file_name="foo", + file_bytes="bar", + eof=True, + mime_type="text/json", + test_id="id", + route_code="abc", + timestamp=now, + ) self.assertEqual( - [('startTestRun',), - ('status', 'id', None, None, True, 'foo', 'bar', True, 'text/json', 'abc', now)], - result._events) + [ + ("startTestRun",), + ( + "status", + "id", + None, + None, + True, + "foo", + "bar", + True, + "text/json", + "abc", + now, + ), + ], + result._events, + ) def test_status(self): result = LoggingStreamResult() result.startTestRun() now = datetime.datetime.now(utc) - result.status("foo", "success", test_tags={'tag'}, - runnable=False, route_code='abc', timestamp=now) + result.status( + "foo", + "success", + test_tags={"tag"}, + runnable=False, + route_code="abc", + timestamp=now, + ) self.assertEqual( - [('startTestRun',), - ('status', 'foo', 'success', {'tag'}, False, None, None, False, None, 'abc', now)], - result._events) + [ + ("startTestRun",), + ( + "status", + "foo", + "success", + {"tag"}, + False, + None, + None, + False, + None, + "abc", + now, + ), + ], + result._events, + ) class TestCopyStreamResultCopies(TestCase): - def setUp(self): super().setUp() self.target1 = LoggingStreamResult() @@ -684,94 +713,195 @@ def setUp(self): def test_startTestRun(self): self.result.startTestRun() - self.assertThat(self.targets, AllMatch(Equals([('startTestRun',)]))) + self.assertThat(self.targets, AllMatch(Equals([("startTestRun",)]))) def test_stopTestRun(self): self.result.startTestRun() self.result.stopTestRun() - self.assertThat(self.targets, - AllMatch(Equals([('startTestRun',), ('stopTestRun',)]))) + self.assertThat( + self.targets, AllMatch(Equals([("startTestRun",), ("stopTestRun",)])) + ) def test_status(self): self.result.startTestRun() now = datetime.datetime.now(utc) - self.result.status("foo", "success", test_tags={'tag'}, - runnable=False, file_name="foo", file_bytes=b'bar', eof=True, - mime_type="text/json", route_code='abc', timestamp=now) - self.assertThat(self.targets, - AllMatch(Equals([('startTestRun',), - ('status', 'foo', 'success', {'tag'}, False, "foo", - b'bar', True, "text/json", 'abc', now) - ]))) + self.result.status( + "foo", + "success", + test_tags={"tag"}, + runnable=False, + file_name="foo", + file_bytes=b"bar", + eof=True, + mime_type="text/json", + route_code="abc", + timestamp=now, + ) + self.assertThat( + self.targets, + AllMatch( + Equals( + [ + ("startTestRun",), + ( + "status", + "foo", + "success", + {"tag"}, + False, + "foo", + b"bar", + True, + "text/json", + "abc", + now, + ), + ] + ) + ), + ) class TestStreamTagger(TestCase): - def test_adding(self): log = LoggingStreamResult() - result = StreamTagger([log], add=['foo']) + result = StreamTagger([log], add=["foo"]) result.startTestRun() result.status() - result.status(test_tags={'bar'}) + result.status(test_tags={"bar"}) result.status(test_tags=None) result.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('status', None, None, {'foo'}, True, None, None, False, None, None, None), - ('status', None, None, {'foo', 'bar'}, True, None, None, False, None, None, None), - ('status', None, None, {'foo'}, True, None, None, False, None, None, None), - ('stopTestRun',), - ], log._events) + self.assertEqual( + [ + ("startTestRun",), + ( + "status", + None, + None, + {"foo"}, + True, + None, + None, + False, + None, + None, + None, + ), + ( + "status", + None, + None, + {"foo", "bar"}, + True, + None, + None, + False, + None, + None, + None, + ), + ( + "status", + None, + None, + {"foo"}, + True, + None, + None, + False, + None, + None, + None, + ), + ("stopTestRun",), + ], + log._events, + ) def test_discarding(self): log = LoggingStreamResult() - result = StreamTagger([log], discard=['foo']) + result = StreamTagger([log], discard=["foo"]) result.startTestRun() result.status() result.status(test_tags=None) - result.status(test_tags={'foo'}) - result.status(test_tags={'bar'}) - result.status(test_tags={'foo', 'bar'}) + result.status(test_tags={"foo"}) + result.status(test_tags={"bar"}) + result.status(test_tags={"foo", "bar"}) result.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('status', None, None, None, True, None, None, False, None, None, None), - ('status', None, None, None, True, None, None, False, None, None, None), - ('status', None, None, None, True, None, None, False, None, None, None), - ('status', None, None, {'bar'}, True, None, None, False, None, None, None), - ('status', None, None, {'bar'}, True, None, None, False, None, None, None), - ('stopTestRun',), - ], log._events) + self.assertEqual( + [ + ("startTestRun",), + ("status", None, None, None, True, None, None, False, None, None, None), + ("status", None, None, None, True, None, None, False, None, None, None), + ("status", None, None, None, True, None, None, False, None, None, None), + ( + "status", + None, + None, + {"bar"}, + True, + None, + None, + False, + None, + None, + None, + ), + ( + "status", + None, + None, + {"bar"}, + True, + None, + None, + False, + None, + None, + None, + ), + ("stopTestRun",), + ], + log._events, + ) class TestStreamToDict(TestCase): - def test_hung_test(self): tests = [] result = StreamToDict(tests.append) result.startTestRun() - result.status('foo', 'inprogress') + result.status("foo", "inprogress") self.assertEqual([], tests) result.stopTestRun() - self.assertEqual([ - {'id': 'foo', 'tags': set(), 'details': {}, 'status': 'inprogress', - 'timestamps': [None, None]} - ], tests) + self.assertEqual( + [ + { + "id": "foo", + "tags": set(), + "details": {}, + "status": "inprogress", + "timestamps": [None, None], + } + ], + tests, + ) def test_all_terminal_states_reported(self): tests = [] result = StreamToDict(tests.append) result.startTestRun() - result.status('success', 'success') - result.status('skip', 'skip') - result.status('exists', 'exists') - result.status('fail', 'fail') - result.status('xfail', 'xfail') - result.status('uxsuccess', 'uxsuccess') + result.status("success", "success") + result.status("skip", "skip") + result.status("exists", "exists") + result.status("fail", "fail") + result.status("xfail", "xfail") + result.status("uxsuccess", "uxsuccess") self.assertThat(tests, HasLength(6)) self.assertEqual( - ['success', 'skip', 'exists', 'fail', 'xfail', 'uxsuccess'], - [test['id'] for test in tests]) + ["success", "skip", "exists", "fail", "xfail", "uxsuccess"], + [test["id"] for test in tests], + ) result.stopTestRun() self.assertThat(tests, HasLength(6)) @@ -779,24 +909,31 @@ def test_files_reported(self): tests = [] result = StreamToDict(tests.append) result.startTestRun() - result.status(file_name="some log.txt", - file_bytes=_b("1234 log message"), eof=True, - mime_type="text/plain; charset=utf8", test_id="foo.bar") - result.status(file_name="another file", - file_bytes=_b("""Traceback..."""), test_id="foo.bar") + result.status( + file_name="some log.txt", + file_bytes=_b("1234 log message"), + eof=True, + mime_type="text/plain; charset=utf8", + test_id="foo.bar", + ) + result.status( + file_name="another file", + file_bytes=_b("""Traceback..."""), + test_id="foo.bar", + ) result.stopTestRun() self.assertThat(tests, HasLength(1)) test = tests[0] - self.assertEqual("foo.bar", test['id']) - self.assertEqual("unknown", test['status']) - details = test['details'] - self.assertEqual( - "1234 log message", details['some log.txt'].as_text()) + self.assertEqual("foo.bar", test["id"]) + self.assertEqual("unknown", test["status"]) + details = test["details"] + self.assertEqual("1234 log message", details["some log.txt"].as_text()) self.assertEqual( - _b("Traceback..."), - _b('').join(details['another file'].iter_bytes())) + _b("Traceback..."), _b("").join(details["another file"].iter_bytes()) + ) self.assertEqual( - "application/octet-stream", repr(details['another file'].content_type)) + "application/octet-stream", repr(details["another file"].content_type) + ) def test_bad_mime(self): # Testtools was making bad mime types, this tests that the specific @@ -804,46 +941,52 @@ def test_bad_mime(self): tests = [] result = StreamToDict(tests.append) result.startTestRun() - result.status(file_name="file", file_bytes=b'a', - mime_type='text/plain; charset=utf8, language=python', - test_id='id') + result.status( + file_name="file", + file_bytes=b"a", + mime_type="text/plain; charset=utf8, language=python", + test_id="id", + ) result.stopTestRun() self.assertThat(tests, HasLength(1)) test = tests[0] - self.assertEqual("id", test['id']) - details = test['details'] - self.assertEqual("a", details['file'].as_text()) + self.assertEqual("id", test["id"]) + details = test["details"] + self.assertEqual("a", details["file"].as_text()) self.assertEqual( - "text/plain; charset=\"utf8\"", - repr(details['file'].content_type)) + 'text/plain; charset="utf8"', repr(details["file"].content_type) + ) def test_timestamps(self): tests = [] result = StreamToDict(tests.append) result.startTestRun() - result.status(test_id='foo', test_status='inprogress', timestamp="A") - result.status(test_id='foo', test_status='success', timestamp="B") - result.status(test_id='bar', test_status='inprogress', timestamp="C") + result.status(test_id="foo", test_status="inprogress", timestamp="A") + result.status(test_id="foo", test_status="success", timestamp="B") + result.status(test_id="bar", test_status="inprogress", timestamp="C") result.stopTestRun() self.assertThat(tests, HasLength(2)) - self.assertEqual(["A", "B"], tests[0]['timestamps']) - self.assertEqual(["C", None], tests[1]['timestamps']) + self.assertEqual(["A", "B"], tests[0]["timestamps"]) + self.assertEqual(["C", None], tests[1]["timestamps"]) def test_files_skipped(self): tests = [] result = StreamToDict(tests.append) result.startTestRun() - result.status(file_name="some log.txt", - file_bytes="", eof=True, - mime_type="text/plain; charset=utf8", test_id="foo.bar") + result.status( + file_name="some log.txt", + file_bytes="", + eof=True, + mime_type="text/plain; charset=utf8", + test_id="foo.bar", + ) result.stopTestRun() self.assertThat(tests, HasLength(1)) - details = tests[0]['details'] + details = tests[0]["details"] self.assertNotIn("some log.txt", details) class TestExtendedToStreamDecorator(TestCase): - def test_explicit_time(self): log = LoggingStreamResult() result = ExtendedToStreamDecorator(log) @@ -854,37 +997,45 @@ def test_explicit_time(self): result.addSuccess(self) result.stopTest(self) result.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('status', - 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_explicit_time', - 'inprogress', - None, - True, - None, - None, - False, - None, - None, - now), - ('status', - 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_explicit_time', - 'success', - set(), - True, - None, - None, - False, - None, - None, - now), - ('stopTestRun',)], log._events) + self.assertEqual( + [ + ("startTestRun",), + ( + "status", + "testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_explicit_time", + "inprogress", + None, + True, + None, + None, + False, + None, + None, + now, + ), + ( + "status", + "testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_explicit_time", + "success", + set(), + True, + None, + None, + False, + None, + None, + now, + ), + ("stopTestRun",), + ], + log._events, + ) def test_wasSuccessful_after_stopTestRun(self): log = LoggingStreamResult() result = ExtendedToStreamDecorator(log) result.startTestRun() - result.status(test_id='foo', test_status='fail') + result.status(test_id="foo", test_status="fail") result.stopTestRun() self.assertEqual(False, result.wasSuccessful()) @@ -895,53 +1046,62 @@ def test_empty_detail_status_correct(self): now = datetime.datetime.now(utc) result.time(now) result.startTest(self) - result.addError(self, details={'foo': text_content("")}) + result.addError(self, details={"foo": text_content("")}) result.stopTest(self) result.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('status', - 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct', - 'inprogress', - None, - True, - None, - None, - False, - None, - None, - now), - ('status', - 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct', - None, - None, - True, - 'foo', - _b(''), - True, - 'text/plain; charset="utf8"', - None, - now), - ('status', - 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct', - 'fail', - set(), - True, - None, - None, - False, - None, - None, - now), - ('stopTestRun',)], log._events) + self.assertEqual( + [ + ("startTestRun",), + ( + "status", + "testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct", + "inprogress", + None, + True, + None, + None, + False, + None, + None, + now, + ), + ( + "status", + "testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct", + None, + None, + True, + "foo", + _b(""), + True, + 'text/plain; charset="utf8"', + None, + now, + ), + ( + "status", + "testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct", + "fail", + set(), + True, + None, + None, + False, + None, + None, + now, + ), + ("stopTestRun",), + ], + log._events, + ) class TestResourcedToStreamDecorator(TestCase): - def setUp(self): super().setUp() if testresources is None: - self.skipTest('Need testresources') + self.skipTest("Need testresources") def test_startMakeResource(self): log = LoggingStreamResult() @@ -952,9 +1112,8 @@ def test_startMakeResource(self): resource = testresources.TestResourceManager() result.startMakeResource(resource) [_, event] = log._events - self.assertEqual( - 'testresources.TestResourceManager.make', event.test_id) - self.assertEqual('inprogress', event.test_status) + self.assertEqual("testresources.TestResourceManager.make", event.test_id) + self.assertEqual("inprogress", event.test_status) self.assertFalse(event.runnable) self.assertEqual(timestamp, event.timestamp) @@ -962,10 +1121,10 @@ def test_startMakeResource_with_custom_id_method(self): log = LoggingStreamResult() result = ResourcedToStreamDecorator(log) resource = testresources.TestResourceManager() - resource.id = lambda: 'nice.resource' + resource.id = lambda: "nice.resource" result.startTestRun() result.startMakeResource(resource) - self.assertEqual('nice.resource.make', log._events[1].test_id) + self.assertEqual("nice.resource.make", log._events[1].test_id) def test_stopMakeResource(self): log = LoggingStreamResult() @@ -974,9 +1133,8 @@ def test_stopMakeResource(self): result.startTestRun() result.stopMakeResource(resource) [_, event] = log._events - self.assertEqual( - 'testresources.TestResourceManager.make', event.test_id) - self.assertEqual('success', event.test_status) + self.assertEqual("testresources.TestResourceManager.make", event.test_id) + self.assertEqual("success", event.test_status) def test_startCleanResource(self): log = LoggingStreamResult() @@ -985,9 +1143,8 @@ def test_startCleanResource(self): result.startTestRun() result.startCleanResource(resource) [_, event] = log._events - self.assertEqual( - 'testresources.TestResourceManager.clean', event.test_id) - self.assertEqual('inprogress', event.test_status) + self.assertEqual("testresources.TestResourceManager.clean", event.test_id) + self.assertEqual("inprogress", event.test_status) def test_stopCleanResource(self): log = LoggingStreamResult() @@ -996,54 +1153,55 @@ def test_stopCleanResource(self): result.startTestRun() result.stopCleanResource(resource) [_, event] = log._events - self.assertEqual( - 'testresources.TestResourceManager.clean', event.test_id) - self.assertEqual('success', event.test_status) + self.assertEqual("testresources.TestResourceManager.clean", event.test_id) + self.assertEqual("success", event.test_status) class TestStreamFailFast(TestCase): - def test_inprogress(self): result = StreamFailFast(self.fail) - result.status('foo', 'inprogress') + result.status("foo", "inprogress") def test_exists(self): result = StreamFailFast(self.fail) - result.status('foo', 'exists') + result.status("foo", "exists") def test_xfail(self): result = StreamFailFast(self.fail) - result.status('foo', 'xfail') + result.status("foo", "xfail") def test_uxsuccess(self): calls = [] + def hook(): calls.append("called") + result = StreamFailFast(hook) - result.status('foo', 'uxsuccess') - result.status('foo', 'uxsuccess') - self.assertEqual(['called', 'called'], calls) + result.status("foo", "uxsuccess") + result.status("foo", "uxsuccess") + self.assertEqual(["called", "called"], calls) def test_success(self): result = StreamFailFast(self.fail) - result.status('foo', 'success') + result.status("foo", "success") def test_fail(self): calls = [] + def hook(): calls.append("called") + result = StreamFailFast(hook) - result.status('foo', 'fail') - result.status('foo', 'fail') - self.assertEqual(['called', 'called'], calls) + result.status("foo", "fail") + result.status("foo", "fail") + self.assertEqual(["called", "called"], calls) def test_skip(self): result = StreamFailFast(self.fail) - result.status('foo', 'skip') + result.status("foo", "skip") class TestStreamSummary(TestCase): - def test_attributes(self): result = StreamSummary() result.startTestRun() @@ -1057,11 +1215,11 @@ def test_attributes(self): def test_startTestRun(self): result = StreamSummary() result.startTestRun() - result.failures.append('x') - result.errors.append('x') - result.skipped.append('x') - result.expectedFailures.append('x') - result.unexpectedSuccesses.append('x') + result.failures.append("x") + result.errors.append("x") + result.skipped.append("x") + result.expectedFailures.append("x") + result.unexpectedSuccesses.append("x") result.testsRun = 1 result.startTestRun() self.assertEqual([], result.failures) @@ -1077,19 +1235,19 @@ def test_wasSuccessful(self): result = StreamSummary() result.startTestRun() self.assertEqual(True, result.wasSuccessful()) - result.failures.append('x') + result.failures.append("x") self.assertEqual(False, result.wasSuccessful()) result.startTestRun() - result.errors.append('x') + result.errors.append("x") self.assertEqual(False, result.wasSuccessful()) result.startTestRun() - result.skipped.append('x') + result.skipped.append("x") self.assertEqual(True, result.wasSuccessful()) result.startTestRun() - result.expectedFailures.append('x') + result.expectedFailures.append("x") self.assertEqual(True, result.wasSuccessful()) result.startTestRun() - result.unexpectedSuccesses.append('x') + result.unexpectedSuccesses.append("x") self.assertEqual(True, result.wasSuccessful()) def test_stopTestRun(self): @@ -1129,29 +1287,45 @@ def test_status_skip(self): # from the 'reason' file attachment if any. result = StreamSummary() result.startTestRun() - result.status(file_name="reason", - file_bytes=_b("Missing dependency"), eof=True, - mime_type="text/plain; charset=utf8", test_id="foo.bar") + result.status( + file_name="reason", + file_bytes=_b("Missing dependency"), + eof=True, + mime_type="text/plain; charset=utf8", + test_id="foo.bar", + ) result.status("foo.bar", "skip") self.assertThat(result.skipped, HasLength(1)) self.assertEqual("foo.bar", result.skipped[0][0].id()) self.assertEqual("Missing dependency", result.skipped[0][1]) def _report_files(self, result): - result.status(file_name="some log.txt", - file_bytes=_b("1234 log message"), eof=True, - mime_type="text/plain; charset=utf8", test_id="foo.bar") - result.status(file_name="traceback", - file_bytes=_b("""Traceback (most recent call last): + result.status( + file_name="some log.txt", + file_bytes=_b("1234 log message"), + eof=True, + mime_type="text/plain; charset=utf8", + test_id="foo.bar", + ) + result.status( + file_name="traceback", + file_bytes=_b( + """Traceback (most recent call last): File "testtools/tests/test_testresult.py", line 607, in test_stopTestRun AllMatch(Equals([('startTestRun',), ('stopTestRun',)]))) testtools.matchers._impl.MismatchError: Differences: [ [('startTestRun',), ('stopTestRun',)] != [] [('startTestRun',), ('stopTestRun',)] != [] ] -"""), eof=True, mime_type="text/plain; charset=utf8", test_id="foo.bar") +""" + ), + eof=True, + mime_type="text/plain; charset=utf8", + test_id="foo.bar", + ) - files_message = Equals("""some log.txt: {{{1234 log message}}} + files_message = Equals( + """some log.txt: {{{1234 log message}}} Traceback (most recent call last): File "testtools/tests/test_testresult.py", line 607, in test_stopTestRun @@ -1160,7 +1334,8 @@ def _report_files(self, result): [('startTestRun',), ('stopTestRun',)] != [] [('startTestRun',), ('stopTestRun',)] != [] ] -""") +""" + ) def test_status_fail(self): # when fail is seen, a synthetic test is reported with all files @@ -1194,7 +1369,6 @@ def test_status_uxsuccess(self): class TestTestControl(TestCase, TestControlContract): - def makeResult(self): return TestControl() @@ -1213,24 +1387,30 @@ def test_addSkipped(self): # its skip_reasons dict. result = self.makeResult() result.addSkip(self, "Skipped for some reason") - self.assertEqual({"Skipped for some reason":[self]}, - result.skip_reasons) + self.assertEqual({"Skipped for some reason": [self]}, result.skip_reasons) result.addSkip(self, "Skipped for some reason") - self.assertEqual({"Skipped for some reason":[self, self]}, - result.skip_reasons) + self.assertEqual({"Skipped for some reason": [self, self]}, result.skip_reasons) result.addSkip(self, "Skipped for another reason") - self.assertEqual({"Skipped for some reason":[self, self], - "Skipped for another reason":[self]}, - result.skip_reasons) + self.assertEqual( + { + "Skipped for some reason": [self, self], + "Skipped for another reason": [self], + }, + result.skip_reasons, + ) def test_now_datetime_now(self): result = self.makeResult() olddatetime = testresult.real.datetime + def restore(): testresult.real.datetime = olddatetime + self.addCleanup(restore) + class Module: pass + now = datetime.datetime.now(utc) stubdatetime = Module() stubdatetime.datetime = Module() @@ -1263,15 +1443,17 @@ def test_traceback_formatting_without_stack_hidden(self): self.assertThat( result.errors[0][1], DocTestMatches( - 'Traceback (most recent call last):\n' + "Traceback (most recent call last):\n" ' File "...testtools...runtest.py", line ..., in _run_user\n' - ' return fn(*args, **kwargs)\n...' + " return fn(*args, **kwargs)\n..." ' File "...testtools...testcase.py", line ..., in _run_test_method\n' - ' return self._get_test_method()()\n...' + " return self._get_test_method()()\n..." ' File "...testtools...tests...test_testresult.py", line ..., in error\n' - ' 1/0\n...' - 'ZeroDivisionError: ...\n', - doctest.ELLIPSIS | doctest.REPORT_UDIFF)) + " 1 / 0\n..." + "ZeroDivisionError: ...\n", + doctest.ELLIPSIS | doctest.REPORT_UDIFF, + ), + ) def test_traceback_formatting_with_stack_hidden(self): result = self.makeResult() @@ -1280,11 +1462,13 @@ def test_traceback_formatting_with_stack_hidden(self): self.assertThat( result.errors[0][1], DocTestMatches( - 'Traceback (most recent call last):\n' + "Traceback (most recent call last):\n" ' File "...testtools...tests...test_testresult.py", line ..., in error\n' - ' 1/0\n...' - 'ZeroDivisionError: ...\n', - doctest.ELLIPSIS)) + " 1 / 0\n..." + "ZeroDivisionError: ...\n", + doctest.ELLIPSIS, + ), + ) def test_traceback_formatting_with_stack_hidden_mismatch(self): result = self.makeResult() @@ -1293,11 +1477,13 @@ def test_traceback_formatting_with_stack_hidden_mismatch(self): self.assertThat( result.failures[0][1], DocTestMatches( - 'Traceback (most recent call last):\n' + "Traceback (most recent call last):\n" ' File "...testtools...tests...test_testresult.py", line ..., in mismatch\n' - ' self.assertEqual(1, 2)\n' - '...MismatchError: 1 != 2\n', - doctest.ELLIPSIS)) + " self.assertEqual(1, 2)\n" + "...MismatchError: 1 != 2\n", + doctest.ELLIPSIS, + ), + ) def test_exc_info_to_unicode(self): # subunit upcalls to TestResult._exc_info_to_unicode, so we need to @@ -1308,8 +1494,7 @@ def test_exc_info_to_unicode(self): exc_info = make_exception_info(RuntimeError, "foo") result = self.makeResult() text_traceback = result._exc_info_to_unicode(exc_info, test) - self.assertEqual( - TracebackContent(exc_info, test).as_text(), text_traceback) + self.assertEqual(TracebackContent(exc_info, test).as_text(), text_traceback) def test_traceback_with_locals(self): result = self.makeResult() @@ -1319,23 +1504,25 @@ def test_traceback_with_locals(self): self.assertThat( result.errors[0][1], DocTestMatches( - 'Traceback (most recent call last):\n' + "Traceback (most recent call last):\n" ' File "...testtools...runtest.py", line ..., in _run_user\n' - ' return fn(*args, **kwargs)\n...' - ' args = ...\n' - ' fn = ...\n' - ' kwargs = ...\n' - ' self = ...\n' + " return fn(*args, **kwargs)\n..." + " args = ...\n" + " fn = ...\n" + " kwargs = ...\n" + " self = ...\n" ' File "...testtools...testcase.py", line ..., in _run_test_method\n' - ' return self._get_test_method()()\n...' - ' result = ...\n' - ' self = ...\n' + " return self._get_test_method()()\n..." + " result = ...\n" + " self = ...\n" ' File "...testtools...tests...test_testresult.py", line ..., in error\n' - ' 1/0\n...' - ' a = 1\n' - ' self = ...\n' - 'ZeroDivisionError: ...\n', - doctest.ELLIPSIS | doctest.REPORT_UDIFF)) + " 1 / 0\n..." + " a = 1\n" + " self = ...\n" + "ZeroDivisionError: ...\n", + doctest.ELLIPSIS | doctest.REPORT_UDIFF, + ), + ) class TestMultiTestResult(TestCase): @@ -1354,10 +1541,12 @@ def assertResultLogsEqual(self, expectedEvents): def test_repr(self): self.assertEqual( - ''.format( + "".format( ExtendedToOriginalDecorator(self.result1), - ExtendedToOriginalDecorator(self.result2)), - repr(self.multiResult)) + ExtendedToOriginalDecorator(self.result2), + ), + repr(self.multiResult), + ) def test_empty(self): # Initializing a `MultiTestResult` doesn't do anything to its @@ -1387,63 +1576,63 @@ def test_startTest(self): # Calling `startTest` on a `MultiTestResult` calls `startTest` on all # its `TestResult`s. self.multiResult.startTest(self) - self.assertResultLogsEqual([('startTest', self)]) + self.assertResultLogsEqual([("startTest", self)]) def test_stop(self): self.assertFalse(self.multiResult.shouldStop) self.multiResult.stop() - self.assertResultLogsEqual(['stop']) + self.assertResultLogsEqual(["stop"]) def test_stopTest(self): # Calling `stopTest` on a `MultiTestResult` calls `stopTest` on all # its `TestResult`s. self.multiResult.stopTest(self) - self.assertResultLogsEqual([('stopTest', self)]) + self.assertResultLogsEqual([("stopTest", self)]) def test_addSkipped(self): # Calling `addSkip` on a `MultiTestResult` calls addSkip on its # results. reason = "Skipped for some reason" self.multiResult.addSkip(self, reason) - self.assertResultLogsEqual([('addSkip', self, reason)]) + self.assertResultLogsEqual([("addSkip", self, reason)]) def test_addSuccess(self): # Calling `addSuccess` on a `MultiTestResult` calls `addSuccess` on # all its `TestResult`s. self.multiResult.addSuccess(self) - self.assertResultLogsEqual([('addSuccess', self)]) + self.assertResultLogsEqual([("addSuccess", self)]) def test_done(self): # Calling `done` on a `MultiTestResult` calls `done` on all its # `TestResult`s. self.multiResult.done() - self.assertResultLogsEqual([('done')]) + self.assertResultLogsEqual([("done")]) def test_addFailure(self): # Calling `addFailure` on a `MultiTestResult` calls `addFailure` on # all its `TestResult`s. - exc_info = make_exception_info(AssertionError, 'failure') + exc_info = make_exception_info(AssertionError, "failure") self.multiResult.addFailure(self, exc_info) - self.assertResultLogsEqual([('addFailure', self, exc_info)]) + self.assertResultLogsEqual([("addFailure", self, exc_info)]) def test_addError(self): # Calling `addError` on a `MultiTestResult` calls `addError` on all # its `TestResult`s. - exc_info = make_exception_info(RuntimeError, 'error') + exc_info = make_exception_info(RuntimeError, "error") self.multiResult.addError(self, exc_info) - self.assertResultLogsEqual([('addError', self, exc_info)]) + self.assertResultLogsEqual([("addError", self, exc_info)]) def test_startTestRun(self): # Calling `startTestRun` on a `MultiTestResult` forwards to all its # `TestResult`s. self.multiResult.startTestRun() - self.assertResultLogsEqual([('startTestRun')]) + self.assertResultLogsEqual([("startTestRun")]) def test_stopTestRun(self): # Calling `stopTestRun` on a `MultiTestResult` forwards to all its # `TestResult`s. self.multiResult.stopTestRun() - self.assertResultLogsEqual([('stopTestRun')]) + self.assertResultLogsEqual([("stopTestRun")]) def test_stopTestRun_returns_results(self): # `MultiTestResult.stopTestRun` returns a tuple of all of the return @@ -1451,23 +1640,24 @@ def test_stopTestRun_returns_results(self): class Result(LoggingResult): def stopTestRun(self): super().stopTestRun() - return 'foo' + return "foo" + multi_result = MultiTestResult(Result([]), Result([])) result = multi_result.stopTestRun() - self.assertEqual(('foo', 'foo'), result) + self.assertEqual(("foo", "foo"), result) def test_tags(self): # Calling `tags` on a `MultiTestResult` calls `tags` on all its # `TestResult`s. - added_tags = {'foo', 'bar'} - removed_tags = {'eggs'} + added_tags = {"foo", "bar"} + removed_tags = {"eggs"} self.multiResult.tags(added_tags, removed_tags) - self.assertResultLogsEqual([('tags', added_tags, removed_tags)]) + self.assertResultLogsEqual([("tags", added_tags, removed_tags)]) def test_time(self): # the time call is dispatched, not eaten by the base class - self.multiResult.time('foo') - self.assertResultLogsEqual([('time', 'foo')]) + self.multiResult.time("foo") + self.assertResultLogsEqual([("time", "foo")]) class TestTextTestResult(TestCase): @@ -1500,8 +1690,10 @@ def test_stopTestRun_count_many(self): self.result.stopTest(test) self.result.stream = io.StringIO() self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("\nRan 2 tests in ...s\n...", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), + DocTestMatches("\nRan 2 tests in ...s\n...", doctest.ELLIPSIS), + ) def test_stopTestRun_count_single(self): test = make_test() @@ -1510,15 +1702,19 @@ def test_stopTestRun_count_single(self): self.result.stopTest(test) self.reset_output() self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("\nRan 1 test in ...s\nOK\n", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), + DocTestMatches("\nRan 1 test in ...s\nOK\n", doctest.ELLIPSIS), + ) def test_stopTestRun_count_zero(self): self.result.startTestRun() self.reset_output() self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("\nRan 0 tests in ...s\nOK\n", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), + DocTestMatches("\nRan 0 tests in ...s\nOK\n", doctest.ELLIPSIS), + ) def test_stopTestRun_current_time(self): test = make_test() @@ -1531,41 +1727,48 @@ def test_stopTestRun_current_time(self): self.result.stopTest(test) self.reset_output() self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("... in 0.001s\n...", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), DocTestMatches("... in 0.001s\n...", doctest.ELLIPSIS) + ) def test_stopTestRun_successful(self): self.result.startTestRun() self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("...\nOK\n", doctest.ELLIPSIS)) + self.assertThat(self.getvalue(), DocTestMatches("...\nOK\n", doctest.ELLIPSIS)) def test_stopTestRun_not_successful_failure(self): test = make_failing_test() self.result.startTestRun() test.run(self.result) self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), + DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS), + ) def test_stopTestRun_not_successful_error(self): test = make_erroring_test() self.result.startTestRun() test.run(self.result) self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), + DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS), + ) def test_stopTestRun_not_successful_unexpected_success(self): test = make_unexpectedly_successful_test() self.result.startTestRun() test.run(self.result) self.result.stopTestRun() - self.assertThat(self.getvalue(), - DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS)) + self.assertThat( + self.getvalue(), + DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS), + ) def test_stopTestRun_shows_details(self): self.skipTest("Disabled per bug 1188420") + def run_tests(): self.result.startTestRun() make_erroring_test().run(self.result) @@ -1573,14 +1776,17 @@ def run_tests(): make_failing_test().run(self.result) self.reset_output() self.result.stopTestRun() + run_with_stack_hidden(True, run_tests) - self.assertThat(self.getvalue(), - DocTestMatches("""...====================================================================== + self.assertThat( + self.getvalue(), + DocTestMatches( + """...====================================================================== ERROR: testtools.tests.test_testresult.Test.error ---------------------------------------------------------------------- Traceback (most recent call last): File "...testtools...tests...test_testresult.py", line ..., in error - 1/0 + 1 / 0 ZeroDivisionError:... divi... by zero... ====================================================================== FAIL: testtools.tests.test_testresult.Test.failed @@ -1592,7 +1798,10 @@ def run_tests(): ====================================================================== UNEXPECTED SUCCESS: testtools.tests.test_testresult.Test.succeeded ---------------------------------------------------------------------- -...""", doctest.ELLIPSIS | doctest.REPORT_NDIFF)) +...""", + doctest.ELLIPSIS | doctest.REPORT_NDIFF, + ), + ) class TestThreadSafeForwardingResult(TestCase): @@ -1602,9 +1811,7 @@ def make_results(self, n): events = [] target = LoggingResult(events) semaphore = threading.Semaphore(1) - return [ - ThreadsafeForwardingResult(target, semaphore) - for i in range(n)], events + return [ThreadsafeForwardingResult(target, semaphore) for i in range(n)], events def test_nonforwarding_methods(self): # startTest and stopTest are not forwarded because they need to be @@ -1618,7 +1825,7 @@ def test_tags_not_forwarded(self): # Tags need to be batched for each test, so they aren't forwarded # until a test runs. [result], events = self.make_results(1) - result.tags({'foo'}, {'bar'}) + result.tags({"foo"}, {"bar"}) self.assertEqual([], events) def test_global_tags_simple(self): @@ -1628,19 +1835,22 @@ def test_global_tags_simple(self): # way for a global tag on an input stream to affect tests from other # streams - we can just always issue test local tags. [result], events = self.make_results(1) - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.time(1) result.startTest(self) result.time(2) result.addSuccess(self) self.assertEqual( - [('time', 1), - ('startTest', self), - ('time', 2), - ('tags', {'foo'}, set()), - ('addSuccess', self), - ('stopTest', self), - ], events) + [ + ("time", 1), + ("startTest", self), + ("time", 2), + ("tags", {"foo"}, set()), + ("addSuccess", self), + ("stopTest", self), + ], + events, + ) def test_global_tags_complex(self): # Multiple calls to tags() in a global context are buffered until the @@ -1650,20 +1860,23 @@ def test_global_tags_complex(self): # speaking incidental - they could be issued separately (in-order) and # still be legitimate. [result], events = self.make_results(1) - result.tags({'foo', 'bar'}, {'baz', 'qux'}) - result.tags({'cat', 'qux'}, {'bar', 'dog'}) + result.tags({"foo", "bar"}, {"baz", "qux"}) + result.tags({"cat", "qux"}, {"bar", "dog"}) result.time(1) result.startTest(self) result.time(2) result.addSuccess(self) self.assertEqual( - [('time', 1), - ('startTest', self), - ('time', 2), - ('tags', {'cat', 'foo', 'qux'}, {'dog', 'bar', 'baz'}), - ('addSuccess', self), - ('stopTest', self), - ], events) + [ + ("time", 1), + ("startTest", self), + ("time", 2), + ("tags", {"cat", "foo", "qux"}, {"dog", "bar", "baz"}), + ("addSuccess", self), + ("stopTest", self), + ], + events, + ) def test_local_tags(self): # Any tags set within a test context are forwarded in that test @@ -1673,27 +1886,30 @@ def test_local_tags(self): [result], events = self.make_results(1) result.time(1) result.startTest(self) - result.tags({'foo'}, set()) - result.tags(set(), {'bar'}) + result.tags({"foo"}, set()) + result.tags(set(), {"bar"}) result.time(2) result.addSuccess(self) self.assertEqual( - [('time', 1), - ('startTest', self), - ('time', 2), - ('tags', {'foo'}, {'bar'}), - ('addSuccess', self), - ('stopTest', self), - ], events) + [ + ("time", 1), + ("startTest", self), + ("time", 2), + ("tags", {"foo"}, {"bar"}), + ("addSuccess", self), + ("stopTest", self), + ], + events, + ) def test_local_tags_dont_leak(self): # A tag set during a test is local to that test and is not set during # the tests that follow. [result], events = self.make_results(1) - a, b = PlaceHolder('a'), PlaceHolder('b') + a, b = PlaceHolder("a"), PlaceHolder("b") result.time(1) result.startTest(a) - result.tags({'foo'}, set()) + result.tags({"foo"}, set()) result.time(2) result.addSuccess(a) result.stopTest(a) @@ -1703,18 +1919,21 @@ def test_local_tags_dont_leak(self): result.addSuccess(b) result.stopTest(b) self.assertEqual( - [('time', 1), - ('startTest', a), - ('time', 2), - ('tags', {'foo'}, set()), - ('addSuccess', a), - ('stopTest', a), - ('time', 3), - ('startTest', b), - ('time', 4), - ('addSuccess', b), - ('stopTest', b), - ], events) + [ + ("time", 1), + ("startTest", a), + ("time", 2), + ("tags", {"foo"}, set()), + ("addSuccess", a), + ("stopTest", a), + ("time", 3), + ("startTest", b), + ("time", 4), + ("addSuccess", b), + ("stopTest", b), + ], + events, + ) def test_startTestRun(self): # Calls to startTestRun are not batched, because we are only @@ -1736,39 +1955,45 @@ def test_forward_addError(self): # Once we receive an addError event, we forward all of the events for # that test, as we now know that test is complete. [result], events = self.make_results(1) - exc_info = make_exception_info(RuntimeError, 'error') + exc_info = make_exception_info(RuntimeError, "error") start_time = datetime.datetime.utcfromtimestamp(1.489) end_time = datetime.datetime.utcfromtimestamp(51.476) result.time(start_time) result.startTest(self) result.time(end_time) result.addError(self, exc_info) - self.assertEqual([ - ('time', start_time), - ('startTest', self), - ('time', end_time), - ('addError', self, exc_info), - ('stopTest', self), - ], events) + self.assertEqual( + [ + ("time", start_time), + ("startTest", self), + ("time", end_time), + ("addError", self, exc_info), + ("stopTest", self), + ], + events, + ) def test_forward_addFailure(self): # Once we receive an addFailure event, we forward all of the events # for that test, as we now know that test is complete. [result], events = self.make_results(1) - exc_info = make_exception_info(AssertionError, 'failure') + exc_info = make_exception_info(AssertionError, "failure") start_time = datetime.datetime.utcfromtimestamp(2.489) end_time = datetime.datetime.utcfromtimestamp(3.476) result.time(start_time) result.startTest(self) result.time(end_time) result.addFailure(self, exc_info) - self.assertEqual([ - ('time', start_time), - ('startTest', self), - ('time', end_time), - ('addFailure', self, exc_info), - ('stopTest', self), - ], events) + self.assertEqual( + [ + ("time", start_time), + ("startTest", self), + ("time", end_time), + ("addFailure", self, exc_info), + ("stopTest", self), + ], + events, + ) def test_forward_addSkip(self): # Once we receive an addSkip event, we forward all of the events for @@ -1781,13 +2006,16 @@ def test_forward_addSkip(self): result.startTest(self) result.time(end_time) result.addSkip(self, reason) - self.assertEqual([ - ('time', start_time), - ('startTest', self), - ('time', end_time), - ('addSkip', self, reason), - ('stopTest', self), - ], events) + self.assertEqual( + [ + ("time", start_time), + ("startTest", self), + ("time", end_time), + ("addSkip", self, reason), + ("stopTest", self), + ], + events, + ) def test_forward_addSuccess(self): # Once we receive an addSuccess event, we forward all of the events @@ -1799,13 +2027,16 @@ def test_forward_addSuccess(self): result.startTest(self) result.time(end_time) result.addSuccess(self) - self.assertEqual([ - ('time', start_time), - ('startTest', self), - ('time', end_time), - ('addSuccess', self), - ('stopTest', self), - ], events) + self.assertEqual( + [ + ("time", start_time), + ("startTest", self), + ("time", end_time), + ("addSuccess", self), + ("stopTest", self), + ], + events, + ) def test_only_one_test_at_a_time(self): # Even if there are multiple ThreadsafeForwardingResults forwarding to @@ -1825,227 +2056,325 @@ def test_only_one_test_at_a_time(self): result2.time(end_time2) result2.addSuccess(test2) result1.addSuccess(test1) - self.assertEqual([ - # test2 finishes first, and so is flushed first. - ('time', start_time2), - ('startTest', test2), - ('time', end_time2), - ('addSuccess', test2), - ('stopTest', test2), - # test1 finishes next, and thus follows. - ('time', start_time1), - ('startTest', test1), - ('time', end_time1), - ('addSuccess', test1), - ('stopTest', test1), - ], events) + self.assertEqual( + [ + # test2 finishes first, and so is flushed first. + ("time", start_time2), + ("startTest", test2), + ("time", end_time2), + ("addSuccess", test2), + ("stopTest", test2), + # test1 finishes next, and thus follows. + ("time", start_time1), + ("startTest", test1), + ("time", end_time1), + ("addSuccess", test1), + ("stopTest", test1), + ], + events, + ) class TestMergeTags(TestCase): - def test_merge_unseen_gone_tag(self): # If an incoming "gone" tag isn't currently tagged one way or the # other, add it to the "gone" tags. - current_tags = {'present'}, {'missing'} - changing_tags = set(), {'going'} - expected = {'present'}, {'missing', 'going'} - self.assertEqual( - expected, _merge_tags(current_tags, changing_tags)) + current_tags = {"present"}, {"missing"} + changing_tags = set(), {"going"} + expected = {"present"}, {"missing", "going"} + self.assertEqual(expected, _merge_tags(current_tags, changing_tags)) def test_merge_incoming_gone_tag_with_current_new_tag(self): # If one of the incoming "gone" tags is one of the existing "new" # tags, then it overrides the "new" tag, leaving it marked as "gone". - current_tags = {'present', 'going'}, {'missing'} - changing_tags = set(), {'going'} - expected = {'present'}, {'missing', 'going'} - self.assertEqual( - expected, _merge_tags(current_tags, changing_tags)) + current_tags = {"present", "going"}, {"missing"} + changing_tags = set(), {"going"} + expected = {"present"}, {"missing", "going"} + self.assertEqual(expected, _merge_tags(current_tags, changing_tags)) def test_merge_unseen_new_tag(self): - current_tags = {'present'}, {'missing'} - changing_tags = {'coming'}, set() - expected = {'coming', 'present'}, {'missing'} - self.assertEqual( - expected, _merge_tags(current_tags, changing_tags)) + current_tags = {"present"}, {"missing"} + changing_tags = {"coming"}, set() + expected = {"coming", "present"}, {"missing"} + self.assertEqual(expected, _merge_tags(current_tags, changing_tags)) def test_merge_incoming_new_tag_with_current_gone_tag(self): # If one of the incoming "new" tags is currently marked as "gone", # then it overrides the "gone" tag, leaving it marked as "new". - current_tags = {'present'}, {'coming', 'missing'} - changing_tags = {'coming'}, set() - expected = {'coming', 'present'}, {'missing'} - self.assertEqual( - expected, _merge_tags(current_tags, changing_tags)) + current_tags = {"present"}, {"coming", "missing"} + changing_tags = {"coming"}, set() + expected = {"coming", "present"}, {"missing"} + self.assertEqual(expected, _merge_tags(current_tags, changing_tags)) class TestStreamResultRouter(TestCase): - def test_start_stop_test_run_no_fallback(self): result = StreamResultRouter() result.startTestRun() result.stopTestRun() def test_no_fallback_errors(self): - self.assertRaises(Exception, StreamResultRouter().status, test_id='f') + self.assertRaises(Exception, StreamResultRouter().status, test_id="f") def test_fallback_calls(self): fallback = LoggingStreamResult() result = StreamResultRouter(fallback) result.startTestRun() - result.status(test_id='foo') + result.status(test_id="foo") result.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('status', 'foo', None, None, True, None, None, False, None, None, - None), - ('stopTestRun',), + self.assertEqual( + [ + ("startTestRun",), + ( + "status", + "foo", + None, + None, + True, + None, + None, + False, + None, + None, + None, + ), + ("stopTestRun",), ], - fallback._events) + fallback._events, + ) def test_fallback_no_do_start_stop_run(self): fallback = LoggingStreamResult() result = StreamResultRouter(fallback, do_start_stop_run=False) result.startTestRun() - result.status(test_id='foo') + result.status(test_id="foo") result.stopTestRun() - self.assertEqual([ - ('status', 'foo', None, None, True, None, None, False, None, None, - None) - ], - fallback._events) + self.assertEqual( + [("status", "foo", None, None, True, None, None, False, None, None, None)], + fallback._events, + ) def test_add_rule_bad_policy(self): router = StreamResultRouter() target = LoggingStreamResult() - self.assertRaises(ValueError, router.add_rule, target, 'route_code_prefixa', - route_prefix='0') + self.assertRaises( + ValueError, router.add_rule, target, "route_code_prefixa", route_prefix="0" + ) def test_add_rule_extra_policy_arg(self): router = StreamResultRouter() target = LoggingStreamResult() - self.assertRaises(TypeError, router.add_rule, target, 'route_code_prefix', - route_prefix='0', foo=1) + self.assertRaises( + TypeError, + router.add_rule, + target, + "route_code_prefix", + route_prefix="0", + foo=1, + ) def test_add_rule_missing_prefix(self): router = StreamResultRouter() target = LoggingStreamResult() - self.assertRaises(TypeError, router.add_rule, target, 'route_code_prefix') + self.assertRaises(TypeError, router.add_rule, target, "route_code_prefix") def test_add_rule_slash_in_prefix(self): router = StreamResultRouter() target = LoggingStreamResult() - self.assertRaises(TypeError, router.add_rule, target, 'route_code_prefix', - route_prefix='0/') + self.assertRaises( + TypeError, router.add_rule, target, "route_code_prefix", route_prefix="0/" + ) def test_add_rule_route_code_consume_False(self): fallback = LoggingStreamResult() target = LoggingStreamResult() router = StreamResultRouter(fallback) - router.add_rule(target, 'route_code_prefix', route_prefix='0') - router.status(test_id='foo', route_code='0') - router.status(test_id='foo', route_code='0/1') - router.status(test_id='foo') - self.assertEqual([ - ('status', 'foo', None, None, True, None, None, False, None, '0', - None), - ('status', 'foo', None, None, True, None, None, False, None, '0/1', - None), + router.add_rule(target, "route_code_prefix", route_prefix="0") + router.status(test_id="foo", route_code="0") + router.status(test_id="foo", route_code="0/1") + router.status(test_id="foo") + self.assertEqual( + [ + ("status", "foo", None, None, True, None, None, False, None, "0", None), + ( + "status", + "foo", + None, + None, + True, + None, + None, + False, + None, + "0/1", + None, + ), ], - target._events) - self.assertEqual([ - ('status', 'foo', None, None, True, None, None, False, None, None, - None), + target._events, + ) + self.assertEqual( + [ + ( + "status", + "foo", + None, + None, + True, + None, + None, + False, + None, + None, + None, + ), ], - fallback._events) + fallback._events, + ) def test_add_rule_route_code_consume_True(self): fallback = LoggingStreamResult() target = LoggingStreamResult() router = StreamResultRouter(fallback) router.add_rule( - target, 'route_code_prefix', route_prefix='0', consume_route=True) - router.status(test_id='foo', route_code='0') # -> None - router.status(test_id='foo', route_code='0/1') # -> 1 - router.status(test_id='foo', route_code='1') # -> fallback as-is. - self.assertEqual([ - ('status', 'foo', None, None, True, None, None, False, None, None, - None), - ('status', 'foo', None, None, True, None, None, False, None, '1', - None), + target, "route_code_prefix", route_prefix="0", consume_route=True + ) + router.status(test_id="foo", route_code="0") # -> None + router.status(test_id="foo", route_code="0/1") # -> 1 + router.status(test_id="foo", route_code="1") # -> fallback as-is. + self.assertEqual( + [ + ( + "status", + "foo", + None, + None, + True, + None, + None, + False, + None, + None, + None, + ), + ("status", "foo", None, None, True, None, None, False, None, "1", None), ], - target._events) - self.assertEqual([ - ('status', 'foo', None, None, True, None, None, False, None, '1', - None), + target._events, + ) + self.assertEqual( + [ + ("status", "foo", None, None, True, None, None, False, None, "1", None), ], - fallback._events) + fallback._events, + ) def test_add_rule_test_id(self): nontest = LoggingStreamResult() test = LoggingStreamResult() router = StreamResultRouter(test) - router.add_rule(nontest, 'test_id', test_id=None) - router.status(test_id='foo', file_name="bar", file_bytes=b'') - router.status(file_name="bar", file_bytes=b'') - self.assertEqual([ - ('status', 'foo', None, None, True, 'bar', b'', False, None, None, - None),], test._events) - self.assertEqual([ - ('status', None, None, None, True, 'bar', b'', False, None, None, - None),], nontest._events) + router.add_rule(nontest, "test_id", test_id=None) + router.status(test_id="foo", file_name="bar", file_bytes=b"") + router.status(file_name="bar", file_bytes=b"") + self.assertEqual( + [ + ( + "status", + "foo", + None, + None, + True, + "bar", + b"", + False, + None, + None, + None, + ), + ], + test._events, + ) + self.assertEqual( + [ + ("status", None, None, None, True, "bar", b"", False, None, None, None), + ], + nontest._events, + ) def test_add_rule_do_start_stop_run(self): nontest = LoggingStreamResult() router = StreamResultRouter() - router.add_rule(nontest, 'test_id', test_id=None, do_start_stop_run=True) + router.add_rule(nontest, "test_id", test_id=None, do_start_stop_run=True) router.startTestRun() router.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('stopTestRun',), - ], nontest._events) + self.assertEqual( + [ + ("startTestRun",), + ("stopTestRun",), + ], + nontest._events, + ) def test_add_rule_do_start_stop_run_after_startTestRun(self): nontest = LoggingStreamResult() router = StreamResultRouter() router.startTestRun() - router.add_rule(nontest, 'test_id', test_id=None, do_start_stop_run=True) + router.add_rule(nontest, "test_id", test_id=None, do_start_stop_run=True) router.stopTestRun() - self.assertEqual([ - ('startTestRun',), - ('stopTestRun',), - ], nontest._events) + self.assertEqual( + [ + ("startTestRun",), + ("stopTestRun",), + ], + nontest._events, + ) class TestStreamToQueue(TestCase): - def make_result(self): queue = Queue() return queue, StreamToQueue(queue, "foo") def test_status(self): def check_event(event_dict, route=None, time=None): - self.assertEqual("status", event_dict['event']) - self.assertEqual("test", event_dict['test_id']) - self.assertEqual("fail", event_dict['test_status']) - self.assertEqual({"quux"}, event_dict['test_tags']) - self.assertEqual(False, event_dict['runnable']) - self.assertEqual("file", event_dict['file_name']) - self.assertEqual(_b("content"), event_dict['file_bytes']) - self.assertEqual(True, event_dict['eof']) - self.assertEqual("quux", event_dict['mime_type']) - self.assertEqual("test", event_dict['test_id']) - self.assertEqual(route, event_dict['route_code']) - self.assertEqual(time, event_dict['timestamp']) + self.assertEqual("status", event_dict["event"]) + self.assertEqual("test", event_dict["test_id"]) + self.assertEqual("fail", event_dict["test_status"]) + self.assertEqual({"quux"}, event_dict["test_tags"]) + self.assertEqual(False, event_dict["runnable"]) + self.assertEqual("file", event_dict["file_name"]) + self.assertEqual(_b("content"), event_dict["file_bytes"]) + self.assertEqual(True, event_dict["eof"]) + self.assertEqual("quux", event_dict["mime_type"]) + self.assertEqual("test", event_dict["test_id"]) + self.assertEqual(route, event_dict["route_code"]) + self.assertEqual(time, event_dict["timestamp"]) + queue, result = self.make_result() - result.status("test", "fail", test_tags={"quux"}, runnable=False, - file_name="file", file_bytes=_b("content"), eof=True, - mime_type="quux", route_code=None, timestamp=None) + result.status( + "test", + "fail", + test_tags={"quux"}, + runnable=False, + file_name="file", + file_bytes=_b("content"), + eof=True, + mime_type="quux", + route_code=None, + timestamp=None, + ) self.assertEqual(1, queue.qsize()) a_time = datetime.datetime.now(utc) - result.status("test", "fail", test_tags={"quux"}, runnable=False, - file_name="file", file_bytes=_b("content"), eof=True, - mime_type="quux", route_code="bar", timestamp=a_time) + result.status( + "test", + "fail", + test_tags={"quux"}, + runnable=False, + file_name="file", + file_bytes=_b("content"), + eof=True, + mime_type="quux", + route_code="bar", + timestamp=a_time, + ) self.assertEqual(2, queue.qsize()) check_event(queue.get(False), route="foo", time=None) check_event(queue.get(False), route="foo/bar", time=a_time) @@ -2053,20 +2382,17 @@ def check_event(event_dict, route=None, time=None): def testStartTestRun(self): queue, result = self.make_result() result.startTestRun() - self.assertEqual( - {'event':'startTestRun', 'result':result}, queue.get(False)) + self.assertEqual({"event": "startTestRun", "result": result}, queue.get(False)) self.assertTrue(queue.empty()) def testStopTestRun(self): queue, result = self.make_result() result.stopTestRun() - self.assertEqual( - {'event':'stopTestRun', 'result':result}, queue.get(False)) + self.assertEqual({"event": "stopTestRun", "result": result}, queue.get(False)) self.assertTrue(queue.empty()) class TestExtendedToOriginalResultDecoratorBase(TestCase): - def make_26_result(self): self.result = Python26TestResult() self.make_converter() @@ -2086,34 +2412,44 @@ def check_outcome_details(self, outcome): """Call an outcome with a details dict to be passed through.""" # This dict is /not/ convertible - thats deliberate, as it should # not hit the conversion code path. - details = {'foo': 'bar'} + details = {"foo": "bar"} getattr(self.converter, outcome)(self, details=details) self.assertEqual([(outcome, self, details)], self.result._events) def get_details_and_string(self): """Get a details dict and expected string.""" + def text1(): - return [_b('1\n2\n')] + return [_b("1\n2\n")] + def text2(): - return [_b('3\n4\n')] + return [_b("3\n4\n")] + def bin1(): - return [_b('5\n')] - details = {'text 1': Content(ContentType('text', 'plain'), text1), - 'text 2': Content(ContentType('text', 'strange'), text2), - 'bin 1': Content(ContentType('application', 'binary'), bin1)} - return (details, - ("Binary content:\n" - " bin 1 (application/binary)\n" - "\n" - "text 1: {{{\n" - "1\n" - "2\n" - "}}}\n" - "\n" - "text 2: {{{\n" - "3\n" - "4\n" - "}}}\n")) + return [_b("5\n")] + + details = { + "text 1": Content(ContentType("text", "plain"), text1), + "text 2": Content(ContentType("text", "strange"), text2), + "bin 1": Content(ContentType("application", "binary"), bin1), + } + return ( + details, + ( + "Binary content:\n" + " bin 1 (application/binary)\n" + "\n" + "text 1: {{{\n" + "1\n" + "2\n" + "}}}\n" + "\n" + "text 2: {{{\n" + "3\n" + "4\n" + "}}}\n" + ), + ) def check_outcome_details_to_exec_info(self, outcome, expected=None): """Call an outcome with a details dict to be made into exc_info.""" @@ -2130,7 +2466,7 @@ def check_outcome_details_to_nothing(self, outcome, expected=None): """Call an outcome with a details dict to be swallowed.""" if not expected: expected = outcome - details = {'foo': 'bar'} + details = {"foo": "bar"} getattr(self.converter, outcome)(self, details=details) self.assertEqual([(expected, self)], self.result._events) @@ -2186,14 +2522,12 @@ def check_outcome_string(self, outcome): self.assertEqual([(outcome, self, "foo")], self.result._events) -class TestExtendedToOriginalResultDecorator( - TestExtendedToOriginalResultDecoratorBase): - +class TestExtendedToOriginalResultDecorator(TestExtendedToOriginalResultDecoratorBase): def test_failfast_py26(self): self.make_26_result() self.assertEqual(False, self.converter.failfast) self.converter.failfast = True - self.assertFalse(hasattr(self.converter.decorated, 'failfast')) + self.assertFalse(hasattr(self.converter.decorated, "failfast")) def test_failfast_py27(self): self.make_27_result() @@ -2213,7 +2547,7 @@ def test_progress_py27(self): def test_progress_pyextended(self): self.make_extended_result() self.converter.progress(1, 2) - self.assertEqual([('progress', 1, 2)], self.result._events) + self.assertEqual([("progress", 1, 2)], self.result._events) def test_shouldStop(self): self.make_26_result() @@ -2224,17 +2558,17 @@ def test_shouldStop(self): def test_startTest_py26(self): self.make_26_result() self.converter.startTest(self) - self.assertEqual([('startTest', self)], self.result._events) + self.assertEqual([("startTest", self)], self.result._events) def test_startTest_py27(self): self.make_27_result() self.converter.startTest(self) - self.assertEqual([('startTest', self)], self.result._events) + self.assertEqual([("startTest", self)], self.result._events) def test_startTest_pyextended(self): self.make_extended_result() self.converter.startTest(self) - self.assertEqual([('startTest', self)], self.result._events) + self.assertEqual([("startTest", self)], self.result._events) def test_startTestRun_py26(self): self.make_26_result() @@ -2244,27 +2578,27 @@ def test_startTestRun_py26(self): def test_startTestRun_py27(self): self.make_27_result() self.converter.startTestRun() - self.assertEqual([('startTestRun',)], self.result._events) + self.assertEqual([("startTestRun",)], self.result._events) def test_startTestRun_pyextended(self): self.make_extended_result() self.converter.startTestRun() - self.assertEqual([('startTestRun',)], self.result._events) + self.assertEqual([("startTestRun",)], self.result._events) def test_stopTest_py26(self): self.make_26_result() self.converter.stopTest(self) - self.assertEqual([('stopTest', self)], self.result._events) + self.assertEqual([("stopTest", self)], self.result._events) def test_stopTest_py27(self): self.make_27_result() self.converter.stopTest(self) - self.assertEqual([('stopTest', self)], self.result._events) + self.assertEqual([("stopTest", self)], self.result._events) def test_stopTest_pyextended(self): self.make_extended_result() self.converter.stopTest(self) - self.assertEqual([('stopTest', self)], self.result._events) + self.assertEqual([("stopTest", self)], self.result._events) def test_stopTestRun_py26(self): self.make_26_result() @@ -2274,12 +2608,12 @@ def test_stopTestRun_py26(self): def test_stopTestRun_py27(self): self.make_27_result() self.converter.stopTestRun() - self.assertEqual([('stopTestRun',)], self.result._events) + self.assertEqual([("stopTestRun",)], self.result._events) def test_stopTestRun_pyextended(self): self.make_extended_result() self.converter.stopTestRun() - self.assertEqual([('stopTestRun',)], self.result._events) + self.assertEqual([("stopTestRun",)], self.result._events) def test_tags_py26(self): self.make_26_result() @@ -2292,7 +2626,7 @@ def test_tags_py27(self): def test_tags_pyextended(self): self.make_extended_result() self.converter.tags({1}, {2}) - self.assertEqual([('tags', {1}, {2})], self.result._events) + self.assertEqual([("tags", {1}, {2})], self.result._events) def test_time_py26(self): self.make_26_result() @@ -2305,12 +2639,11 @@ def test_time_py27(self): def test_time_pyextended(self): self.make_extended_result() self.converter.time(1) - self.assertEqual([('time', 1)], self.result._events) + self.assertEqual([("time", 1)], self.result._events) class TestExtendedToOriginalAddError(TestExtendedToOriginalResultDecoratorBase): - - outcome = 'addError' + outcome = "addError" def test_outcome_Original_py26(self): self.make_26_result() @@ -2340,38 +2673,32 @@ def test_outcome__no_details(self): self.make_extended_result() self.assertThat( lambda: getattr(self.converter, self.outcome)(self), - Raises(MatchesException(ValueError))) - - -class TestExtendedToOriginalAddFailure( - TestExtendedToOriginalAddError): + Raises(MatchesException(ValueError)), + ) - outcome = 'addFailure' +class TestExtendedToOriginalAddFailure(TestExtendedToOriginalAddError): + outcome = "addFailure" -class TestExtendedToOriginalAddExpectedFailure( - TestExtendedToOriginalAddError): - outcome = 'addExpectedFailure' +class TestExtendedToOriginalAddExpectedFailure(TestExtendedToOriginalAddError): + outcome = "addExpectedFailure" def test_outcome_Original_py26(self): self.make_26_result() - self.check_outcome_exc_info_to_nothing(self.outcome, 'addSuccess') + self.check_outcome_exc_info_to_nothing(self.outcome, "addSuccess") def test_outcome_Extended_py26(self): self.make_26_result() - self.check_outcome_details_to_nothing(self.outcome, 'addSuccess') - - + self.check_outcome_details_to_nothing(self.outcome, "addSuccess") -class TestExtendedToOriginalAddSkip( - TestExtendedToOriginalResultDecoratorBase): - outcome = 'addSkip' +class TestExtendedToOriginalAddSkip(TestExtendedToOriginalResultDecoratorBase): + outcome = "addSkip" def test_outcome_Original_py26(self): self.make_26_result() - self.check_outcome_string_nothing(self.outcome, 'addSuccess') + self.check_outcome_string_nothing(self.outcome, "addSuccess") def test_outcome_Original_py27(self): self.make_27_result() @@ -2383,7 +2710,7 @@ def test_outcome_Original_pyextended(self): def test_outcome_Extended_py26(self): self.make_26_result() - self.check_outcome_string_nothing(self.outcome, 'addSuccess') + self.check_outcome_string_nothing(self.outcome, "addSuccess") def test_outcome_Extended_py27_no_reason(self): self.make_27_result() @@ -2391,8 +2718,9 @@ def test_outcome_Extended_py27_no_reason(self): def test_outcome_Extended_py27_reason(self): self.make_27_result() - self.check_outcome_details_to_arg(self.outcome, 'foo', - {'reason': Content(UTF8_TEXT, lambda:[_b('foo')])}) + self.check_outcome_details_to_arg( + self.outcome, "foo", {"reason": Content(UTF8_TEXT, lambda: [_b("foo")])} + ) def test_outcome_Extended_pyextended(self): self.make_extended_result() @@ -2402,14 +2730,13 @@ def test_outcome__no_details(self): self.make_extended_result() self.assertThat( lambda: getattr(self.converter, self.outcome)(self), - Raises(MatchesException(ValueError))) - + Raises(MatchesException(ValueError)), + ) -class TestExtendedToOriginalAddSuccess( - TestExtendedToOriginalResultDecoratorBase): - outcome = 'addSuccess' - expected = 'addSuccess' +class TestExtendedToOriginalAddSuccess(TestExtendedToOriginalResultDecoratorBase): + outcome = "addSuccess" + expected = "addSuccess" def test_outcome_Original_py26(self): self.make_26_result() @@ -2437,10 +2764,10 @@ def test_outcome_Extended_pyextended(self): class TestExtendedToOriginalAddUnexpectedSuccess( - TestExtendedToOriginalResultDecoratorBase): - - outcome = 'addUnexpectedSuccess' - expected = 'addFailure' + TestExtendedToOriginalResultDecoratorBase +): + outcome = "addUnexpectedSuccess" + expected = "addFailure" def test_outcome_Original_py26(self): self.make_26_result() @@ -2472,13 +2799,15 @@ def test_outcome_Extended_pyextended(self): class TestExtendedToOriginalResultOtherAttributes( - TestExtendedToOriginalResultDecoratorBase): - + TestExtendedToOriginalResultDecoratorBase +): def test_other_attribute(self): class OtherExtendedResult: def foo(self): return 2 + bar = 1 + self.result = OtherExtendedResult() self.make_converter() self.assertEqual(1, self.converter.bar) @@ -2495,10 +2824,10 @@ class TestNonAsciiResults(TestCase): """ _sample_texts = ( - "pa\u026a\u03b8\u0259n", # Unicode encodings only - "\u5357\u7121", # In ISO 2022 encodings - "\xa7\xa7\xa7", # In ISO 8859 encodings - ) + "pa\u026a\u03b8\u0259n", # Unicode encodings only + "\u5357\u7121", # In ISO 2022 encodings + "\xa7\xa7\xa7", # In ISO 8859 encodings + ) _is_pypy = "__pypy__" in sys.builtin_module_names # Everything but Jython shows syntax errors on the current character @@ -2527,19 +2856,19 @@ def _write_module(self, name, encoding, contents): finally: f.close() - def _test_external_case(self, testline, coding="ascii", modulelevel="", - suffix=""): + def _test_external_case(self, testline, coding="ascii", modulelevel="", suffix=""): """Create and run a test case in a separate module""" self._setup_external_case(testline, coding, modulelevel, suffix) return self._run_external_case() - def _setup_external_case(self, testline, coding="ascii", modulelevel="", - suffix=""): + def _setup_external_case(self, testline, coding="ascii", modulelevel="", suffix=""): """Create a test case in a separate module""" _, prefix, self.modname = self.id().rsplit(".", 2) self.dir = tempfile.mkdtemp(prefix=prefix, suffix=suffix) self.addCleanup(shutil.rmtree, self.dir) - self._write_module(self.modname, coding, + self._write_module( + self.modname, + coding, # Older Python 2 versions don't see a coding declaration in a # docstring so it has to be in a comment, but then we can't # workaround bug: @@ -2548,7 +2877,8 @@ def _setup_external_case(self, testline, coding="ascii", modulelevel="", "%s\n" "class Test(testtools.TestCase):\n" " def runTest(self):\n" - " %s\n" % (coding, modulelevel, testline)) + " %s\n" % (coding, modulelevel, testline), + ) def _run_external_case(self): """Run the prepared test case in a separate module""" @@ -2562,12 +2892,12 @@ def _run_external_case(self): def _get_sample_text(self, encoding="unicode_internal"): if encoding is None: - encoding = "unicode_internal" + encoding = "unicode_internal" for u in self._sample_texts: try: b = u.encode(encoding) if u == b.decode(encoding): - return u, u + return u, u except (LookupError, UnicodeError): pass self.skipTest("Could not find a sample text for encoding: %r" % encoding) @@ -2585,43 +2915,46 @@ def test_non_ascii_failure_string_via_exec(self): """Assertion via exec can be non-ascii and still gets decoded""" text, raw = self._get_sample_text(_get_exception_encoding()) textoutput = self._test_external_case( - testline='exec ("self.fail(%s)")' % ascii(raw)) + testline='exec ("self.fail(%s)")' % ascii(raw) + ) self.assertIn(self._as_output(text), textoutput) def test_control_characters_in_failure_string(self): """Control characters in assertions should be escaped""" textoutput = self._test_external_case("self.fail('\\a\\a\\a')") - self.expectFailure("Defense against the beeping horror unimplemented", - self.assertNotIn, self._as_output("\a\a\a"), textoutput) + self.expectFailure( + "Defense against the beeping horror unimplemented", + self.assertNotIn, + self._as_output("\a\a\a"), + textoutput, + ) self.assertIn(self._as_output("\uFFFD\uFFFD\uFFFD"), textoutput) def _local_os_error_matcher(self): - return MatchesAny(Contains("FileExistsError: "), - Contains("PermissionError: ")) + return MatchesAny(Contains("FileExistsError: "), Contains("PermissionError: ")) def test_os_error(self): """Locale error messages from the OS shouldn't break anything""" textoutput = self._test_external_case( - modulelevel="import os", - testline="os.mkdir('/')") + modulelevel="import os", testline="os.mkdir('/')" + ) self.assertThat(textoutput, self._local_os_error_matcher()) def test_assertion_text_shift_jis(self): """A terminal raw backslash in an encoded string is weird but fine""" example_text = "\u5341" textoutput = self._test_external_case( - coding="shift_jis", - testline="self.fail('%s')" % example_text) + coding="shift_jis", testline="self.fail('%s')" % example_text + ) output_text = example_text - self.assertIn(self._as_output("AssertionError: %s" % output_text), - textoutput) + self.assertIn(self._as_output("AssertionError: %s" % output_text), textoutput) def test_file_comment_iso2022_jp(self): """Control character escapes must be preserved if valid encoding""" example_text, _ = self._get_sample_text("iso2022_jp") textoutput = self._test_external_case( - coding="iso2022_jp", - testline="self.fail('Simple') # %s" % example_text) + coding="iso2022_jp", testline="self.fail('Simple') # %s" % example_text + ) self.assertIn(self._as_output(example_text), textoutput) def test_unicode_exception(self): @@ -2631,10 +2964,12 @@ def test_unicode_exception(self): "class FancyError(Exception):\n" # A __unicode__ method does nothing on py3k but the default works " def __unicode__(self):\n" - " return self.args[0]\n") + " return self.args[0]\n" + ) textoutput = self._test_external_case( modulelevel=exception_class, - testline="raise FancyError(%s)" % ascii(example_text)) + testline="raise FancyError(%s)" % ascii(example_text), + ) self.assertIn(self._as_output(example_text), textoutput) def test_unprintable_exception(self): @@ -2646,15 +2981,15 @@ def test_unprintable_exception(self): " def __unicode__(self):\n" " raise RuntimeError\n" " def __repr__(self):\n" - " raise RuntimeError\n") + " raise RuntimeError\n" + ) if sys.version_info >= (3, 11): expected = "UnprintableError: \n" else: - expected = ( - "UnprintableError: \n") + expected = "UnprintableError: \n" textoutput = self._test_external_case( - modulelevel=exception_class, - testline="raise UnprintableError") + modulelevel=exception_class, testline="raise UnprintableError" + ) self.assertIn(self._as_output(expected), textoutput) def test_non_ascii_dirname(self): @@ -2665,26 +3000,32 @@ def test_non_ascii_dirname(self): # than just ascii which raises a SyntaxError with no other details coding="utf-8", testline="self.fail('Simple')", - suffix=raw) + suffix=raw, + ) self.assertIn(self._as_output(text), textoutput) def test_syntax_error(self): """Syntax errors should still have fancy special-case formatting""" if platform.python_implementation() == "PyPy": - spaces = ' ' + spaces = " " elif sys.version_info >= (3, 10): - spaces = ' ' + spaces = " " else: - spaces = ' ' - marker = '^^^' if sys.version_info >= (3, 10) else '^' + spaces = " " + marker = "^^^" if sys.version_info >= (3, 10) else "^" textoutput = self._test_external_case("exec ('f(a, b c)')") - self.assertIn(self._as_output( - ' File "", line 1\n' - ' f(a, b c)\n' - + ' ' * self._error_on_character + - spaces + marker + '\n' - 'SyntaxError: ' - ), textoutput) + self.assertIn( + self._as_output( + ' File "", line 1\n' + " f(a, b c)\n" + + " " * self._error_on_character + + spaces + + marker + + "\n" + "SyntaxError: " + ), + textoutput, + ) def test_syntax_error_malformed(self): """Syntax errors with bogus parameters should break anything""" @@ -2695,53 +3036,61 @@ def test_syntax_error_line_iso_8859_1(self): """Syntax error on a latin-1 line shows the line decoded""" text, raw = self._get_sample_text("iso-8859-1") textoutput = self._setup_external_case("import bad") - self._write_module("bad", "iso-8859-1", - "# coding: iso-8859-1\n! = 0 # %s\n" % text) + self._write_module( + "bad", "iso-8859-1", "# coding: iso-8859-1\n! = 0 # %s\n" % text + ) textoutput = self._run_external_case() - self.assertIn(self._as_output( - #'bad.py", line 2\n' - ' ! = 0 # %s\n' - ' ^\n' - 'SyntaxError: ' % - (text,)), textoutput) + self.assertIn( + self._as_output( + #'bad.py", line 2\n' + " ! = 0 # %s\n" " ^\n" "SyntaxError: " % (text,) + ), + textoutput, + ) def test_syntax_error_line_iso_8859_5(self): """Syntax error on a iso-8859-5 line shows the line decoded""" text, raw = self._get_sample_text("iso-8859-5") textoutput = self._setup_external_case("import bad") - self._write_module("bad", "iso-8859-5", - "# coding: iso-8859-5\n%% = 0 # %s\n" % text) + self._write_module( + "bad", "iso-8859-5", "# coding: iso-8859-5\n%% = 0 # %s\n" % text + ) textoutput = self._run_external_case() self.assertThat( textoutput, MatchesRegex( - self._as_output(( - #'bad.py", line 2\n' - '.*%% = 0 # %s\n' - + ' ' * self._error_on_character + - '\\s*\\^\n' - 'SyntaxError:.*') % - (text,)), - re.MULTILINE | re.DOTALL) + self._as_output( + ( + #'bad.py", line 2\n' + ".*%% = 0 # %s\n" + " " * self._error_on_character + "\\s*\\^\n" + "SyntaxError:.*" + ) + % (text,) + ), + re.MULTILINE | re.DOTALL, + ), ) def test_syntax_error_line_euc_jp(self): """Syntax error on a euc_jp line shows the line decoded""" text, raw = self._get_sample_text("euc_jp") textoutput = self._setup_external_case("import bad") - self._write_module("bad", "euc_jp", - "# coding: euc_jp\n$ = 0 # %s\n" % text) + self._write_module("bad", "euc_jp", "# coding: euc_jp\n$ = 0 # %s\n" % text) textoutput = self._run_external_case() # pypy uses cpython's multibyte codecs so has their behavior here if self._is_pypy: self._error_on_character = True - self.assertIn(self._as_output(( - #'bad.py", line 2\n' - ' $ = 0 # %s\n' - + ' ' * self._error_on_character + - ' ^\n' - 'SyntaxError: ') % - (text,)), textoutput) + self.assertIn( + self._as_output( + ( + #'bad.py", line 2\n' + " $ = 0 # %s\n" + " " * self._error_on_character + " ^\n" + "SyntaxError: " + ) + % (text,) + ), + textoutput, + ) def test_syntax_error_line_utf_8(self): """Syntax error on a utf-8 line shows the line decoded""" @@ -2750,18 +3099,24 @@ def test_syntax_error_line_utf_8(self): self._write_module("bad", "utf-8", "\ufeff^ = 0 # %s\n" % text) textoutput = self._run_external_case() # Python 3.9 no longer prints the '\ufeff' - if sys.version_info >= (3,9): - textoutput = textoutput.replace('\ufeff', '') + if sys.version_info >= (3, 9): + textoutput = textoutput.replace("\ufeff", "") self.assertThat( textoutput, MatchesRegex( - self._as_output(( - '.*bad.py", line 1\n' - '\\s*\\^ = 0 # %s\n' - + ' ' * self._error_on_character + - '\\s*\\^\n' - 'SyntaxError:.*') % text), - re.M | re.S) + self._as_output( + ( + '.*bad.py", line 1\n' + "\\s*\\^ = 0 # %s\n" + + " " + * self._error_on_character + + "\\s*\\^\n" + "SyntaxError:.*" + ) + % text + ), + re.M | re.S, + ), ) @@ -2770,6 +3125,7 @@ class TestNonAsciiResultsWithUnittest(TestNonAsciiResults): def _run(self, stream, test): from unittest import TextTestRunner as _Runner + return _Runner(stream).run(test) def _as_output(self, text): @@ -2777,73 +3133,91 @@ def _as_output(self, text): class TestDetailsToStr(TestCase): - def test_no_details(self): string = _details_to_str({}) - self.assertThat(string, Equals('')) + self.assertThat(string, Equals("")) def test_binary_content(self): content = content_from_stream( - io.StringIO('foo'), content_type=ContentType('image', 'jpeg')) - string = _details_to_str({'attachment': content}) + io.StringIO("foo"), content_type=ContentType("image", "jpeg") + ) + string = _details_to_str({"attachment": content}) self.assertThat( - string, Equals("""\ + string, + Equals( + """\ Binary content: attachment (image/jpeg) -""")) +""" + ), + ) def test_single_line_content(self): - content = text_content('foo') - string = _details_to_str({'attachment': content}) - self.assertThat(string, Equals('attachment: {{{foo}}}\n')) + content = text_content("foo") + string = _details_to_str({"attachment": content}) + self.assertThat(string, Equals("attachment: {{{foo}}}\n")) def test_multi_line_text_content(self): - content = text_content('foo\nbar\nbaz') - string = _details_to_str({'attachment': content}) - self.assertThat(string, Equals('attachment: {{{\nfoo\nbar\nbaz\n}}}\n')) + content = text_content("foo\nbar\nbaz") + string = _details_to_str({"attachment": content}) + self.assertThat(string, Equals("attachment: {{{\nfoo\nbar\nbaz\n}}}\n")) def test_special_text_content(self): - content = text_content('foo') - string = _details_to_str({'attachment': content}, special='attachment') - self.assertThat(string, Equals('foo\n')) + content = text_content("foo") + string = _details_to_str({"attachment": content}, special="attachment") + self.assertThat(string, Equals("foo\n")) def test_multiple_text_content(self): string = _details_to_str( - {'attachment': text_content('foo\nfoo'), - 'attachment-1': text_content('bar\nbar')}) + { + "attachment": text_content("foo\nfoo"), + "attachment-1": text_content("bar\nbar"), + } + ) self.assertThat( - string, Equals('attachment: {{{\n' - 'foo\n' - 'foo\n' - '}}}\n' - '\n' - 'attachment-1: {{{\n' - 'bar\n' - 'bar\n' - '}}}\n')) + string, + Equals( + "attachment: {{{\n" + "foo\n" + "foo\n" + "}}}\n" + "\n" + "attachment-1: {{{\n" + "bar\n" + "bar\n" + "}}}\n" + ), + ) def test_empty_attachment(self): - string = _details_to_str({'attachment': text_content('')}) + string = _details_to_str({"attachment": text_content("")}) self.assertThat( - string, Equals("""\ + string, + Equals( + """\ Empty attachments: attachment -""")) +""" + ), + ) def test_lots_of_different_attachments(self): def jpg(x): - return content_from_stream(io.StringIO(x), ContentType('image', 'jpeg')) + return content_from_stream(io.StringIO(x), ContentType("image", "jpeg")) + attachments = { - 'attachment': text_content('foo'), - 'attachment-1': text_content('traceback'), - 'attachment-2': jpg('pic1'), - 'attachment-3': text_content('bar'), - 'attachment-4': text_content(''), - 'attachment-5': jpg('pic2'), - } - string = _details_to_str(attachments, special='attachment-1') + "attachment": text_content("foo"), + "attachment-1": text_content("traceback"), + "attachment-2": jpg("pic1"), + "attachment-3": text_content("bar"), + "attachment-4": text_content(""), + "attachment-5": jpg("pic2"), + } + string = _details_to_str(attachments, special="attachment-1") self.assertThat( - string, Equals("""\ + string, + Equals( + """\ Binary content: attachment-2 (image/jpeg) attachment-5 (image/jpeg) @@ -2854,11 +3228,12 @@ def jpg(x): attachment-3: {{{bar}}} traceback -""")) +""" + ), + ) class TestByTestResultTests(TestCase): - def setUp(self): super().setUp() self.log = [] @@ -2868,12 +3243,12 @@ def setUp(self): def assertCalled(self, **kwargs): defaults = { - 'test': self, - 'tags': set(), - 'details': None, - 'start_time': 0, - 'stop_time': 1, - } + "test": self, + "tags": set(), + "details": None, + "start_time": 0, + "stop_time": 1, + } defaults.update(kwargs) self.assertEqual([defaults], self.log) @@ -2889,48 +3264,48 @@ def test_add_success(self): self.result.startTest(self) self.result.addSuccess(self) self.result.stopTest(self) - self.assertCalled(status='success') + self.assertCalled(status="success") def test_add_success_details(self): self.result.startTest(self) - details = {'foo': 'bar'} + details = {"foo": "bar"} self.result.addSuccess(self, details=details) self.result.stopTest(self) - self.assertCalled(status='success', details=details) + self.assertCalled(status="success", details=details) def test_global_tags(self): - self.result.tags(['foo'], []) + self.result.tags(["foo"], []) self.result.startTest(self) self.result.addSuccess(self) self.result.stopTest(self) - self.assertCalled(status='success', tags={'foo'}) + self.assertCalled(status="success", tags={"foo"}) def test_local_tags(self): - self.result.tags(['foo'], []) + self.result.tags(["foo"], []) self.result.startTest(self) - self.result.tags(['bar'], []) + self.result.tags(["bar"], []) self.result.addSuccess(self) self.result.stopTest(self) - self.assertCalled(status='success', tags={'foo', 'bar'}) + self.assertCalled(status="success", tags={"foo", "bar"}) def test_add_error(self): self.result.startTest(self) try: - 1/0 + 1 / 0 except ZeroDivisionError: error = sys.exc_info() self.result.addError(self, error) self.result.stopTest(self) self.assertCalled( - status='error', - details={'traceback': TracebackContent(error, self)}) + status="error", details={"traceback": TracebackContent(error, self)} + ) def test_add_error_details(self): self.result.startTest(self) details = {"foo": text_content("bar")} self.result.addError(self, details=details) self.result.stopTest(self) - self.assertCalled(status='error', details=details) + self.assertCalled(status="error", details=details) def test_add_failure(self): self.result.startTest(self) @@ -2941,86 +3316,90 @@ def test_add_failure(self): self.result.addFailure(self, failure) self.result.stopTest(self) self.assertCalled( - status='failure', - details={'traceback': TracebackContent(failure, self)}) + status="failure", details={"traceback": TracebackContent(failure, self)} + ) def test_add_failure_details(self): self.result.startTest(self) details = {"foo": text_content("bar")} self.result.addFailure(self, details=details) self.result.stopTest(self) - self.assertCalled(status='failure', details=details) + self.assertCalled(status="failure", details=details) def test_add_xfail(self): self.result.startTest(self) try: - 1/0 + 1 / 0 except ZeroDivisionError: error = sys.exc_info() self.result.addExpectedFailure(self, error) self.result.stopTest(self) self.assertCalled( - status='xfail', - details={'traceback': TracebackContent(error, self)}) + status="xfail", details={"traceback": TracebackContent(error, self)} + ) def test_add_xfail_details(self): self.result.startTest(self) details = {"foo": text_content("bar")} self.result.addExpectedFailure(self, details=details) self.result.stopTest(self) - self.assertCalled(status='xfail', details=details) + self.assertCalled(status="xfail", details=details) def test_add_unexpected_success(self): self.result.startTest(self) - details = {'foo': 'bar'} + details = {"foo": "bar"} self.result.addUnexpectedSuccess(self, details=details) self.result.stopTest(self) - self.assertCalled(status='success', details=details) + self.assertCalled(status="success", details=details) def test_add_skip_reason(self): self.result.startTest(self) reason = self.getUniqueString() self.result.addSkip(self, reason) self.result.stopTest(self) - self.assertCalled( - status='skip', details={'reason': text_content(reason)}) + self.assertCalled(status="skip", details={"reason": text_content(reason)}) def test_add_skip_details(self): self.result.startTest(self) - details = {'foo': 'bar'} + details = {"foo": "bar"} self.result.addSkip(self, details=details) self.result.stopTest(self) - self.assertCalled(status='skip', details=details) + self.assertCalled(status="skip", details=details) def test_twice(self): self.result.startTest(self) - self.result.addSuccess(self, details={'foo': 'bar'}) + self.result.addSuccess(self, details={"foo": "bar"}) self.result.stopTest(self) self.result.startTest(self) self.result.addSuccess(self) self.result.stopTest(self) self.assertEqual( - [{'test': self, - 'status': 'success', - 'start_time': 0, - 'stop_time': 1, - 'tags': set(), - 'details': {'foo': 'bar'}}, - {'test': self, - 'status': 'success', - 'start_time': 2, - 'stop_time': 3, - 'tags': set(), - 'details': None}, - ], - self.log) + [ + { + "test": self, + "status": "success", + "start_time": 0, + "stop_time": 1, + "tags": set(), + "details": {"foo": "bar"}, + }, + { + "test": self, + "status": "success", + "start_time": 2, + "stop_time": 3, + "tags": set(), + "details": None, + }, + ], + self.log, + ) class TestTagger(TestCase): - def test_tags_tests(self): result = ExtendedTestResult() - tagger = Tagger(result, {'foo'}, {'bar'}) + tagger = Tagger(result, {"foo"}, {"bar"}) test1, test2 = self, make_test() tagger.startTest(test1) tagger.addSuccess(test1) @@ -3029,40 +3408,50 @@ def test_tags_tests(self): tagger.addSuccess(test2) tagger.stopTest(test2) self.assertEqual( - [('startTest', test1), - ('tags', {'foo'}, {'bar'}), - ('addSuccess', test1), - ('stopTest', test1), - ('startTest', test2), - ('tags', {'foo'}, {'bar'}), - ('addSuccess', test2), - ('stopTest', test2), - ], result._events) + [ + ("startTest", test1), + ("tags", {"foo"}, {"bar"}), + ("addSuccess", test1), + ("stopTest", test1), + ("startTest", test2), + ("tags", {"foo"}, {"bar"}), + ("addSuccess", test2), + ("stopTest", test2), + ], + result._events, + ) class TestTimestampingStreamResult(TestCase): - def test_startTestRun(self): result = TimestampingStreamResult(LoggingStreamResult()) result.startTestRun() - self.assertEqual([('startTestRun',)], result.targets[0]._events) + self.assertEqual([("startTestRun",)], result.targets[0]._events) def test_stopTestRun(self): result = TimestampingStreamResult(LoggingStreamResult()) result.stopTestRun() - self.assertEqual([('stopTestRun',)], result.targets[0]._events) + self.assertEqual([("stopTestRun",)], result.targets[0]._events) def test_status_no_timestamp(self): result = TimestampingStreamResult(LoggingStreamResult()) - result.status(test_id="A", test_status="B", test_tags="C", - runnable="D", file_name="E", file_bytes=b"F", eof=True, - mime_type="G", route_code="H") + result.status( + test_id="A", + test_status="B", + test_tags="C", + runnable="D", + file_name="E", + file_bytes=b"F", + eof=True, + mime_type="G", + route_code="H", + ) events = result.targets[0]._events self.assertThat(events, HasLength(1)) self.assertThat(events[0], HasLength(11)) self.assertEqual( - ("status", "A", "B", "C", "D", "E", b"F", True, "G", "H"), - events[0][:10]) + ("status", "A", "B", "C", "D", "E", b"F", True, "G", "H"), events[0][:10] + ) self.assertNotEqual(None, events[0][10]) self.assertIsInstance(events[0][10], datetime.datetime) @@ -3074,4 +3463,5 @@ def test_status_timestamp(self): def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_testsuite.py b/testtools/tests/test_testsuite.py index c490409e..ae7f13fe 100644 --- a/testtools/tests/test_testsuite.py +++ b/testtools/tests/test_testsuite.py @@ -13,46 +13,51 @@ PlaceHolder, TestByTestResult, TestCase, - ) +) from testtools.helpers import try_import from testtools.matchers import DocTestMatches, Equals from testtools.testresult.doubles import StreamResult as LoggingStream from testtools.testsuite import FixtureSuite, sorted_tests from testtools.tests.helpers import LoggingResult -FunctionFixture = try_import('fixtures.FunctionFixture') +FunctionFixture = try_import("fixtures.FunctionFixture") class Sample(TestCase): def __hash__(self): return id(self) + def test_method1(self): pass + def test_method2(self): pass class TestConcurrentTestSuiteRun(TestCase): - def test_broken_test(self): log = [] + def on_test(test, status, start_time, stop_time, tags, details): log.append((test.id(), status, set(details.keys()))) + class BrokenTest: # Simple break - no result parameter to run() def __call__(self): pass + run = __call__ + original_suite = unittest.TestSuite([BrokenTest()]) suite = ConcurrentTestSuite(original_suite, self.split_suite) suite.run(TestByTestResult(on_test)) - self.assertEqual([('broken-runner', 'error', {'traceback'})], log) + self.assertEqual([("broken-runner", "error", {"traceback"})], log) def test_trivial(self): log = [] result = LoggingResult(log) - test1 = Sample('test_method1') - test2 = Sample('test_method2') + test1 = Sample("test_method1") + test2 = Sample("test_method2") original_suite = unittest.TestSuite([test1, test2]) suite = ConcurrentTestSuite(original_suite, self.split_suite) suite.run(result) @@ -68,22 +73,25 @@ def test_wrap_result(self): wrap_log = [] def wrap_result(thread_safe_result, thread_number): - wrap_log.append( - (thread_safe_result.result.decorated, thread_number)) + wrap_log.append((thread_safe_result.result.decorated, thread_number)) return thread_safe_result result_log = [] result = LoggingResult(result_log) - test1 = Sample('test_method1') - test2 = Sample('test_method2') + test1 = Sample("test_method1") + test2 = Sample("test_method2") original_suite = unittest.TestSuite([test1, test2]) suite = ConcurrentTestSuite( - original_suite, self.split_suite, wrap_result=wrap_result) + original_suite, self.split_suite, wrap_result=wrap_result + ) suite.run(result) self.assertEqual( - [(result, 0), - (result, 1), - ], wrap_log) + [ + (result, 0), + (result, 1), + ], + wrap_log, + ) # Smoke test to make sure everything ran OK. self.assertNotEqual([], result_log) @@ -92,73 +100,85 @@ def split_suite(self, suite): class TestConcurrentStreamTestSuiteRun(TestCase): - def test_trivial(self): result = LoggingStream() - test1 = Sample('test_method1') - test2 = Sample('test_method2') + test1 = Sample("test_method1") + test2 = Sample("test_method2") + def cases(): - return [(test1, '0'), (test2, '1')] + return [(test1, "0"), (test2, "1")] + suite = ConcurrentStreamTestSuite(cases) suite.run(result) + def freeze(set_or_none): if set_or_none is None: return set_or_none return frozenset(set_or_none) + # Ignore event order: we're testing the code is all glued together, # which just means we can pump events through and they get route codes # added appropriately. - self.assertEqual({ - ('status', - 'testtools.tests.test_testsuite.Sample.test_method1', - 'inprogress', - None, - True, - None, - None, - False, - None, - '0', - None, - ), - ('status', - 'testtools.tests.test_testsuite.Sample.test_method1', - 'success', - frozenset(), - True, - None, - None, - False, - None, - '0', - None, - ), - ('status', - 'testtools.tests.test_testsuite.Sample.test_method2', - 'inprogress', - None, - True, - None, - None, - False, - None, - '1', - None, - ), - ('status', - 'testtools.tests.test_testsuite.Sample.test_method2', - 'success', - frozenset(), - True, - None, - None, - False, - None, - '1', - None, - ), - }, {event[0:3] + (freeze(event[3]),) + event[4:10] + (None,) - for event in result._events}) + self.assertEqual( + { + ( + "status", + "testtools.tests.test_testsuite.Sample.test_method1", + "inprogress", + None, + True, + None, + None, + False, + None, + "0", + None, + ), + ( + "status", + "testtools.tests.test_testsuite.Sample.test_method1", + "success", + frozenset(), + True, + None, + None, + False, + None, + "0", + None, + ), + ( + "status", + "testtools.tests.test_testsuite.Sample.test_method2", + "inprogress", + None, + True, + None, + None, + False, + None, + "1", + None, + ), + ( + "status", + "testtools.tests.test_testsuite.Sample.test_method2", + "success", + frozenset(), + True, + None, + None, + False, + None, + "1", + None, + ), + }, + { + event[0:3] + (freeze(event[3]),) + event[4:10] + (None,) + for event in result._events + }, + ) def test_broken_runner(self): # If the object called breaks, the stream is informed about it @@ -167,47 +187,115 @@ class BrokenTest: # broken - no result parameter! def __call__(self): pass + def run(self): pass + result = LoggingStream() + def cases(): - return [(BrokenTest(), '0')] + return [(BrokenTest(), "0")] + suite = ConcurrentStreamTestSuite(cases) suite.run(result) events = result._events # Check the traceback loosely. - self.assertEqual(events[1][6].decode('utf8'), - "Traceback (most recent call last):\n") - self.assertThat(events[2][6].decode('utf8'), DocTestMatches("""\ + self.assertEqual( + events[1][6].decode("utf8"), "Traceback (most recent call last):\n" + ) + self.assertThat( + events[2][6].decode("utf8"), + DocTestMatches( + """\ File "...testtools/testsuite.py", line ..., in _run_test test.run(process_result)... -""", doctest.ELLIPSIS)) - self.assertThat(events[3][6].decode('utf8'), DocTestMatches("""\ +""", + doctest.ELLIPSIS, + ), + ) + self.assertThat( + events[3][6].decode("utf8"), + DocTestMatches( + """\ TypeError: ...run() takes ...1 ...argument...2...given... -""", doctest.ELLIPSIS)) +""", + doctest.ELLIPSIS, + ), + ) events = [event[0:10] + (None,) for event in events] events[1] = events[1][:6] + (None,) + events[1][7:] events[2] = events[2][:6] + (None,) + events[2][7:] events[3] = events[3][:6] + (None,) + events[3][7:] - self.assertEqual([ - ('status', "broken-runner-'0'", 'inprogress', None, True, None, None, False, None, '0', None), - ('status', "broken-runner-'0'", None, None, True, 'traceback', None, - False, - 'text/x-traceback; charset="utf8"; language="python"', - '0', - None), - ('status', "broken-runner-'0'", None, None, True, 'traceback', None, - False, - 'text/x-traceback; charset="utf8"; language="python"', - '0', - None), - ('status', "broken-runner-'0'", None, None, True, 'traceback', None, - True, - 'text/x-traceback; charset="utf8"; language="python"', - '0', - None), - ('status', "broken-runner-'0'", 'fail', set(), True, None, None, False, None, '0', None) - ], events) + self.assertEqual( + [ + ( + "status", + "broken-runner-'0'", + "inprogress", + None, + True, + None, + None, + False, + None, + "0", + None, + ), + ( + "status", + "broken-runner-'0'", + None, + None, + True, + "traceback", + None, + False, + 'text/x-traceback; charset="utf8"; language="python"', + "0", + None, + ), + ( + "status", + "broken-runner-'0'", + None, + None, + True, + "traceback", + None, + False, + 'text/x-traceback; charset="utf8"; language="python"', + "0", + None, + ), + ( + "status", + "broken-runner-'0'", + None, + None, + True, + "traceback", + None, + True, + 'text/x-traceback; charset="utf8"; language="python"', + "0", + None, + ), + ( + "status", + "broken-runner-'0'", + "fail", + set(), + True, + None, + None, + False, + None, + "0", + None, + ), + ], + events, + ) def split_suite(self, suite): tests = list(enumerate(iterate_tests(suite))) @@ -219,16 +307,18 @@ def test_setupclass_skip(self): class Skips(TestCase): @classmethod def setUpClass(cls): - raise cls.skipException('foo') + raise cls.skipException("foo") + def test_notrun(self): pass + # Test discovery uses the default suite from unittest (unless users # deliberately change things, in which case they keep both pieces). suite = unittest.TestSuite([Skips("test_notrun")]) log = [] result = LoggingResult(log) suite.run(result) - self.assertEqual(['addSkip'], [item[0] for item in log]) + self.assertEqual(["addSkip"], [item[0] for item in log]) def test_setupclass_upcall(self): # Note that this is kindof-a-case-test, kindof-suite, because @@ -237,8 +327,10 @@ class Simples(TestCase): @classmethod def setUpClass(cls): super().setUpClass() + def test_simple(self): pass + # Test discovery uses the default suite from unittest (unless users # deliberately change things, in which case they keep both pieces). suite = unittest.TestSuite([Simples("test_simple")]) @@ -246,12 +338,11 @@ def test_simple(self): result = LoggingResult(log) suite.run(result) self.assertEqual( - ['startTest', 'addSuccess', 'stopTest'], - [item[0] for item in log]) + ["startTest", "addSuccess", "stopTest"], [item[0] for item in log] + ) class TestFixtureSuite(TestCase): - def setUp(self): super().setUp() if FunctionFixture is None: @@ -259,83 +350,93 @@ def setUp(self): def test_fixture_suite(self): log = [] + class Sample(TestCase): def test_one(self): log.append(1) + def test_two(self): log.append(2) + fixture = FunctionFixture( - lambda: log.append('setUp'), - lambda fixture: log.append('tearDown')) - suite = FixtureSuite(fixture, [Sample('test_one'), Sample('test_two')]) + lambda: log.append("setUp"), lambda fixture: log.append("tearDown") + ) + suite = FixtureSuite(fixture, [Sample("test_one"), Sample("test_two")]) suite.run(LoggingResult([])) - self.assertEqual(['setUp', 1, 2, 'tearDown'], log) + self.assertEqual(["setUp", 1, 2, "tearDown"], log) def test_fixture_suite_sort(self): log = [] + class Sample(TestCase): def test_one(self): log.append(1) + def test_two(self): log.append(2) + fixture = FunctionFixture( - lambda: log.append('setUp'), - lambda fixture: log.append('tearDown')) - suite = FixtureSuite(fixture, [Sample('test_one'), Sample('test_one')]) + lambda: log.append("setUp"), lambda fixture: log.append("tearDown") + ) + suite = FixtureSuite(fixture, [Sample("test_one"), Sample("test_one")]) self.assertRaises(ValueError, suite.sort_tests) class TestSortedTests(TestCase): - def test_sorts_custom_suites(self): - a = PlaceHolder('a') - b = PlaceHolder('b') + a = PlaceHolder("a") + b = PlaceHolder("b") + class Subclass(unittest.TestSuite): def sort_tests(self): self._tests = sorted_tests(self, True) + input_suite = Subclass([b, a]) suite = sorted_tests(input_suite) self.assertEqual([a, b], list(iterate_tests(suite))) self.assertEqual([input_suite], list(iter(suite))) def test_custom_suite_without_sort_tests_works(self): - a = PlaceHolder('a') - b = PlaceHolder('b') + a = PlaceHolder("a") + b = PlaceHolder("b") + class Subclass(unittest.TestSuite): pass + input_suite = Subclass([b, a]) suite = sorted_tests(input_suite) self.assertEqual([b, a], list(iterate_tests(suite))) self.assertEqual([input_suite], list(iter(suite))) def test_sorts_simple_suites(self): - a = PlaceHolder('a') - b = PlaceHolder('b') + a = PlaceHolder("a") + b = PlaceHolder("b") suite = sorted_tests(unittest.TestSuite([b, a])) self.assertEqual([a, b], list(iterate_tests(suite))) def test_duplicate_simple_suites(self): - a = PlaceHolder('a') - b = PlaceHolder('b') - c = PlaceHolder('a') - self.assertRaises( - ValueError, sorted_tests, unittest.TestSuite([a, b, c])) + a = PlaceHolder("a") + b = PlaceHolder("b") + c = PlaceHolder("a") + self.assertRaises(ValueError, sorted_tests, unittest.TestSuite([a, b, c])) def test_multiple_duplicates(self): # If there are multiple duplicates on a test suite, we report on them # all. - a = PlaceHolder('a') - b = PlaceHolder('b') - c = PlaceHolder('a') - d = PlaceHolder('b') + a = PlaceHolder("a") + b = PlaceHolder("b") + c = PlaceHolder("a") + d = PlaceHolder("b") error = self.assertRaises( - ValueError, sorted_tests, unittest.TestSuite([a, b, c, d])) + ValueError, sorted_tests, unittest.TestSuite([a, b, c, d]) + ) self.assertThat( str(error), - Equals("Duplicate test ids detected: {}".format( - pformat({'a': 2, 'b': 2})))) + Equals("Duplicate test ids detected: {}".format(pformat({"a": 2, "b": 2}))), + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/test_with_with.py b/testtools/tests/test_with_with.py index ca136a17..3a167096 100644 --- a/testtools/tests/test_with_with.py +++ b/testtools/tests/test_with_with.py @@ -6,39 +6,38 @@ from testtools import ( ExpectedException, TestCase, - ) +) from testtools.matchers import ( AfterPreprocessing, Equals, EndsWith, - ) +) class TestExpectedException(TestCase): """Test the ExpectedException context manager.""" def test_pass_on_raise(self): - with ExpectedException(ValueError, 'tes.'): - raise ValueError('test') + with ExpectedException(ValueError, "tes."): + raise ValueError("test") def test_pass_on_raise_matcher(self): - with ExpectedException( - ValueError, AfterPreprocessing(str, Equals('test'))): - raise ValueError('test') + with ExpectedException(ValueError, AfterPreprocessing(str, Equals("test"))): + raise ValueError("test") def test_raise_on_text_mismatch(self): try: - with ExpectedException(ValueError, 'tes.'): - raise ValueError('mismatch') + with ExpectedException(ValueError, "tes."): + raise ValueError("mismatch") except AssertionError: e = sys.exc_info()[1] self.assertEqual("'mismatch' does not match /tes./", str(e)) else: - self.fail('AssertionError not raised.') + self.fail("AssertionError not raised.") def test_raise_on_general_mismatch(self): - matcher = AfterPreprocessing(str, Equals('test')) - value_error = ValueError('mismatch') + matcher = AfterPreprocessing(str, Equals("test")) + value_error = ValueError("mismatch") try: with ExpectedException(ValueError, matcher): raise value_error @@ -46,47 +45,50 @@ def test_raise_on_general_mismatch(self): e = sys.exc_info()[1] self.assertEqual(matcher.match(value_error).describe(), str(e)) else: - self.fail('AssertionError not raised.') + self.fail("AssertionError not raised.") def test_raise_on_error_mismatch(self): try: - with ExpectedException(TypeError, 'tes.'): - raise ValueError('mismatch') + with ExpectedException(TypeError, "tes."): + raise ValueError("mismatch") except ValueError: e = sys.exc_info()[1] - self.assertEqual('mismatch', str(e)) + self.assertEqual("mismatch", str(e)) else: - self.fail('ValueError not raised.') + self.fail("ValueError not raised.") def test_raise_if_no_exception(self): try: - with ExpectedException(TypeError, 'tes.'): + with ExpectedException(TypeError, "tes."): pass except AssertionError: e = sys.exc_info()[1] - self.assertEqual('TypeError not raised.', str(e)) + self.assertEqual("TypeError not raised.", str(e)) else: - self.fail('AssertionError not raised.') + self.fail("AssertionError not raised.") def test_pass_on_raise_any_message(self): with ExpectedException(ValueError): - raise ValueError('whatever') - + raise ValueError("whatever") + def test_annotate(self): def die(): with ExpectedException(ValueError, msg="foo"): pass + exc = self.assertRaises(AssertionError, die) - self.assertThat(exc.args[0], EndsWith(': foo')) + self.assertThat(exc.args[0], EndsWith(": foo")) def test_annotated_matcher(self): def die(): - with ExpectedException(ValueError, 'bar', msg="foo"): + with ExpectedException(ValueError, "bar", msg="foo"): pass + exc = self.assertRaises(AssertionError, die) - self.assertThat(exc.args[0], EndsWith(': foo')) + self.assertThat(exc.args[0], EndsWith(": foo")) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/twistedsupport/__init__.py b/testtools/tests/twistedsupport/__init__.py index 96e6ee1c..c8b52868 100644 --- a/testtools/tests/twistedsupport/__init__.py +++ b/testtools/tests/twistedsupport/__init__.py @@ -10,6 +10,7 @@ def test_suite(): test_runtest, test_spinner, ) + modules = [ test_deferred, test_matchers, diff --git a/testtools/tests/twistedsupport/_helpers.py b/testtools/tests/twistedsupport/_helpers.py index 05ca193d..05c01077 100644 --- a/testtools/tests/twistedsupport/_helpers.py +++ b/testtools/tests/twistedsupport/_helpers.py @@ -1,17 +1,16 @@ # Copyright (c) 2010, 2016 testtools developers. See LICENSE for details. __all__ = [ - 'NeedsTwistedTestCase', + "NeedsTwistedTestCase", ] from testtools.helpers import try_import from testtools import TestCase -defer = try_import('twisted.internet.defer') +defer = try_import("twisted.internet.defer") class NeedsTwistedTestCase(TestCase): - def setUp(self): super().setUp() if defer is None: diff --git a/testtools/tests/twistedsupport/test_deferred.py b/testtools/tests/twistedsupport/test_deferred.py index d101272b..31692590 100644 --- a/testtools/tests/twistedsupport/test_deferred.py +++ b/testtools/tests/twistedsupport/test_deferred.py @@ -7,17 +7,15 @@ Equals, MatchesException, Raises, - ) +) from ._helpers import NeedsTwistedTestCase -DeferredNotFired = try_import( - 'testtools.twistedsupport._deferred.DeferredNotFired') -extract_result = try_import( - 'testtools.twistedsupport._deferred.extract_result') +DeferredNotFired = try_import("testtools.twistedsupport._deferred.DeferredNotFired") +extract_result = try_import("testtools.twistedsupport._deferred.extract_result") -defer = try_import('twisted.internet.defer') -Failure = try_import('twisted.python.failure.Failure') +defer = try_import("twisted.internet.defer") +Failure = try_import("twisted.python.failure.Failure") class TestExtractResult(NeedsTwistedTestCase): @@ -28,7 +26,8 @@ def test_not_fired(self): # given a Deferred that has not fired. self.assertThat( lambda: extract_result(defer.Deferred()), - Raises(MatchesException(DeferredNotFired))) + Raises(MatchesException(DeferredNotFired)), + ) def test_success(self): # _spinner.extract_result returns the value of the Deferred if it has @@ -41,15 +40,16 @@ def test_failure(self): # _spinner.extract_result raises the failure's exception if it's given # a Deferred that is failing. try: - 1/0 + 1 / 0 except ZeroDivisionError: f = Failure() d = defer.fail(f) self.assertThat( - lambda: extract_result(d), - Raises(MatchesException(ZeroDivisionError))) + lambda: extract_result(d), Raises(MatchesException(ZeroDivisionError)) + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/twistedsupport/test_matchers.py b/testtools/tests/twistedsupport/test_matchers.py index ba26e329..3dac008e 100644 --- a/testtools/tests/twistedsupport/test_matchers.py +++ b/testtools/tests/twistedsupport/test_matchers.py @@ -23,15 +23,17 @@ def mismatches(description, details=None): if details is None: details = Equals({}) - matcher = MatchesDict({ - 'description': description, - 'details': details, - }) + matcher = MatchesDict( + { + "description": description, + "details": details, + } + ) def get_mismatch_info(mismatch): return { - 'description': mismatch.describe(), - 'details': mismatch.get_details(), + "description": mismatch.describe(), + "details": mismatch.get_details(), } return AfterPreprocessing(get_mismatch_info, matcher) @@ -61,21 +63,27 @@ def test_succeeded_does_no_match(self): deferred = defer.succeed(result) mismatch = self.match(deferred) self.assertThat( - mismatch, mismatches(Equals( - 'No result expected on %r, found %r instead' - % (deferred, result)))) + mismatch, + mismatches( + Equals( + "No result expected on %r, found %r instead" % (deferred, result) + ) + ), + ) def test_failed_does_not_match(self): # A Deferred that's failed does not match has_no_result(). - fail = make_failure(RuntimeError('arbitrary failure')) + fail = make_failure(RuntimeError("arbitrary failure")) deferred = defer.fail(fail) # Suppress unhandled error in Deferred. self.addCleanup(deferred.addErrback, lambda _: None) mismatch = self.match(deferred) self.assertThat( - mismatch, mismatches(Equals( - 'No result expected on %r, found %r instead' - % (deferred, fail)))) + mismatch, + mismatches( + Equals("No result expected on %r, found %r instead" % (deferred, fail)) + ), + ) def test_success_after_assertion(self): # We can create a Deferred, assert that it hasn't fired, then fire it @@ -95,13 +103,12 @@ def test_failure_after_assertion(self): self.assertThat(deferred, has_no_result()) results = [] deferred.addErrback(results.append) - fail = make_failure(RuntimeError('arbitrary failure')) + fail = make_failure(RuntimeError("arbitrary failure")) deferred.errback(fail) self.assertThat(results, Equals([fail])) class SuccessResultTests(NeedsTwistedTestCase): - def match(self, matcher, value): return succeeded(matcher).match(value) @@ -121,8 +128,8 @@ def test_different_succeeded_result_fails(self): mismatch = matcher.match(result) self.assertThat( self.match(matcher, deferred), - mismatches(Equals(mismatch.describe()), - Equals(mismatch.get_details()))) + mismatches(Equals(mismatch.describe()), Equals(mismatch.get_details())), + ) def test_not_fired_fails(self): # A Deferred that has not yet fired fails to match. @@ -131,50 +138,60 @@ def test_not_fired_fails(self): self.assertThat( self.match(arbitrary_matcher, deferred), mismatches( - Equals(('Success result expected on %r, found no result ' - 'instead') % (deferred,)))) + Equals( + ("Success result expected on %r, found no result " "instead") + % (deferred,) + ) + ), + ) def test_failing_fails(self): # A Deferred that has fired with a failure fails to match. deferred = defer.Deferred() - fail = make_failure(RuntimeError('arbitrary failure')) + fail = make_failure(RuntimeError("arbitrary failure")) deferred.errback(fail) arbitrary_matcher = Is(None) self.assertThat( self.match(arbitrary_matcher, deferred), mismatches( Equals( - 'Success result expected on %r, found failure result ' - 'instead: %r' % (deferred, fail)), - Equals({'traceback': TracebackContent( - (fail.type, fail.value, fail.getTracebackObject()), None, - )}), - )) + "Success result expected on %r, found failure result " + "instead: %r" % (deferred, fail) + ), + Equals( + { + "traceback": TracebackContent( + (fail.type, fail.value, fail.getTracebackObject()), + None, + ) + } + ), + ), + ) class FailureResultTests(NeedsTwistedTestCase): - def match(self, matcher, value): return failed(matcher).match(value) def test_failure_passes(self): # A Deferred that has fired with a failure matches against the value # it was fired with. - fail = make_failure(RuntimeError('arbitrary failure')) + fail = make_failure(RuntimeError("arbitrary failure")) deferred = defer.fail(fail) self.assertThat(self.match(Is(fail), deferred), Is(None)) def test_different_failure_fails(self): # A Deferred that has fired with a failure matches against the value # it was fired with. - fail = make_failure(RuntimeError('arbitrary failure')) + fail = make_failure(RuntimeError("arbitrary failure")) deferred = defer.fail(fail) matcher = Is(None) # Something that doesn't match `fail`. mismatch = matcher.match(fail) self.assertThat( self.match(matcher, deferred), - mismatches(Equals(mismatch.describe()), - Equals(mismatch.get_details()))) + mismatches(Equals(mismatch.describe()), Equals(mismatch.get_details())), + ) def test_success_fails(self): # A Deferred that has fired successfully fails to match. @@ -183,9 +200,13 @@ def test_success_fails(self): matcher = Is(None) # Can be any matcher self.assertThat( self.match(matcher, deferred), - mismatches(Equals( - 'Failure result expected on %r, found success ' - 'result (%r) instead' % (deferred, result)))) + mismatches( + Equals( + "Failure result expected on %r, found success " + "result (%r) instead" % (deferred, result) + ) + ), + ) def test_no_result_fails(self): # A Deferred that has not fired fails to match. @@ -193,11 +214,16 @@ def test_no_result_fails(self): matcher = Is(None) # Can be any matcher self.assertThat( self.match(matcher, deferred), - mismatches(Equals( - 'Failure result expected on %r, found no result instead' - % (deferred,)))) + mismatches( + Equals( + "Failure result expected on %r, found no result instead" + % (deferred,) + ) + ), + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/twistedsupport/test_runtest.py b/testtools/tests/twistedsupport/test_runtest.py index 51021aae..4b46cc64 100644 --- a/testtools/tests/twistedsupport/test_runtest.py +++ b/testtools/tests/twistedsupport/test_runtest.py @@ -9,7 +9,7 @@ skipIf, TestCase, TestResult, - ) +) from testtools.helpers import try_import from testtools.matchers import ( AfterPreprocessing, @@ -23,7 +23,7 @@ MatchesDict, MatchesListwise, Not, - ) +) from testtools.runtest import RunTest from testtools.testresult.doubles import ExtendedTestResult from testtools.tests.helpers import ( @@ -32,23 +32,24 @@ ) from ._helpers import NeedsTwistedTestCase -DebugTwisted = try_import( - 'testtools.twistedsupport._deferreddebug.DebugTwisted') +DebugTwisted = try_import("testtools.twistedsupport._deferreddebug.DebugTwisted") -assert_fails_with = try_import('testtools.twistedsupport.assert_fails_with') +assert_fails_with = try_import("testtools.twistedsupport.assert_fails_with") AsynchronousDeferredRunTest = try_import( - 'testtools.twistedsupport.AsynchronousDeferredRunTest') -flush_logged_errors = try_import( - 'testtools.twistedsupport.flush_logged_errors') + "testtools.twistedsupport.AsynchronousDeferredRunTest" +) +flush_logged_errors = try_import("testtools.twistedsupport.flush_logged_errors") SynchronousDeferredRunTest = try_import( - 'testtools.twistedsupport.SynchronousDeferredRunTest') + "testtools.twistedsupport.SynchronousDeferredRunTest" +) -defer = try_import('twisted.internet.defer') -failure = try_import('twisted.python.failure') -log = try_import('twisted.python.log') -DelayedCall = try_import('twisted.internet.base.DelayedCall') +defer = try_import("twisted.internet.defer") +failure = try_import("twisted.python.failure") +log = try_import("twisted.python.log") +DelayedCall = try_import("twisted.internet.base.DelayedCall") _get_global_publisher_and_observers = try_import( - 'testtools.twistedsupport._runtest._get_global_publisher_and_observers') + "testtools.twistedsupport._runtest._get_global_publisher_and_observers" +) class X: @@ -60,65 +61,74 @@ class X: class Base(TestCase): def setUp(self): super(X.Base, self).setUp() - self.calls = ['setUp'] - self.addCleanup(self.calls.append, 'clean-up') + self.calls = ["setUp"] + self.addCleanup(self.calls.append, "clean-up") + def test_something(self): - self.calls.append('test') + self.calls.append("test") + def tearDown(self): - self.calls.append('tearDown') + self.calls.append("tearDown") super(X.Base, self).tearDown() class BaseExceptionRaised(Base): - expected_calls = ['setUp', 'tearDown', 'clean-up'] - expected_results = [('addError', SystemExit)] + expected_calls = ["setUp", "tearDown", "clean-up"] + expected_results = [("addError", SystemExit)] + def test_something(self): raise SystemExit(0) class ErrorInSetup(Base): - expected_calls = ['setUp', 'clean-up'] - expected_results = [('addError', RuntimeError)] + expected_calls = ["setUp", "clean-up"] + expected_results = [("addError", RuntimeError)] + def setUp(self): super(X.ErrorInSetup, self).setUp() raise RuntimeError("Error in setUp") class ErrorInTest(Base): - expected_calls = ['setUp', 'tearDown', 'clean-up'] - expected_results = [('addError', RuntimeError)] + expected_calls = ["setUp", "tearDown", "clean-up"] + expected_results = [("addError", RuntimeError)] + def test_something(self): raise RuntimeError("Error in test") class FailureInTest(Base): - expected_calls = ['setUp', 'tearDown', 'clean-up'] - expected_results = [('addFailure', AssertionError)] + expected_calls = ["setUp", "tearDown", "clean-up"] + expected_results = [("addFailure", AssertionError)] + def test_something(self): self.fail("test failed") class ErrorInTearDown(Base): - expected_calls = ['setUp', 'test', 'clean-up'] - expected_results = [('addError', RuntimeError)] + expected_calls = ["setUp", "test", "clean-up"] + expected_results = [("addError", RuntimeError)] + def tearDown(self): raise RuntimeError("Error in tearDown") class ErrorInCleanup(Base): - expected_calls = ['setUp', 'test', 'tearDown', 'clean-up'] - expected_results = [('addError', ZeroDivisionError)] + expected_calls = ["setUp", "test", "tearDown", "clean-up"] + expected_results = [("addError", ZeroDivisionError)] + def test_something(self): - self.calls.append('test') - self.addCleanup(lambda: 1/0) + self.calls.append("test") + self.addCleanup(lambda: 1 / 0) class ExpectThatFailure(Base): """Calling expectThat with a failing match fails the test.""" - expected_calls = ['setUp', 'test', 'tearDown', 'clean-up'] - expected_results = [('addFailure', AssertionError)] + + expected_calls = ["setUp", "test", "tearDown", "clean-up"] + expected_results = [("addFailure", AssertionError)] + def test_something(self): - self.calls.append('test') + self.calls.append("test") self.expectThat(object(), Is(object())) class TestIntegration(NeedsTwistedTestCase): - def assertResultsMatch(self, test, result): events = list(result._events) - self.assertEqual(('startTest', test), events.pop(0)) + self.assertEqual(("startTest", test), events.pop(0)) for expected_result in test.expected_results: result = events.pop(0) if len(expected_result) == 1: @@ -127,11 +137,11 @@ def assertResultsMatch(self, test, result): self.assertEqual((expected_result[0], test), result[:2]) error_type = expected_result[1] self.assertIn(error_type.__name__, str(result[2])) - self.assertEqual([('stopTest', test)], events) + self.assertEqual([("stopTest", test)], events) def test_runner(self): result = ExtendedTestResult() - test = self.test_factory('test_something', runTest=self.runner) + test = self.test_factory("test_something", runTest=self.runner) if self.test_factory is X.BaseExceptionRaised: self.assertRaises(SystemExit, test.run, result) else: @@ -143,11 +153,12 @@ def test_runner(self): def make_integration_tests(): from unittest import TestSuite from testtools import clone_test_with_new_id + runners = [ - ('RunTest', RunTest), - ('SynchronousDeferredRunTest', SynchronousDeferredRunTest), - ('AsynchronousDeferredRunTest', AsynchronousDeferredRunTest), - ] + ("RunTest", RunTest), + ("SynchronousDeferredRunTest", SynchronousDeferredRunTest), + ("AsynchronousDeferredRunTest", AsynchronousDeferredRunTest), + ] tests = [ X.BaseExceptionRaised, @@ -158,15 +169,14 @@ def make_integration_tests(): X.ErrorInCleanup, X.ExpectThatFailure, ] - base_test = X.TestIntegration('test_runner') + base_test = X.TestIntegration("test_runner") integration_tests = [] for runner_name, runner in runners: for test in tests: new_test = clone_test_with_new_id( - base_test, '{}({}, {})'.format( - base_test.id(), - runner_name, - test.__name__)) + base_test, + "{}({}, {})".format(base_test.id(), runner_name, test.__name__), + ) new_test.test_factory = test new_test.runner = runner integration_tests.append(new_test) @@ -174,7 +184,6 @@ def make_integration_tests(): class TestSynchronousDeferredRunTest(NeedsTwistedTestCase): - def make_result(self): return ExtendedTestResult() @@ -185,63 +194,67 @@ def test_success(self): class SomeCase(TestCase): def test_success(self): return defer.succeed(None) - test = SomeCase('test_success') + + test = SomeCase("test_success") runner = self.make_runner(test) result = self.make_result() runner.run(result) self.assertThat( - result._events, Equals([ - ('startTest', test), - ('addSuccess', test), - ('stopTest', test)])) + result._events, + Equals([("startTest", test), ("addSuccess", test), ("stopTest", test)]), + ) def test_failure(self): class SomeCase(TestCase): def test_failure(self): return defer.maybeDeferred(self.fail, "Egads!") - test = SomeCase('test_failure') + + test = SomeCase("test_failure") runner = self.make_runner(test) result = self.make_result() runner.run(result) self.assertThat( - [event[:2] for event in result._events], Equals([ - ('startTest', test), - ('addFailure', test), - ('stopTest', test)])) + [event[:2] for event in result._events], + Equals([("startTest", test), ("addFailure", test), ("stopTest", test)]), + ) def test_setUp_followed_by_test(self): class SomeCase(TestCase): def setUp(self): super().setUp() return defer.succeed(None) + def test_failure(self): return defer.maybeDeferred(self.fail, "Egads!") - test = SomeCase('test_failure') + + test = SomeCase("test_failure") runner = self.make_runner(test) result = self.make_result() runner.run(result) self.assertThat( - [event[:2] for event in result._events], Equals([ - ('startTest', test), - ('addFailure', test), - ('stopTest', test)])) + [event[:2] for event in result._events], + Equals([("startTest", test), ("addFailure", test), ("stopTest", test)]), + ) class TestAsynchronousDeferredRunTest(NeedsTwistedTestCase): - def make_reactor(self): from twisted.internet import reactor + return reactor def make_result(self): return ExtendedTestResult() - def make_runner(self, test, timeout=None, suppress_twisted_logging=True, - store_twisted_logs=True): + def make_runner( + self, test, timeout=None, suppress_twisted_logging=True, store_twisted_logs=True + ): if timeout is None: timeout = self.make_timeout() return AsynchronousDeferredRunTest( - test, test.exception_handlers, timeout=timeout, + test, + test.exception_handlers, + timeout=timeout, suppress_twisted_logging=suppress_twisted_logging, store_twisted_logs=store_twisted_logs, ) @@ -256,24 +269,28 @@ def test_setUp_returns_deferred_that_fires_later(self): call_log = [] marker = object() d = defer.Deferred().addCallback(call_log.append) + class SomeCase(TestCase): def setUp(self): super().setUp() - call_log.append('setUp') + call_log.append("setUp") return d + def test_something(self): - call_log.append('test') + call_log.append("test") + def fire_deferred(): - self.assertThat(call_log, Equals(['setUp'])) + self.assertThat(call_log, Equals(["setUp"])) d.callback(marker) - test = SomeCase('test_something') + + test = SomeCase("test_something") timeout = self.make_timeout() runner = self.make_runner(test, timeout=timeout) result = self.make_result() reactor = self.make_reactor() reactor.callLater(timeout, fire_deferred) runner.run(result) - self.assertThat(call_log, Equals(['setUp', marker, 'test'])) + self.assertThat(call_log, Equals(["setUp", marker, "test"])) def test_calls_setUp_test_tearDown_in_sequence(self): # setUp, the test method and tearDown can all return @@ -282,44 +299,50 @@ def test_calls_setUp_test_tearDown_in_sequence(self): # Deferred from the previous stage has fired. call_log = [] a = defer.Deferred() - a.addCallback(lambda x: call_log.append('a')) + a.addCallback(lambda x: call_log.append("a")) b = defer.Deferred() - b.addCallback(lambda x: call_log.append('b')) + b.addCallback(lambda x: call_log.append("b")) c = defer.Deferred() - c.addCallback(lambda x: call_log.append('c')) + c.addCallback(lambda x: call_log.append("c")) + class SomeCase(TestCase): def setUp(self): super().setUp() - call_log.append('setUp') + call_log.append("setUp") return a + def test_success(self): - call_log.append('test') + call_log.append("test") return b + def tearDown(self): super().tearDown() - call_log.append('tearDown') + call_log.append("tearDown") return c - test = SomeCase('test_success') + + test = SomeCase("test_success") timeout = self.make_timeout() runner = self.make_runner(test, timeout) result = self.make_result() reactor = self.make_reactor() + def fire_a(): - self.assertThat(call_log, Equals(['setUp'])) + self.assertThat(call_log, Equals(["setUp"])) a.callback(None) + def fire_b(): - self.assertThat(call_log, Equals(['setUp', 'a', 'test'])) + self.assertThat(call_log, Equals(["setUp", "a", "test"])) b.callback(None) + def fire_c(): - self.assertThat( - call_log, Equals(['setUp', 'a', 'test', 'b', 'tearDown'])) + self.assertThat(call_log, Equals(["setUp", "a", "test", "b", "tearDown"])) c.callback(None) + reactor.callLater(timeout * 0.25, fire_a) reactor.callLater(timeout * 0.5, fire_b) reactor.callLater(timeout * 0.75, fire_c) runner.run(result) - self.assertThat( - call_log, Equals(['setUp', 'a', 'test', 'b', 'tearDown', 'c'])) + self.assertThat(call_log, Equals(["setUp", "a", "test", "b", "tearDown", "c"])) def test_async_cleanups(self): # Cleanups added with addCleanup can return @@ -328,23 +351,28 @@ def test_async_cleanups(self): class SomeCase(TestCase): def test_whatever(self): pass - test = SomeCase('test_whatever') + + test = SomeCase("test_whatever") call_log = [] - a = defer.Deferred().addCallback(lambda x: call_log.append('a')) - b = defer.Deferred().addCallback(lambda x: call_log.append('b')) - c = defer.Deferred().addCallback(lambda x: call_log.append('c')) + a = defer.Deferred().addCallback(lambda x: call_log.append("a")) + b = defer.Deferred().addCallback(lambda x: call_log.append("b")) + c = defer.Deferred().addCallback(lambda x: call_log.append("c")) test.addCleanup(lambda: a) test.addCleanup(lambda: b) test.addCleanup(lambda: c) + def fire_a(): self.assertThat(call_log, Equals([])) a.callback(None) + def fire_b(): - self.assertThat(call_log, Equals(['a'])) + self.assertThat(call_log, Equals(["a"])) b.callback(None) + def fire_c(): - self.assertThat(call_log, Equals(['a', 'b'])) + self.assertThat(call_log, Equals(["a", "b"])) c.callback(None) + timeout = self.make_timeout() reactor = self.make_reactor() reactor.callLater(timeout * 0.25, fire_a) @@ -353,36 +381,38 @@ def fire_c(): runner = self.make_runner(test, timeout) result = self.make_result() runner.run(result) - self.assertThat(call_log, Equals(['a', 'b', 'c'])) + self.assertThat(call_log, Equals(["a", "b", "c"])) def test_clean_reactor(self): # If there's cruft left over in the reactor, the test fails. reactor = self.make_reactor() timeout = self.make_timeout() + class SomeCase(TestCase): def test_cruft(self): reactor.callLater(timeout * 10.0, lambda: None) - test = SomeCase('test_cruft') + + test = SomeCase("test_cruft") runner = self.make_runner(test, timeout) result = self.make_result() runner.run(result) self.assertThat( [event[:2] for event in result._events], - Equals( - [('startTest', test), - ('addError', test), - ('stopTest', test)])) + Equals([("startTest", test), ("addError", test), ("stopTest", test)]), + ) error = result._events[1][2] - self.assertThat(error, KeysEqual('traceback', 'twisted-log')) + self.assertThat(error, KeysEqual("traceback", "twisted-log")) def test_exports_reactor(self): # The reactor is set as an attribute on the test case. reactor = self.make_reactor() timeout = self.make_timeout() + class SomeCase(TestCase): def test_cruft(self): self.assertIs(reactor, self.reactor) - test = SomeCase('test_cruft') + + test = SomeCase("test_cruft") runner = self.make_runner(test, timeout) result = TestResult() runner.run(result) @@ -396,28 +426,32 @@ def test_unhandled_error_from_deferred(self): # We're interested in the behavior when debugging is disabled. When # debugging is enabled, we get more stack traces. self.useFixture(DebugTwisted(False)) + class SomeCase(TestCase): def test_cruft(self): # Note we aren't returning the Deferred so that the error will # be unhandled. - defer.maybeDeferred(lambda: 1/0) - defer.maybeDeferred(lambda: 2/0) - test = SomeCase('test_cruft') + defer.maybeDeferred(lambda: 1 / 0) + defer.maybeDeferred(lambda: 2 / 0) + + test = SomeCase("test_cruft") runner = self.make_runner(test) result = self.make_result() runner.run(result) error = result._events[1][2] - result._events[1] = ('addError', test, None) - self.assertThat(result._events, Equals( - [('startTest', test), - ('addError', test, None), - ('stopTest', test)])) + result._events[1] = ("addError", test, None) + self.assertThat( + result._events, + Equals([("startTest", test), ("addError", test, None), ("stopTest", test)]), + ) self.assertThat( - error, KeysEqual( - 'twisted-log', - 'unhandled-error-in-deferred', - 'unhandled-error-in-deferred-1', - )) + error, + KeysEqual( + "twisted-log", + "unhandled-error-in-deferred", + "unhandled-error-in-deferred-1", + ), + ) def test_unhandled_error_from_deferred_combined_with_error(self): # If there's a Deferred with an unhandled error, the test fails. Each @@ -427,40 +461,46 @@ def test_unhandled_error_from_deferred_combined_with_error(self): # We're interested in the behavior when debugging is disabled. When # debugging is enabled, we get more stack traces. self.useFixture(DebugTwisted(False)) + class SomeCase(TestCase): def test_cruft(self): # Note we aren't returning the Deferred so that the error will # be unhandled. - defer.maybeDeferred(lambda: 1/0) + defer.maybeDeferred(lambda: 1 / 0) 2 / 0 - test = SomeCase('test_cruft') + + test = SomeCase("test_cruft") runner = self.make_runner(test) result = self.make_result() runner.run(result) error = result._events[1][2] - result._events[1] = ('addError', test, None) - self.assertThat(result._events, Equals( - [('startTest', test), - ('addError', test, None), - ('stopTest', test)])) + result._events[1] = ("addError", test, None) self.assertThat( - error, KeysEqual( - 'traceback', - 'twisted-log', - 'unhandled-error-in-deferred', - )) + result._events, + Equals([("startTest", test), ("addError", test, None), ("stopTest", test)]), + ) + self.assertThat( + error, + KeysEqual( + "traceback", + "twisted-log", + "unhandled-error-in-deferred", + ), + ) @skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only") def test_keyboard_interrupt_stops_test_run(self): # If we get a SIGINT during a test run, the test stops and no more # tests run. - SIGINT = getattr(signal, 'SIGINT', None) + SIGINT = getattr(signal, "SIGINT", None) if not SIGINT: raise self.skipTest("SIGINT unavailable") + class SomeCase(TestCase): def test_pause(self): return defer.Deferred() - test = SomeCase('test_pause') + + test = SomeCase("test_pause") reactor = self.make_reactor() timeout = self.make_timeout() runner = self.make_runner(test, timeout * 5) @@ -473,13 +513,15 @@ def test_pause(self): def test_fast_keyboard_interrupt_stops_test_run(self): # If we get a SIGINT during a test run, the test stops and no more # tests run. - SIGINT = getattr(signal, 'SIGINT', None) + SIGINT = getattr(signal, "SIGINT", None) if not SIGINT: raise self.skipTest("SIGINT unavailable") + class SomeCase(TestCase): def test_pause(self): return defer.Deferred() - test = SomeCase('test_pause') + + test = SomeCase("test_pause") reactor = self.make_reactor() timeout = self.make_timeout() runner = self.make_runner(test, timeout * 5) @@ -494,17 +536,17 @@ def test_timeout_causes_test_error(self): class SomeCase(TestCase): def test_pause(self): return defer.Deferred() - test = SomeCase('test_pause') + + test = SomeCase("test_pause") runner = self.make_runner(test) result = self.make_result() runner.run(result) error = result._events[1][2] self.assertThat( - [event[:2] for event in result._events], Equals( - [('startTest', test), - ('addError', test), - ('stopTest', test)])) - self.assertIn('TimeoutError', str(error['traceback'])) + [event[:2] for event in result._events], + Equals([("startTest", test), ("addError", test), ("stopTest", test)]), + ) + self.assertIn("TimeoutError", str(error["traceback"])) def test_convenient_construction(self): # As a convenience method, AsynchronousDeferredRunTest has a @@ -523,11 +565,14 @@ def test_convenient_construction(self): def test_use_convenient_factory(self): # Make sure that the factory can actually be used. factory = AsynchronousDeferredRunTest.make_factory() + class SomeCase(TestCase): run_tests_with = factory + def test_something(self): pass - case = SomeCase('test_something') + + case = SomeCase("test_something") case.run() def test_convenient_construction_default_reactor(self): @@ -568,19 +613,18 @@ def test_convenient_construction_default_debugging(self): def test_deferred_error(self): class SomeTest(TestCase): def test_something(self): - return defer.maybeDeferred(lambda: 1/0) - test = SomeTest('test_something') + return defer.maybeDeferred(lambda: 1 / 0) + + test = SomeTest("test_something") runner = self.make_runner(test) result = self.make_result() runner.run(result) self.assertThat( [event[:2] for event in result._events], - Equals([ - ('startTest', test), - ('addError', test), - ('stopTest', test)])) + Equals([("startTest", test), ("addError", test), ("stopTest", test)]), + ) error = result._events[1][2] - self.assertThat(error, KeysEqual('traceback', 'twisted-log')) + self.assertThat(error, KeysEqual("traceback", "twisted-log")) def test_only_addError_once(self): # Even if the reactor is unclean and the test raises an error and the @@ -590,36 +634,39 @@ def test_only_addError_once(self): # debugging is enabled, we get more stack traces. self.useFixture(DebugTwisted(False)) reactor = self.make_reactor() + class WhenItRains(TestCase): def it_pours(self): # Add a dirty cleanup. self.addCleanup(lambda: 3 / 0) # Dirty the reactor. from twisted.internet.protocol import ServerFactory - reactor.listenTCP(0, ServerFactory(), interface='127.0.0.1') + + reactor.listenTCP(0, ServerFactory(), interface="127.0.0.1") # Unhandled error. defer.maybeDeferred(lambda: 2 / 0) # Actual error. raise RuntimeError("Excess precipitation") - test = WhenItRains('it_pours') + + test = WhenItRains("it_pours") runner = self.make_runner(test) result = self.make_result() runner.run(result) self.assertThat( [event[:2] for event in result._events], - Equals([ - ('startTest', test), - ('addError', test), - ('stopTest', test)])) + Equals([("startTest", test), ("addError", test), ("stopTest", test)]), + ) error = result._events[1][2] self.assertThat( - error, KeysEqual( - 'traceback', - 'traceback-1', - 'traceback-2', - 'twisted-log', - 'unhandled-error-in-deferred', - )) + error, + KeysEqual( + "traceback", + "traceback-1", + "traceback-2", + "twisted-log", + "unhandled-error-in-deferred", + ), + ) def test_log_err_is_error(self): # An error logged during the test run is recorded as an error in the @@ -627,25 +674,36 @@ def test_log_err_is_error(self): class LogAnError(TestCase): def test_something(self): try: - 1/0 + 1 / 0 except ZeroDivisionError: f = failure.Failure() log.err(f) - test = LogAnError('test_something') + + test = LogAnError("test_something") runner = self.make_runner(test, store_twisted_logs=False) result = self.make_result() runner.run(result) self.assertThat( result._events, MatchesEvents( - ('startTest', test), - ('addError', test, { - 'logged-error': AsText(ContainsAll([ - 'Traceback (most recent call last):', - 'ZeroDivisionError', - ])), - }), - ('stopTest', test))) + ("startTest", test), + ( + "addError", + test, + { + "logged-error": AsText( + ContainsAll( + [ + "Traceback (most recent call last):", + "ZeroDivisionError", + ] + ) + ), + }, + ), + ("stopTest", test), + ), + ) def test_log_err_flushed_is_success(self): # An error logged during the test run is recorded as an error in the @@ -653,21 +711,22 @@ def test_log_err_flushed_is_success(self): class LogAnError(TestCase): def test_something(self): try: - 1/0 + 1 / 0 except ZeroDivisionError: f = failure.Failure() log.err(f) flush_logged_errors(ZeroDivisionError) - test = LogAnError('test_something') + + test = LogAnError("test_something") runner = self.make_runner(test, store_twisted_logs=False) result = self.make_result() runner.run(result) self.assertThat( result._events, MatchesEvents( - ('startTest', test), - ('addSuccess', test), - ('stopTest', test))) + ("startTest", test), ("addSuccess", test), ("stopTest", test) + ), + ) def test_log_in_details(self): # If store_twisted_logs is True, we include the Twisted logs as @@ -675,20 +734,27 @@ def test_log_in_details(self): class LogAnError(TestCase): def test_something(self): log.msg("foo") - 1/0 - test = LogAnError('test_something') + 1 / 0 + + test = LogAnError("test_something") runner = self.make_runner(test, store_twisted_logs=True) result = self.make_result() runner.run(result) self.assertThat( result._events, MatchesEvents( - ('startTest', test), - ('addError', test, { - 'traceback': Not(Is(None)), - 'twisted-log': AsText(EndsWith(' foo\n')), - }), - ('stopTest', test))) + ("startTest", test), + ( + "addError", + test, + { + "traceback": Not(Is(None)), + "twisted-log": AsText(EndsWith(" foo\n")), + }, + ), + ("stopTest", test), + ), + ) def test_do_not_log_to_twisted(self): # If suppress_twisted_logging is True, we don't log anything to the @@ -702,7 +768,7 @@ class LogSomething(TestCase): def test_something(self): log.msg("foo") - test = LogSomething('test_something') + test = LogSomething("test_something") runner = self.make_runner(test, suppress_twisted_logging=True) result = self.make_result() runner.run(result) @@ -719,13 +785,13 @@ class LogSomething(TestCase): def test_something(self): log.msg("foo") - test = LogSomething('test_something') + test = LogSomething("test_something") runner = self.make_runner(test, suppress_twisted_logging=False) result = self.make_result() runner.run(result) self.assertThat( - messages, - MatchesListwise([ContainsDict({'message': Equals(('foo',))})])) + messages, MatchesListwise([ContainsDict({"message": Equals(("foo",))})]) + ) def test_restore_observers(self): # We restore the original observers. @@ -735,12 +801,11 @@ class LogSomething(TestCase): def test_something(self): pass - test = LogSomething('test_something') + test = LogSomething("test_something") runner = self.make_runner(test) result = self.make_result() runner.run(result) - self.assertThat( - _get_global_publisher_and_observers()[1], Equals(observers)) + self.assertThat(_get_global_publisher_and_observers()[1], Equals(observers)) def test_restore_observers_after_timeout(self): # We restore the original observers even if the test times out. @@ -750,37 +815,46 @@ class LogSomething(TestCase): def test_something(self): return defer.Deferred() - test = LogSomething('test_something') + test = LogSomething("test_something") runner = self.make_runner(test, timeout=0.0001) result = self.make_result() runner.run(result) - self.assertThat( - _get_global_publisher_and_observers()[1], Equals(observers)) + self.assertThat(_get_global_publisher_and_observers()[1], Equals(observers)) def test_debugging_unchanged_during_test_by_default(self): debugging = [(defer.Deferred.debug, DelayedCall.debug)] + class SomeCase(TestCase): def test_debugging_enabled(self): debugging.append((defer.Deferred.debug, DelayedCall.debug)) - test = SomeCase('test_debugging_enabled') + + test = SomeCase("test_debugging_enabled") runner = AsynchronousDeferredRunTest( - test, handlers=test.exception_handlers, - reactor=self.make_reactor(), timeout=self.make_timeout()) + test, + handlers=test.exception_handlers, + reactor=self.make_reactor(), + timeout=self.make_timeout(), + ) runner.run(self.make_result()) self.assertEqual(debugging[0], debugging[1]) def test_debugging_enabled_during_test_with_debug_flag(self): - self.patch(defer.Deferred, 'debug', False) - self.patch(DelayedCall, 'debug', False) + self.patch(defer.Deferred, "debug", False) + self.patch(DelayedCall, "debug", False) debugging = [] + class SomeCase(TestCase): def test_debugging_enabled(self): debugging.append((defer.Deferred.debug, DelayedCall.debug)) - test = SomeCase('test_debugging_enabled') + + test = SomeCase("test_debugging_enabled") runner = AsynchronousDeferredRunTest( - test, handlers=test.exception_handlers, - reactor=self.make_reactor(), timeout=self.make_timeout(), - debug=True) + test, + handlers=test.exception_handlers, + reactor=self.make_reactor(), + timeout=self.make_timeout(), + debug=True, + ) runner.run(self.make_result()) self.assertEqual([(True, True)], debugging) self.assertEqual(False, defer.Deferred.debug) @@ -798,55 +872,67 @@ def test_assert_fails_with_success(self): # succeeds. marker = object() d = assert_fails_with(defer.succeed(marker), RuntimeError) + def check_result(failure): failure.trap(self.failureException) self.assertThat( str(failure.value), - Equals(f"RuntimeError not raised ({marker!r} returned)")) - d.addCallbacks( - lambda x: self.fail("Should not have succeeded"), check_result) + Equals(f"RuntimeError not raised ({marker!r} returned)"), + ) + + d.addCallbacks(lambda x: self.fail("Should not have succeeded"), check_result) return d def test_assert_fails_with_success_multiple_types(self): # assert_fails_with fails the test if it's given a Deferred that # succeeds. marker = object() - d = assert_fails_with( - defer.succeed(marker), RuntimeError, ZeroDivisionError) + d = assert_fails_with(defer.succeed(marker), RuntimeError, ZeroDivisionError) + def check_result(failure): failure.trap(self.failureException) self.assertThat( str(failure.value), - Equals("RuntimeError, ZeroDivisionError not raised " - "(%r returned)" % (marker,))) - d.addCallbacks( - lambda x: self.fail("Should not have succeeded"), check_result) + Equals( + "RuntimeError, ZeroDivisionError not raised " + "(%r returned)" % (marker,) + ), + ) + + d.addCallbacks(lambda x: self.fail("Should not have succeeded"), check_result) return d def test_assert_fails_with_wrong_exception(self): # assert_fails_with fails the test if it's given a Deferred that # succeeds. d = assert_fails_with( - defer.maybeDeferred(lambda: 1/0), RuntimeError, KeyboardInterrupt) + defer.maybeDeferred(lambda: 1 / 0), RuntimeError, KeyboardInterrupt + ) + def check_result(failure): failure.trap(self.failureException) lines = str(failure.value).splitlines() self.assertThat( lines[:2], - Equals([ - ("ZeroDivisionError raised instead of RuntimeError, " - "KeyboardInterrupt:"), - " Traceback (most recent call last):", - ])) - d.addCallbacks( - lambda x: self.fail("Should not have succeeded"), check_result) + Equals( + [ + ( + "ZeroDivisionError raised instead of RuntimeError, " + "KeyboardInterrupt:" + ), + " Traceback (most recent call last):", + ] + ), + ) + + d.addCallbacks(lambda x: self.fail("Should not have succeeded"), check_result) return d def test_assert_fails_with_expected_exception(self): # assert_fails_with calls back with the value of the failure if it's # one of the expected types of failures. try: - 1/0 + 1 / 0 except ZeroDivisionError: f = failure.Failure() d = assert_fails_with(defer.fail(f), ZeroDivisionError) @@ -857,24 +943,29 @@ def test_custom_failure_exception(self): # argument, then it will raise that instead of `AssertionError`. class CustomException(Exception): pass + marker = object() d = assert_fails_with( - defer.succeed(marker), RuntimeError, - failureException=CustomException) + defer.succeed(marker), RuntimeError, failureException=CustomException + ) + def check_result(failure): failure.trap(CustomException) self.assertThat( str(failure.value), - Equals(f"RuntimeError not raised ({marker!r} returned)")) + Equals(f"RuntimeError not raised ({marker!r} returned)"), + ) + return d.addCallbacks( - lambda x: self.fail("Should not have succeeded"), check_result) + lambda x: self.fail("Should not have succeeded"), check_result + ) class TestRunWithLogObservers(NeedsTwistedTestCase): - def test_restores_observers(self): from testtools.twistedsupport._runtest import run_with_log_observers from twisted.python import log + # Make sure there's at least one observer. This reproduces bug # #926189. log.addObserver(lambda *args: None) @@ -902,12 +993,12 @@ def test_default(self): # go to the Twisted logs. class SomeTest(TestCase): def test_something(self): - log.msg('foo') + log.msg("foo") - _, messages = self._get_logged_messages(SomeTest('test_something').run) + _, messages = self._get_logged_messages(SomeTest("test_something").run) self.assertThat( - messages, - MatchesListwise([ContainsDict({'message': Equals(('foo',))})])) + messages, MatchesListwise([ContainsDict({"message": Equals(("foo",))})]) + ) def test_nothing_logged(self): # Using _NoTwistedLogObservers means that nothing is logged to @@ -917,9 +1008,9 @@ def test_nothing_logged(self): class SomeTest(TestCase): def test_something(self): self.useFixture(_NoTwistedLogObservers()) - log.msg('foo') + log.msg("foo") - _, messages = self._get_logged_messages(SomeTest('test_something').run) + _, messages = self._get_logged_messages(SomeTest("test_something").run) self.assertThat(messages, Equals([])) def test_logging_restored(self): @@ -929,16 +1020,16 @@ def test_logging_restored(self): class SomeTest(TestCase): def test_something(self): self.useFixture(_NoTwistedLogObservers()) - log.msg('foo') + log.msg("foo") def run_then_log(): - SomeTest('test_something').run() - log.msg('bar') + SomeTest("test_something").run() + log.msg("bar") _, messages = self._get_logged_messages(run_then_log) self.assertThat( - messages, - MatchesListwise([ContainsDict({'message': Equals(('bar',))})])) + messages, MatchesListwise([ContainsDict({"message": Equals(("bar",))})]) + ) class TestTwistedLogObservers(NeedsTwistedTestCase): @@ -954,13 +1045,13 @@ def test_logged_messages_go_to_observer(self): class SomeTest(TestCase): def test_something(self): self.useFixture(_TwistedLogObservers([messages.append])) - log.msg('foo') + log.msg("foo") - SomeTest('test_something').run() - log.msg('bar') + SomeTest("test_something").run() + log.msg("bar") self.assertThat( - messages, - MatchesListwise([ContainsDict({'message': Equals(('foo',))})])) + messages, MatchesListwise([ContainsDict({"message": Equals(("foo",))})]) + ) class TestErrorObserver(NeedsTwistedTestCase): @@ -969,11 +1060,14 @@ class TestErrorObserver(NeedsTwistedTestCase): def test_captures_errors(self): # _ErrorObserver stores all errors logged while it is active. from testtools.twistedsupport._runtest import ( - _ErrorObserver, _LogObserver, _NoTwistedLogObservers) + _ErrorObserver, + _LogObserver, + _NoTwistedLogObservers, + ) log_observer = _LogObserver() error_observer = _ErrorObserver(log_observer) - exception = ValueError('bar') + exception = ValueError("bar") class SomeTest(TestCase): def test_something(self): @@ -981,14 +1075,14 @@ def test_something(self): # to stderr. self.useFixture(_NoTwistedLogObservers()) self.useFixture(error_observer) - log.msg('foo') + log.msg("foo") log.err(exception) - SomeTest('test_something').run() + SomeTest("test_something").run() self.assertThat( error_observer.flush_logged_errors(), - MatchesListwise([ - AfterPreprocessing(lambda x: x.value, Equals(exception))])) + MatchesListwise([AfterPreprocessing(lambda x: x.value, Equals(exception))]), + ) class TestCaptureTwistedLogs(NeedsTwistedTestCase): @@ -1001,19 +1095,23 @@ def test_captures_logs(self): class SomeTest(TestCase): def test_something(self): self.useFixture(CaptureTwistedLogs()) - log.msg('foo') + log.msg("foo") - test = SomeTest('test_something') + test = SomeTest("test_something") test.run() self.assertThat( test.getDetails(), - MatchesDict({ - 'twisted-log': AsText(Contains('foo')), - })) + MatchesDict( + { + "twisted-log": AsText(Contains("foo")), + } + ), + ) def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/tests/twistedsupport/test_spinner.py b/testtools/tests/twistedsupport/test_spinner.py index c2f055bd..d3a0761c 100644 --- a/testtools/tests/twistedsupport/test_spinner.py +++ b/testtools/tests/twistedsupport/test_spinner.py @@ -12,49 +12,51 @@ Is, MatchesException, Raises, - ) +) from ._helpers import NeedsTwistedTestCase -_spinner = try_import('testtools.twistedsupport._spinner') +_spinner = try_import("testtools.twistedsupport._spinner") -defer = try_import('twisted.internet.defer') -Failure = try_import('twisted.python.failure.Failure') +defer = try_import("twisted.internet.defer") +Failure = try_import("twisted.python.failure.Failure") class TestNotReentrant(NeedsTwistedTestCase): - def test_not_reentrant(self): # A function decorated as not being re-entrant will raise a # _spinner.ReentryError if it is called while it is running. calls = [] + @_spinner.not_reentrant def log_something(): calls.append(None) if len(calls) < 5: log_something() - self.assertThat( - log_something, Raises(MatchesException(_spinner.ReentryError))) + + self.assertThat(log_something, Raises(MatchesException(_spinner.ReentryError))) self.assertEqual(1, len(calls)) def test_deeper_stack(self): calls = [] + @_spinner.not_reentrant def g(): calls.append(None) if len(calls) < 5: f() + @_spinner.not_reentrant def f(): calls.append(None) if len(calls) < 5: g() + self.assertThat(f, Raises(MatchesException(_spinner.ReentryError))) self.assertEqual(2, len(calls)) class TestTrapUnhandledErrors(NeedsTwistedTestCase): - def test_no_deferreds(self): marker = object() result, errors = _spinner.trap_unhandled_errors(lambda: marker) @@ -63,23 +65,24 @@ def test_no_deferreds(self): def test_unhandled_error(self): failures = [] + def make_deferred_but_dont_handle(): try: - 1/0 + 1 / 0 except ZeroDivisionError: f = Failure() failures.append(f) defer.fail(f) - result, errors = _spinner.trap_unhandled_errors( - make_deferred_but_dont_handle) + + result, errors = _spinner.trap_unhandled_errors(make_deferred_but_dont_handle) self.assertIs(None, result) self.assertEqual(failures, [error.failResult for error in errors]) class TestRunInReactor(NeedsTwistedTestCase): - def make_reactor(self): from twisted.internet import reactor + return reactor def make_spinner(self, reactor=None): @@ -108,37 +111,43 @@ def test_exception_reraised(self): # If the given function raises an error, run_in_reactor re-raises that # error. self.assertThat( - lambda: self.make_spinner().run(self.make_timeout(), lambda: 1/0), - Raises(MatchesException(ZeroDivisionError))) + lambda: self.make_spinner().run(self.make_timeout(), lambda: 1 / 0), + Raises(MatchesException(ZeroDivisionError)), + ) def test_keyword_arguments(self): # run_in_reactor passes keyword arguments on. calls = [] + def function(*a, **kw): return calls.extend([a, kw]) + self.make_spinner().run(self.make_timeout(), function, foo=42) - self.assertThat(calls, Equals([(), {'foo': 42}])) + self.assertThat(calls, Equals([(), {"foo": 42}])) def test_not_reentrant(self): # run_in_reactor raises an error if it is called inside another call # to run_in_reactor. spinner = self.make_spinner() - self.assertThat(lambda: spinner.run( - self.make_timeout(), spinner.run, self.make_timeout(), - lambda: None), Raises(MatchesException(_spinner.ReentryError))) + self.assertThat( + lambda: spinner.run( + self.make_timeout(), spinner.run, self.make_timeout(), lambda: None + ), + Raises(MatchesException(_spinner.ReentryError)), + ) def test_deferred_value_returned(self): # If the given function returns a Deferred, run_in_reactor returns the # value in the Deferred at the end of the callback chain. marker = object() result = self.make_spinner().run( - self.make_timeout(), lambda: defer.succeed(marker)) + self.make_timeout(), lambda: defer.succeed(marker) + ) self.assertThat(result, Is(marker)) def test_preserve_signal_handler(self): - signals = ['SIGINT', 'SIGTERM', 'SIGCHLD'] - signals = list(filter( - None, (getattr(signal, name, None) for name in signals))) + signals = ["SIGINT", "SIGTERM", "SIGCHLD"] + signals = list(filter(None, (getattr(signal, name, None) for name in signals))) for sig in signals: self.addCleanup(signal.signal, sig, signal.getsignal(sig)) new_hdlrs = list(lambda *a: None for _ in signals) @@ -154,7 +163,8 @@ def test_timeout(self): timeout = self.make_timeout() self.assertThat( lambda: self.make_spinner().run(timeout, lambda: defer.Deferred()), - Raises(MatchesException(_spinner.TimeoutError))) + Raises(MatchesException(_spinner.TimeoutError)), + ) def test_no_junk_by_default(self): # If the reactor hasn't spun yet, then there cannot be any junk. @@ -194,9 +204,10 @@ def test_clean_selectables(self): # # Note that the socket is left open. This emulates a bug in trial. from twisted.internet.protocol import ServerFactory + reactor = self.make_reactor() spinner = self.make_spinner(reactor) - port = reactor.listenTCP(0, ServerFactory(), interface='127.0.0.1') + port = reactor.listenTCP(0, ServerFactory(), interface="127.0.0.1") spinner.run(self.make_timeout(), lambda: None) results = spinner.get_junk() self.assertThat(results, Equals([port])) @@ -204,6 +215,7 @@ def test_clean_selectables(self): def test_clean_running_threads(self): import threading import time + current_threads = list(threading.enumerate()) reactor = self.make_reactor() timeout = self.make_timeout() @@ -216,33 +228,45 @@ def test_leftover_junk_available(self): # way, 'run' will clean up the reactor and then store information # about the junk. This information can be got using get_junk. from twisted.internet.protocol import ServerFactory + reactor = self.make_reactor() spinner = self.make_spinner(reactor) port = spinner.run( - self.make_timeout(), reactor.listenTCP, 0, ServerFactory(), - interface='127.0.0.1') + self.make_timeout(), + reactor.listenTCP, + 0, + ServerFactory(), + interface="127.0.0.1", + ) self.assertThat(spinner.get_junk(), Equals([port])) def test_will_not_run_with_previous_junk(self): # If 'run' is called and there's still junk in the spinner's junk # list, then the spinner will refuse to run. from twisted.internet.protocol import ServerFactory + reactor = self.make_reactor() spinner = self.make_spinner(reactor) timeout = self.make_timeout() - spinner.run(timeout, reactor.listenTCP, 0, ServerFactory(), interface='127.0.0.1') - self.assertThat(lambda: spinner.run(timeout, lambda: None), - Raises(MatchesException(_spinner.StaleJunkError))) + spinner.run( + timeout, reactor.listenTCP, 0, ServerFactory(), interface="127.0.0.1" + ) + self.assertThat( + lambda: spinner.run(timeout, lambda: None), + Raises(MatchesException(_spinner.StaleJunkError)), + ) def test_clear_junk_clears_previous_junk(self): # If 'run' is called and there's still junk in the spinner's junk # list, then the spinner will refuse to run. from twisted.internet.protocol import ServerFactory + reactor = self.make_reactor() spinner = self.make_spinner(reactor) timeout = self.make_timeout() - port = spinner.run(timeout, reactor.listenTCP, 0, ServerFactory(), - interface='127.0.0.1') + port = spinner.run( + timeout, reactor.listenTCP, 0, ServerFactory(), interface="127.0.0.1" + ) junk = spinner.clear_junk() self.assertThat(junk, Equals([port])) self.assertThat(spinner.get_junk(), Equals([])) @@ -250,7 +274,7 @@ def test_clear_junk_clears_previous_junk(self): @skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only") def test_sigint_raises_no_result_error(self): # If we get a SIGINT during a run, we raise _spinner.NoResultError. - SIGINT = getattr(signal, 'SIGINT', None) + SIGINT = getattr(signal, "SIGINT", None) if not SIGINT: self.skipTest("SIGINT not available") reactor = self.make_reactor() @@ -259,7 +283,8 @@ def test_sigint_raises_no_result_error(self): reactor.callLater(timeout, os.kill, os.getpid(), SIGINT) self.assertThat( lambda: spinner.run(timeout * 5, defer.Deferred), - Raises(MatchesException(_spinner.NoResultError))) + Raises(MatchesException(_spinner.NoResultError)), + ) self.assertEqual([], spinner._clean()) @skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only") @@ -272,7 +297,7 @@ def test_sigint_raises_no_result_error_second_time(self): @skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only") def test_fast_sigint_raises_no_result_error(self): # If we get a SIGINT during a run, we raise _spinner.NoResultError. - SIGINT = getattr(signal, 'SIGINT', None) + SIGINT = getattr(signal, "SIGINT", None) if not SIGINT: self.skipTest("SIGINT not available") reactor = self.make_reactor() @@ -281,7 +306,8 @@ def test_fast_sigint_raises_no_result_error(self): reactor.callWhenRunning(os.kill, os.getpid(), SIGINT) self.assertThat( lambda: spinner.run(timeout * 5, defer.Deferred), - Raises(MatchesException(_spinner.NoResultError))) + Raises(MatchesException(_spinner.NoResultError)), + ) self.assertEqual([], spinner._clean()) @skipIf(os.name != "posix", "Sending SIGINT with os.kill is posix only") @@ -303,7 +329,8 @@ def test_fires_after_timeout(self): deferred1 = defer.Deferred() self.expectThat( lambda: spinner1.run(timeout, lambda: deferred1), - Raises(MatchesException(_spinner.TimeoutError))) + Raises(MatchesException(_spinner.TimeoutError)), + ) # Make a Deferred that will fire *after* deferred1 as long as the # reactor keeps spinning. We don't care that it's a callback of @@ -311,7 +338,8 @@ def test_fires_after_timeout(self): marker = object() deferred2 = defer.Deferred() deferred1.addCallback( - lambda ignored: reactor.callLater(0, deferred2.callback, marker)) + lambda ignored: reactor.callLater(0, deferred2.callback, marker) + ) def fire_other(): """Fire Deferred from the last spin while waiting for this one.""" @@ -324,4 +352,5 @@ def fire_other(): def test_suite(): from unittest import TestLoader + return TestLoader().loadTestsFromName(__name__) diff --git a/testtools/testsuite.py b/testtools/testsuite.py index 79fc4852..b129aa03 100644 --- a/testtools/testsuite.py +++ b/testtools/testsuite.py @@ -3,11 +3,11 @@ """Test suites and related things.""" __all__ = [ - 'ConcurrentTestSuite', - 'ConcurrentStreamTestSuite', - 'filter_by_ids', - 'iterate_tests', - 'sorted_tests', + "ConcurrentTestSuite", + "ConcurrentStreamTestSuite", + "filter_by_ids", + "iterate_tests", + "sorted_tests", ] from collections import Counter @@ -82,9 +82,11 @@ def run(self, result): semaphore = threading.Semaphore(1) for i, test in enumerate(tests): process_result = self._wrap_result( - testtools.ThreadsafeForwardingResult(result, semaphore), i) + testtools.ThreadsafeForwardingResult(result, semaphore), i + ) reader_thread = threading.Thread( - target=self._run_test, args=(test, process_result, queue)) + target=self._run_test, args=(test, process_result, queue) + ) threads[test] = reader_thread, process_result reader_thread.start() while threads: @@ -102,9 +104,7 @@ def _run_test(self, test, process_result, queue): test.run(process_result) except Exception: # The run logic itself failed. - case = testtools.ErrorHolder( - "broken-runner", - error=sys.exc_info()) + case = testtools.ErrorHolder("broken-runner", error=sys.exc_info()) case.run(process_result) finally: queue.put(test) @@ -154,24 +154,25 @@ def run(self, result): for test, route_code in tests: to_queue = testtools.StreamToQueue(queue, route_code) process_result = testtools.ExtendedToStreamDecorator( - testtools.TimestampingStreamResult(to_queue)) + testtools.TimestampingStreamResult(to_queue) + ) runner_thread = threading.Thread( - target=self._run_test, - args=(test, process_result, route_code)) + target=self._run_test, args=(test, process_result, route_code) + ) threads[to_queue] = runner_thread, process_result runner_thread.start() while threads: event_dict = queue.get() - event = event_dict.pop('event') - if event == 'status': + event = event_dict.pop("event") + if event == "status": result.status(**event_dict) - elif event == 'stopTestRun': - thread = threads.pop(event_dict['result'])[0] + elif event == "stopTestRun": + thread = threads.pop(event_dict["result"])[0] thread.join() - elif event == 'startTestRun': + elif event == "startTestRun": pass else: - raise ValueError(f'unknown event type {event!r}') + raise ValueError(f"unknown event type {event!r}") except: for thread, process_result in threads.values(): # Signal to each TestControl in the ExtendedToStreamDecorator @@ -187,15 +188,14 @@ def _run_test(self, test, process_result, route_code): except Exception: # The run logic itself failed. case = testtools.ErrorHolder( - f"broken-runner-'{route_code}'", - error=sys.exc_info()) + f"broken-runner-'{route_code}'", error=sys.exc_info() + ) case.run(process_result) finally: process_result.stopTestRun() class FixtureSuite(unittest.TestSuite): - def __init__(self, fixture, tests): super().__init__(tests) self._fixture = fixture @@ -217,7 +217,7 @@ def _flatten_tests(suite_or_case, unpack_outer=False): except TypeError: # Not iterable, assume it's a test case. return [(suite_or_case.id(), suite_or_case)] - if (type(suite_or_case) in (unittest.TestSuite,) or unpack_outer): + if type(suite_or_case) in (unittest.TestSuite,) or unpack_outer: # Plain old test suite (or any others we may add). result = [] for test in tests: @@ -232,7 +232,7 @@ def _flatten_tests(suite_or_case, unpack_outer=False): suite_id = test.id() break # If it has a sort_tests method, call that. - if hasattr(suite_or_case, 'sort_tests'): + if hasattr(suite_or_case, "sort_tests"): suite_or_case.sort_tests() return [(suite_id, suite_or_case)] @@ -279,10 +279,10 @@ def filter_by_ids(suite_or_case, test_ids): than guessing how to reconstruct a new suite. """ # Compatible objects - if hasattr(suite_or_case, 'filter_by_ids'): + if hasattr(suite_or_case, "filter_by_ids"): return suite_or_case.filter_by_ids(test_ids) # TestCase objects. - if hasattr(suite_or_case, 'id'): + if hasattr(suite_or_case, "id"): if suite_or_case.id() in test_ids: return suite_or_case else: @@ -302,11 +302,9 @@ def sorted_tests(suite_or_case, unpack_outer=False): # Duplicate test id can induce TypeError in Python 3.3. # Detect the duplicate test ids, raise exception when found. seen = Counter(case.id() for case in iterate_tests(suite_or_case)) - duplicates = { - test_id: count for test_id, count in seen.items() if count > 1} + duplicates = {test_id: count for test_id, count in seen.items() if count > 1} if duplicates: - raise ValueError( - f'Duplicate test ids detected: {pformat(duplicates)}') + raise ValueError(f"Duplicate test ids detected: {pformat(duplicates)}") tests = _flatten_tests(suite_or_case, unpack_outer=unpack_outer) tests.sort() diff --git a/testtools/twistedsupport/__init__.py b/testtools/twistedsupport/__init__.py index b80a7b71..86516ae7 100644 --- a/testtools/twistedsupport/__init__.py +++ b/testtools/twistedsupport/__init__.py @@ -4,17 +4,16 @@ __all__ = [ # Matchers - 'succeeded', - 'failed', - 'has_no_result', - + "succeeded", + "failed", + "has_no_result", # Running tests - 'AsynchronousDeferredRunTest', - 'AsynchronousDeferredRunTestForBrokenTwisted', - 'SynchronousDeferredRunTest', - 'CaptureTwistedLogs', - 'assert_fails_with', - 'flush_logged_errors', + "AsynchronousDeferredRunTest", + "AsynchronousDeferredRunTestForBrokenTwisted", + "SynchronousDeferredRunTest", + "CaptureTwistedLogs", + "assert_fails_with", + "flush_logged_errors", ] from ._matchers import ( diff --git a/testtools/twistedsupport/_deferred.py b/testtools/twistedsupport/_deferred.py index ffeb2d97..909e2f8f 100644 --- a/testtools/twistedsupport/_deferred.py +++ b/testtools/twistedsupport/_deferred.py @@ -50,10 +50,8 @@ class ImpossibleDeferredError(Exception): """Raised if a Deferred somehow triggers both a success and a failure.""" def __init__(self, deferred, successes, failures): - msg = ('Impossible condition on %r, got both success (%r) and ' - 'failure (%r)') - super().__init__( - msg % (deferred, successes, failures)) + msg = "Impossible condition on %r, got both success (%r) and " "failure (%r)" + super().__init__(msg % (deferred, successes, failures)) def on_deferred_result(deferred, on_success, on_failure, on_no_result): diff --git a/testtools/twistedsupport/_deferreddebug.py b/testtools/twistedsupport/_deferreddebug.py index 89c0b249..2a23e209 100644 --- a/testtools/twistedsupport/_deferreddebug.py +++ b/testtools/twistedsupport/_deferreddebug.py @@ -14,8 +14,8 @@ def __init__(self, debug=True): def _setUp(self): self.useFixture( - MonkeyPatch('twisted.internet.defer.Deferred.debug', - self._debug_setting)) + MonkeyPatch("twisted.internet.defer.Deferred.debug", self._debug_setting) + ) self.useFixture( - MonkeyPatch('twisted.internet.base.DelayedCall.debug', - self._debug_setting)) + MonkeyPatch("twisted.internet.base.DelayedCall.debug", self._debug_setting) + ) diff --git a/testtools/twistedsupport/_matchers.py b/testtools/twistedsupport/_matchers.py index 9c4695d9..3b2923f2 100644 --- a/testtools/twistedsupport/_matchers.py +++ b/testtools/twistedsupport/_matchers.py @@ -25,8 +25,8 @@ class _NoResult: @staticmethod def _got_result(deferred, result): return Mismatch( - 'No result expected on %r, found %r instead' - % (deferred, result)) + "No result expected on %r, found %r instead" % (deferred, result) + ) def match(self, deferred): """Match ``deferred`` if it hasn't fired.""" @@ -80,16 +80,16 @@ def __init__(self, matcher): def _got_failure(deferred, failure): deferred.addErrback(lambda _: None) return Mismatch( - 'Success result expected on %r, found failure result ' - 'instead: %r' % (deferred, failure), - {'traceback': failure_content(failure)}, + "Success result expected on %r, found failure result " + "instead: %r" % (deferred, failure), + {"traceback": failure_content(failure)}, ) @staticmethod def _got_no_result(deferred): return Mismatch( - 'Success result expected on %r, found no result ' - 'instead' % (deferred,)) + "Success result expected on %r, found no result " "instead" % (deferred,) + ) def match(self, deferred): """Match against the successful result of ``deferred``.""" @@ -139,14 +139,15 @@ def _got_failure(self, deferred, failure): @staticmethod def _got_success(deferred, success): return Mismatch( - 'Failure result expected on %r, found success ' - 'result (%r) instead' % (deferred, success)) + "Failure result expected on %r, found success " + "result (%r) instead" % (deferred, success) + ) @staticmethod def _got_no_result(deferred): return Mismatch( - 'Failure result expected on %r, found no result instead' - % (deferred,)) + "Failure result expected on %r, found no result instead" % (deferred,) + ) def match(self, deferred): return on_deferred_result( diff --git a/testtools/twistedsupport/_runtest.py b/testtools/twistedsupport/_runtest.py index d4f77758..9992c53b 100644 --- a/testtools/twistedsupport/_runtest.py +++ b/testtools/twistedsupport/_runtest.py @@ -20,11 +20,11 @@ def test_something(self): """ __all__ = [ - 'AsynchronousDeferredRunTest', - 'AsynchronousDeferredRunTestForBrokenTwisted', - 'SynchronousDeferredRunTest', - 'assert_fails_with', - ] + "AsynchronousDeferredRunTest", + "AsynchronousDeferredRunTestForBrokenTwisted", + "SynchronousDeferredRunTest", + "assert_fails_with", +] import io import warnings @@ -41,14 +41,16 @@ def test_something(self): Spinner, TimeoutError, trap_unhandled_errors, - ) +) from twisted.internet import defer + try: from twisted.logger import globalLogPublisher except ImportError: globalLogPublisher = None from twisted.python import log + try: from twisted.trial.unittest import _LogObserver except ImportError: @@ -58,11 +60,12 @@ def test_something(self): class _DeferredRunTest(RunTest): """Base for tests that return Deferreds.""" - def _got_user_failure(self, failure, tb_label='traceback'): + def _got_user_failure(self, failure, tb_label="traceback"): """We got a failure from user code.""" return self._got_user_exception( (failure.type, failure.value, failure.getTracebackObject()), - tb_label=tb_label) + tb_label=tb_label, + ) class SynchronousDeferredRunTest(_DeferredRunTest): @@ -167,21 +170,25 @@ def setUp(self): # ... do something with twisted_logs ... """ - LOG_DETAIL_NAME = 'twisted-log' + LOG_DETAIL_NAME = "twisted-log" def _setUp(self): logs = io.StringIO() full_observer = log.FileLogObserver(logs) self.useFixture(_TwistedLogObservers([full_observer.emit])) - self.addDetail(self.LOG_DETAIL_NAME, Content( - UTF8_TEXT, lambda: [logs.getvalue().encode("utf-8")])) + self.addDetail( + self.LOG_DETAIL_NAME, + Content(UTF8_TEXT, lambda: [logs.getvalue().encode("utf-8")]), + ) def run_with_log_observers(observers, function, *args, **kwargs): """Run 'function' with the given Twisted log observers.""" warnings.warn( - 'run_with_log_observers is deprecated since 1.8.2.', - DeprecationWarning, stacklevel=2) + "run_with_log_observers is deprecated since 1.8.2.", + DeprecationWarning, + stacklevel=2, + ) with _NoTwistedLogObservers(): with _TwistedLogObservers(observers): return function(*args, **kwargs) @@ -227,9 +234,17 @@ class AsynchronousDeferredRunTest(_DeferredRunTest): only fire if the reactor is left to spin for a while. """ - def __init__(self, case, handlers=None, last_resort=None, reactor=None, - timeout=0.005, debug=False, suppress_twisted_logging=True, - store_twisted_logs=True): + def __init__( + self, + case, + handlers=None, + last_resort=None, + reactor=None, + timeout=0.005, + debug=False, + suppress_twisted_logging=True, + store_twisted_logs=True, + ): """Construct an ``AsynchronousDeferredRunTest``. Please be sure to always use keyword syntax, not positional, as the @@ -256,8 +271,7 @@ def __init__(self, case, handlers=None, last_resort=None, reactor=None, that took place during the run as the 'twisted-log' detail. Defaults to True. """ - super().__init__( - case, handlers, last_resort) + super().__init__(case, handlers, last_resort) if reactor is None: from twisted.internet import reactor self._reactor = reactor @@ -267,8 +281,14 @@ def __init__(self, case, handlers=None, last_resort=None, reactor=None, self._store_twisted_logs = store_twisted_logs @classmethod - def make_factory(cls, reactor=None, timeout=0.005, debug=False, - suppress_twisted_logging=True, store_twisted_logs=True): + def make_factory( + cls, + reactor=None, + timeout=0.005, + debug=False, + suppress_twisted_logging=True, + store_twisted_logs=True, + ): """Make a factory that conforms to the RunTest factory interface. Example:: @@ -278,15 +298,23 @@ class SomeTests(TestCase): run_tests_with = AsynchronousDeferredRunTest.make_factory( timeout=120) """ + # This is horrible, but it means that the return value of the method # will be able to be assigned to a class variable *and* also be # invoked directly. class AsynchronousDeferredRunTestFactory: def __call__(self, case, handlers=None, last_resort=None): return cls( - case, handlers, last_resort, reactor, timeout, debug, - suppress_twisted_logging, store_twisted_logs, + case, + handlers, + last_resort, + reactor, + timeout, + debug, + suppress_twisted_logging, + store_twisted_logs, ) + return AsynchronousDeferredRunTestFactory() @defer.inlineCallbacks @@ -335,6 +363,7 @@ def clean_up_done(result): if result is not None: self._exceptions.append(result) fails.append(None) + return d.addCallback(clean_up_done) def set_up_done(exception_caught): @@ -355,7 +384,7 @@ def tear_down(ignored): return d def force_failure(ignored): - if getattr(self.case, 'force_failure', None): + if getattr(self.case, "force_failure", None): d = self._run_user(_raise_force_fail_error) d.addCallback(fails.append) return d @@ -375,8 +404,7 @@ def _log_user_exception(self, e): def _blocking_run_deferred(self, spinner): try: - return trap_unhandled_errors( - spinner.run, self._timeout, self._run_deferred) + return trap_unhandled_errors(spinner.run, self._timeout, self._run_deferred) except NoResultError: # We didn't get a result at all! This could be for any number of # reasons, but most likely someone hit Ctrl-C during the test. @@ -414,12 +442,10 @@ def _run_core(self): for name, detail in capture_logs.getDetails().items(): self.case.addDetail(name, detail) with _ErrorObserver(_log_observer) as error_fixture: - successful, unhandled = self._blocking_run_deferred( - spinner) + successful, unhandled = self._blocking_run_deferred(spinner) for logged_error in error_fixture.flush_logged_errors(): successful = False - self._got_user_failure( - logged_error, tb_label='logged-error') + self._got_user_failure(logged_error, tb_label="logged-error") if unhandled: successful = False @@ -428,9 +454,9 @@ def _run_core(self): info = debug_info._getDebugTracebacks() if info: self.case.addDetail( - 'unhandled-error-in-deferred-debug', - text_content(info)) - self._got_user_failure(f, 'unhandled-error-in-deferred') + "unhandled-error-in-deferred-debug", text_content(info) + ) + self._got_user_failure(f, "unhandled-error-in-deferred") junk = spinner.clear_junk() if junk: @@ -483,22 +509,26 @@ def assert_fails_with(d, *exc_types, **kwargs): :return: A ``Deferred`` that will fail with an ``AssertionError`` if ``d`` does not fail with one of the exception types. """ - failureException = kwargs.pop('failureException', None) + failureException = kwargs.pop("failureException", None) if failureException is None: # Avoid circular imports. from testtools import TestCase + failureException = TestCase.failureException expected_names = ", ".join(exc_type.__name__ for exc_type in exc_types) def got_success(result): - raise failureException( - f"{expected_names} not raised ({result!r} returned)") + raise failureException(f"{expected_names} not raised ({result!r} returned)") def got_failure(failure): if failure.check(*exc_types): return failure.value - raise failureException("{} raised instead of {}:\n {}".format( - failure.type.__name__, expected_names, failure.getTraceback())) + raise failureException( + "{} raised instead of {}:\n {}".format( + failure.type.__name__, expected_names, failure.getTraceback() + ) + ) + return d.addCallbacks(got_success, got_failure) @@ -511,12 +541,14 @@ def __init__(self, junk): "The reactor still thinks it needs to do things. Close all " "connections, kill all processes and make sure all delayed " "calls have either fired or been cancelled:\n%s" - % ''.join(map(self._get_junk_info, junk))) + % "".join(map(self._get_junk_info, junk)), + ) def _get_junk_info(self, junk): from twisted.internet.base import DelayedCall + if isinstance(junk, DelayedCall): ret = str(junk) else: ret = repr(junk) - return f' {ret}\n' + return f" {ret}\n" diff --git a/testtools/twistedsupport/_spinner.py b/testtools/twistedsupport/_spinner.py index a7bb648a..41520bbb 100644 --- a/testtools/twistedsupport/_spinner.py +++ b/testtools/twistedsupport/_spinner.py @@ -7,14 +7,14 @@ """ __all__ = [ - 'NoResultError', - 'not_reentrant', - 'ReentryError', - 'Spinner', - 'StaleJunkError', - 'TimeoutError', - 'trap_unhandled_errors', - ] + "NoResultError", + "not_reentrant", + "ReentryError", + "Spinner", + "StaleJunkError", + "TimeoutError", + "trap_unhandled_errors", +] from fixtures import Fixture import signal @@ -32,9 +32,11 @@ class ReentryError(Exception): """Raised when we try to re-enter a function that forbids it.""" def __init__(self, function): - Exception.__init__(self, + Exception.__init__( + self, "%r in not re-entrant but was called within a call to itself." - % (function,)) + % (function,), + ) def not_reentrant(function, _calls={}): @@ -42,6 +44,7 @@ def not_reentrant(function, _calls={}): The decorated function will raise an error if called from within itself. """ + def decorated(*args, **kwargs): if _calls.get(function, False): raise ReentryError(function) @@ -50,6 +53,7 @@ def decorated(*args, **kwargs): return function(*args, **kwargs) finally: _calls[function] = False + return mergeFunctionMetadata(function, decorated) @@ -76,7 +80,6 @@ def trap_unhandled_errors(function, *args, **kwargs): # via inheritance doesn't work with Python 2. So we handle the two cases # differently. TODO: perhaps there's a way to have a single code path? class DebugInfo(real_DebugInfo): # type: ignore - _runRealDel = True def __init__(self): @@ -106,26 +109,29 @@ class TimeoutError(Exception): """Raised when run_in_reactor takes too long to run a function.""" def __init__(self, function, timeout): - Exception.__init__(self, - f"{function!r} took longer than {timeout} seconds") + Exception.__init__(self, f"{function!r} took longer than {timeout} seconds") class NoResultError(Exception): """Raised when the reactor has stopped but we don't have any result.""" def __init__(self): - Exception.__init__(self, + Exception.__init__( + self, "Tried to get test's result from Deferred when no result is " - "available. Probably means we received SIGINT or similar.") + "available. Probably means we received SIGINT or similar.", + ) class StaleJunkError(Exception): """Raised when there's junk in the spinner from a previous run.""" def __init__(self, junk): - Exception.__init__(self, + Exception.__init__( + self, "There was junk in the spinner from a previous run. " - "Use clear_junk() to clear it out: %r" % (junk,)) + "Use clear_junk() to clear it out: %r" % (junk,), + ) class Spinner: @@ -141,10 +147,10 @@ class Spinner: # Signals that we save and restore for each spin. _PRESERVED_SIGNALS = [ - 'SIGINT', - 'SIGTERM', - 'SIGCHLD', - ] + "SIGINT", + "SIGTERM", + "SIGCHLD", + ] # There are many APIs within Twisted itself where a Deferred fires but # leaves cleanup work scheduled for the reactor to do. Arguably, many of @@ -255,9 +261,11 @@ def get_junk(self): def _save_signals(self): available_signals = [ - getattr(signal, name, None) for name in self._PRESERVED_SIGNALS] + getattr(signal, name, None) for name in self._PRESERVED_SIGNALS + ] self._saved_signals = [ - (sig, signal.getsignal(sig)) for sig in available_signals if sig] + (sig, signal.getsignal(sig)) for sig in available_signals if sig + ] def _restore_signals(self): for sig, hdlr in self._saved_signals: @@ -293,7 +301,8 @@ def run(self, timeout, function, *args, **kwargs): raise StaleJunkError(junk) self._save_signals() self._timeout_call = self._reactor.callLater( - timeout, self._timed_out, function, timeout) + timeout, self._timed_out, function, timeout + ) # Calling 'stop' on the reactor will make it impossible to # re-start the reactor. Since the default signal handlers for # TERM, BREAK and INT all call reactor.stop(), we'll patch it over @@ -306,6 +315,7 @@ def run_function(): d = defer.maybeDeferred(function, *args, **kwargs) d.addCallbacks(self._got_success, self._got_failure) d.addBoth(self._stop_reactor) + try: self._reactor.callWhenRunning(run_function) self._spinning = True diff --git a/testtools/utils.py b/testtools/utils.py index d345efe1..4df3cd7f 100644 --- a/testtools/utils.py +++ b/testtools/utils.py @@ -6,7 +6,10 @@ """ import warnings -warnings.warn("Please import iterate_tests from testtools.testsuite - " - "testtools.utils is deprecated.", DeprecationWarning, stacklevel=2) - +warnings.warn( + "Please import iterate_tests from testtools.testsuite - " + "testtools.utils is deprecated.", + DeprecationWarning, + stacklevel=2, +)