Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ module = [
# except tests (we're not sadists)
"testtools.twistedsupport.*",
"tests.*",
# NOTE(stephenfin): These are deprecated so we're not going to type them
"testtools.tests.helpers",
]
disallow_untyped_calls = false
disallow_untyped_defs = false
Expand Down
2 changes: 1 addition & 1 deletion tests/matchers/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
_BinaryMismatch,
_NotNearlyEqual,
)
from testtools.matchers.test import TestMatchersInterface

from ..helpers import FullStackRunTest
from ..matchers.helpers import TestMatchersInterface


class Test_BinaryMismatch(TestCase):
Expand Down
3 changes: 1 addition & 2 deletions tests/matchers/test_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

from testtools import TestCase
from testtools.matchers import Always, Never

from ..matchers.helpers import TestMatchersInterface
from testtools.matchers.test import TestMatchersInterface


class TestAlwaysInterface(TestMatchersInterface, TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tests/matchers/test_datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
MatchesSetwise,
MatchesStructure,
)
from testtools.matchers.test import TestMatchersInterface

from ..helpers import FullStackRunTest
from ..matchers.helpers import TestMatchersInterface


def run_doctest(obj, name):
Expand Down
3 changes: 1 addition & 2 deletions tests/matchers/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
MatchesDict,
_SubDictOf,
)

from ..matchers.helpers import TestMatchersInterface
from testtools.matchers.test import TestMatchersInterface


class TestMatchesAllDictInterface(TestCase, TestMatchersInterface):
Expand Down
2 changes: 1 addition & 1 deletion tests/matchers/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

from testtools import TestCase
from testtools.matchers._doctest import DocTestMatches
from testtools.matchers.test import TestMatchersInterface

from ..helpers import FullStackRunTest
from ..matchers.helpers import TestMatchersInterface


class TestDocTestMatchesInterface(TestCase, TestMatchersInterface):
Expand Down
2 changes: 1 addition & 1 deletion tests/matchers/test_exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
Raises,
raises,
)
from testtools.matchers.test import TestMatchersInterface

from ..helpers import FullStackRunTest
from ..matchers.helpers import TestMatchersInterface


def make_error(type, *args, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion tests/matchers/test_higherorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
MatchesPredicateWithParams,
Not,
)
from testtools.matchers.test import TestMatchersInterface

from ..helpers import FullStackRunTest
from ..matchers.helpers import TestMatchersInterface


class TestAllMatch(TestCase, TestMatchersInterface):
Expand Down
2 changes: 1 addition & 1 deletion tests/matchers/test_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
MatchesStructure,
)
from testtools.matchers._warnings import IsDeprecated, WarningMessage, Warnings
from testtools.matchers.test import TestMatchersInterface

from ..helpers import FullStackRunTest
from ..matchers.helpers import TestMatchersInterface


def make_warning(warning_type, message):
Expand Down
39 changes: 39 additions & 0 deletions tests/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import ast
import io
import sys
import traceback

