| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- import contextlib
- import os
- import platform
- import socket
- import sys
- import sysconfig
- import textwrap
- import typing
- import unittest
- import warnings
- from tornado.testing import bind_unused_port
- _TestCaseType = typing.TypeVar("_TestCaseType", bound=typing.Type[unittest.TestCase])
- skipIfNonUnix = unittest.skipIf(
- os.name != "posix" or sys.platform == "cygwin", "non-unix platform"
- )
- # Set the environment variable NO_NETWORK=1 to disable any tests that
- # depend on an external network.
- skipIfNoNetwork = unittest.skipIf("NO_NETWORK" in os.environ, "network access disabled")
- skipNotCPython = unittest.skipIf(
- # "CPython" here essentially refers to the traditional synchronous refcounting GC,
- # so we skip these tests in free-threading builds of cpython too.
- platform.python_implementation() != "CPython"
- or sysconfig.get_config_var("Py_GIL_DISABLED"),
- "Not CPython implementation",
- )
- def _detect_ipv6():
- if not socket.has_ipv6:
- # socket.has_ipv6 check reports whether ipv6 was present at compile
- # time. It's usually true even when ipv6 doesn't work for other reasons.
- return False
- sock = None
- try:
- sock = socket.socket(socket.AF_INET6)
- sock.bind(("::1", 0))
- except OSError:
- return False
- finally:
- if sock is not None:
- sock.close()
- return True
- skipIfNoIPv6 = unittest.skipIf(not _detect_ipv6(), "ipv6 support not present")
- def refusing_port():
- """Returns a local port number that will refuse all connections.
- Return value is (cleanup_func, port); the cleanup function
- must be called to free the port to be reused.
- """
- # On travis-ci port numbers are reassigned frequently. To avoid
- # collisions with other tests, we use an open client-side socket's
- # ephemeral port number to ensure that nothing can listen on that
- # port.
- server_socket, port = bind_unused_port()
- server_socket.setblocking(True)
- client_socket = socket.socket()
- client_socket.connect(("127.0.0.1", port))
- conn, client_addr = server_socket.accept()
- conn.close()
- server_socket.close()
- return (client_socket.close, client_addr[1])
- def exec_test(caller_globals, caller_locals, s):
- """Execute ``s`` in a given context and return the result namespace.
- Used to define functions for tests in particular python
- versions that would be syntax errors in older versions.
- """
- # Flatten the real global and local namespace into our fake
- # globals: it's all global from the perspective of code defined
- # in s.
- global_namespace = dict(caller_globals, **caller_locals) # type: ignore
- local_namespace = {} # type: typing.Dict[str, typing.Any]
- exec(textwrap.dedent(s), global_namespace, local_namespace)
- return local_namespace
- @contextlib.contextmanager
- def ignore_deprecation():
- """Context manager to ignore deprecation warnings."""
- with warnings.catch_warnings():
- warnings.simplefilter("ignore", DeprecationWarning)
- yield
- ABT_SKIP_MESSAGE = "abstract base class"
- def abstract_base_test(cls: _TestCaseType) -> _TestCaseType:
- """Decorator to mark a test class as an "abstract" base class.
- This is different from a regular abstract base class because
- we do not limit instantiation of the class. (If we did, it would
- interfere with test discovery). Instead, we prevent the tests from
- being run.
- Subclasses of an abstract base test are run as normal. There is
- no support for the ``@abstractmethod`` decorator so there is no runtime
- check that all such methods are implemented.
- Note that while it is semantically cleaner to modify the test loader
- to exclude abstract base tests, this is more complicated and would
- interfere with third-party test runners. This approach degrades
- gracefully to other tools such as editor-integrated testing.
- """
- # Type-checking fails due to https://github.com/python/mypy/issues/14458
- # @functools.wraps(cls)
- class AbstractBaseWrapper(cls): # type: ignore
- @classmethod
- def setUpClass(cls):
- if cls is AbstractBaseWrapper:
- raise unittest.SkipTest(ABT_SKIP_MESSAGE)
- super(AbstractBaseWrapper, cls).setUpClass()
- return AbstractBaseWrapper # type: ignore
|