import testtools
from testtools.compat import (
reraise,
text_repr,
unicode_output_stream,
)
Expand Down Expand Up @@ -216,6 +218,43 @@ 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"""

def test_exc_info(self):
"""After reraise exc_info matches plus some extra traceback"""
try:
raise ValueError("Bad value")
except ValueError:
_exc_info = sys.exc_info()
try:
reraise(*_exc_info)
except ValueError:
_new_exc_info = sys.exc_info()
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) :]
)

def test_custom_exception_no_args(self):
"""Reraising does not require args attribute to contain params"""

class CustomException(Exception):
"""Exception that expects and sets attrs but not args"""

def __init__(self, value):
Exception.__init__(self)
self.value = value

try:
raise CustomException("Some value")
except CustomException:
_exc_info = sys.exc_info()
self.assertRaises(CustomException, reraise, *_exc_info)


def test_suite():
from unittest import TestLoader

Expand Down
38 changes: 37 additions & 1 deletion testtools/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,45 @@
import codecs
import io
import sys
import types
import unicodedata
import warnings
from io import BytesIO, StringIO # for backwards-compat
from typing import IO
from typing import IO, Any, NoReturn


def reraise(
exc_class: type[BaseException],
exc_obj: BaseException,
exc_tb: types.TracebackType,
_marker: Any = object(),
) -> NoReturn:
"""Re-raise an exception received from sys.exc_info() or similar."""
warnings.warn(
"This is not necessary in Python 3.",
DeprecationWarning,
stacklevel=2,
)
raise exc_obj.with_traceback(exc_tb)


def _u(s: str) -> str:
warnings.warn(
"This is not necessary in Python 3.",
DeprecationWarning,
stacklevel=2,
)
return s


def _b(s: str) -> bytes:
"""A byte literal."""
warnings.warn(
"This is not necessary in Python 3.",
DeprecationWarning,
stacklevel=2,
)
return s.encode("latin-1")


def _slow_escape(text: str) -> str:
Expand Down
File renamed without changes.
Empty file added testtools/tests/__init__.py
Empty file.
166 changes: 166 additions & 0 deletions testtools/tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Copyright (c) 2008-2016 testtools developers. See LICENSE for details.

"""Helpers for tests."""

__all__ = [
"LoggingResult",
]

import sys
import warnings

from testtools import TestResult, runtest
from testtools.content import StackLinesContent
from testtools.matchers import (
AfterPreprocessing,
Equals,
MatchesDict,
MatchesListwise,
)

warnings.warn(
"This module is deprecated for removal",
DeprecationWarning,
stacklevel=2,
)


# GZ 2010-08-12: Don't do this, pointlessly creates an exc_info cycle
try:
raise Exception
except Exception:
an_exc_info = sys.exc_info()


# Deprecated: This classes attributes are somewhat non deterministic which
# leads to hard to predict tests (because Python upstream are changing things.
class LoggingResult(TestResult):
"""TestResult that logs its event to a list."""

def __init__(self, log):
self._events = log
super().__init__()

def startTest(self, test):
self._events.append(("startTest", test))
super().startTest(test)

def stop(self):
self._events.append("stop")
super().stop()

def stopTest(self, test):
self._events.append(("stopTest", test))
super().stopTest(test)

def addFailure(self, test, err=None, details=None):
self._events.append(("addFailure", test, err))
super().addFailure(test, err, details)

def addError(self, test, err=None, details=None):
self._events.append(("addError", test, err))
super().addError(test, err, details)

def addSkip(self, test, reason=None, details=None):
# Extract reason from details if not provided directly
if reason is None and details and "reason" in details:
reason = details["reason"].as_text()
self._events.append(("addSkip", test, reason))
super().addSkip(test, reason, details)

def addSuccess(self, test, details=None):
self._events.append(("addSuccess", test))
super().addSuccess(test, details)

def startTestRun(self):
self._events.append("startTestRun")
super().startTestRun()

def stopTestRun(self):
self._events.append("stopTestRun")
super().stopTestRun()

def done(self):
self._events.append("done")
super().done()

def tags(self, 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))
super().time(a_datetime)


def is_stack_hidden():
return StackLinesContent.HIDE_INTERNAL_STACK


def hide_testtools_stack(should_hide=True):
result = StackLinesContent.HIDE_INTERNAL_STACK
StackLinesContent.HIDE_INTERNAL_STACK = should_hide
return result


def run_with_stack_hidden(should_hide, f, *args, **kwargs):
old_should_hide = hide_testtools_stack(should_hide)
try:
return f(*args, **kwargs)
finally:
hide_testtools_stack(old_should_hide)


class FullStackRunTest(runtest.RunTest):
def _run_user(self, fn, *args, **kwargs):
return run_with_stack_hidden(False, super()._run_user, fn, *args, **kwargs)


class MatchesEvents:
"""Match a list of test result events.

Specify events as a data structure. Ordinary Python objects within this
structure will be compared exactly, but you can also use matchers at any
point.
"""

def __init__(self, *expected):
self._expected = 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"):
return obj
elif isinstance(obj, tuple) or isinstance(obj, list):
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()}
)
else:
return Equals(obj)

def match(self, observed):
matcher = self._make_matcher(self._expected)
return matcher.match(observed)


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)


def raise_(exception):
"""Raise ``exception``.

Useful for raising exceptions when it is inconvenient to use a statement
(e.g. in a lambda).

:param Exception exception: An exception to raise.
:raises: Whatever exception is

"""
raise exception
Empty file.
15 changes: 15 additions & 0 deletions testtools/tests/matchers/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) 2008-2012 testtools developers. See LICENSE for details.

import warnings

from testtools.matchers.test import TestMatchersInterface

warnings.warn(
"This module is deprecated for removal",
DeprecationWarning,
stacklevel=2,
)

__all__ = [
"TestMatchersInterface",
]