Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-40280: Skip socket, fork, subprocess tests on Emscripten #31986

Merged
merged 13 commits into from
Mar 22, 2022
2 changes: 1 addition & 1 deletion Lib/test/lock_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from test.support import threading_helper


requires_fork = unittest.skipUnless(hasattr(os, 'fork'),
requires_fork = unittest.skipUnless(support.has_fork_support,
"platform doesn't support fork "
"(no _at_fork_reinit method)")

Expand Down
3 changes: 2 additions & 1 deletion Lib/test/pythoninfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import sys
import traceback
import unittest
import warnings


Expand Down Expand Up @@ -615,7 +616,7 @@ def collect_resource(info_add):
def collect_test_socket(info_add):
try:
from test import test_socket
except ImportError:
except (ImportError, unittest.SkipTest):
return

# all check attributes like HAVE_SOCKET_CAN
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"requires_IEEE_754", "requires_zlib",
"has_fork_support", "requires_fork",
"has_subprocess_support", "requires_subprocess",
"has_socket_support", "requires_working_socket",
"anticipate_failure", "load_package_tests", "detect_api_mismatch",
"check__all__", "skip_if_buggy_ucrt_strfptime",
"check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer",
Expand Down Expand Up @@ -520,6 +521,21 @@ def requires_subprocess():
"""Used for subprocess, os.spawn calls, fd inheritance"""
return unittest.skipUnless(has_subprocess_support, "requires subprocess support")

# Emscripten's socket emulation has limitation. WASI doesn't have sockets yet.
has_socket_support = not is_emscripten and not is_wasi

def requires_working_socket(*, module=False):
tiran marked this conversation as resolved.
Show resolved Hide resolved
"""Skip tests or modules that require working sockets

Can be used as a function/class decorator or to skip an entire module.
"""
msg = "requires socket support"
if module:
if not has_socket_support:
raise unittest.SkipTest(msg)
else:
return unittest.skipUnless(has_socket_support, msg)

# Does strftime() support glibc extension like '%4Y'?
has_strftime_extensions = False
if sys.platform != "win32":
Expand Down
4 changes: 3 additions & 1 deletion Lib/test/test_asyncgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import contextlib

from test.support.import_helper import import_module
from test.support import gc_collect
from test.support import gc_collect, requires_working_socket
asyncio = import_module("asyncio")


requires_working_socket(module=True)

_no_default = object()


Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_asynchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import asynchat
import asyncore

support.requires_working_socket(module=True)

HOST = socket_helper.HOST
SERVER_QUIT = b'QUIT\n'

Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_asyncio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
from test import support
from test.support import load_package_tests
from test.support import import_helper

support.requires_working_socket(module=True)

# Skip tests if we don't have concurrent.futures.
import_helper.import_module('concurrent.futures')
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_asyncore.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
if support.PGO:
raise unittest.SkipTest("test is not helpful for PGO")

support.requires_working_socket(module=True)

import warnings
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_contextlib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from test.test_contextlib import TestBaseExitStack

support.requires_working_socket(module=True)

def _async_test(func):
"""Decorator to turn an async function into a test case."""
Expand Down
7 changes: 6 additions & 1 deletion Lib/test/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
import types
import contextlib


if not support.has_subprocess_support:
raise unittest.SkipTest("test_CLI requires subprocess support.")


# NOTE: There are some additional tests relating to interaction with
# zipimport in the test_zipimport_support test module.

Expand Down Expand Up @@ -455,7 +460,7 @@ def basics(): r"""
>>> tests = finder.find(sample_func)

>>> print(tests) # doctest: +ELLIPSIS
[<DocTest sample_func from test_doctest.py:28 (1 example)>]
[<DocTest sample_func from test_doctest.py:33 (1 example)>]

The exact name depends on how test_doctest was invoked, so allow for
leading path components.
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_docxmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import sys
import threading
import unittest
from test import support

support.requires_working_socket(module=True)

def make_request_and_skipIf(condition, reason):
# If we skip the test, we have to make a request because
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_ftplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import asyncore
import asynchat

support.requires_working_socket(module=True)

TIMEOUT = support.LOOPBACK_TIMEOUT
DEFAULT_ENCODING = 'utf-8'
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from test.support import socket_helper
from test.support import warnings_helper

support.requires_working_socket(module=True)

here = os.path.dirname(__file__)
# Self-signed cert file for 'localhost'
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_httpservers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from test.support import os_helper
from test.support import threading_helper

support.requires_working_socket(module=True)

class NoLogRequestHandler:
def log_message(self, *args):
Expand Down
5 changes: 4 additions & 1 deletion Lib/test/test_imaplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import socket

from test.support import (verbose,
run_with_tz, run_with_locale, cpython_only)
run_with_tz, run_with_locale, cpython_only,
requires_working_socket)
from test.support import hashlib_helper
from test.support import threading_helper
from test.support import warnings_helper
Expand All @@ -23,6 +24,8 @@
except ImportError:
ssl = None

support.requires_working_socket(module=True)

CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem")
CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem")

Expand Down
16 changes: 13 additions & 3 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from test.support import os_helper
from test.support import (
STDLIB_DIR, is_jython, swap_attr, swap_item, cpython_only)
STDLIB_DIR, is_jython, swap_attr, swap_item, cpython_only, is_emscripten)
from test.support.import_helper import (
forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport)
from test.support.os_helper import (
Expand Down Expand Up @@ -101,8 +101,17 @@ def test_from_import_missing_attr_has_name_and_so_path(self):
with self.assertRaises(ImportError) as cm:
from _testcapi import i_dont_exist
self.assertEqual(cm.exception.name, '_testcapi')
self.assertEqual(cm.exception.path, _testcapi.__file__)
self.assertRegex(str(cm.exception), r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|pyd)\)")
if hasattr(_testcapi, "__file__"):
self.assertEqual(cm.exception.path, _testcapi.__file__)
self.assertRegex(
str(cm.exception),
r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|pyd)\)"
)
else:
self.assertEqual(
str(cm.exception),
"cannot import name 'i_dont_exist' from '_testcapi' (unknown location)"
)

def test_from_import_missing_attr_has_name(self):
with self.assertRaises(ImportError) as cm:
Expand Down Expand Up @@ -525,6 +534,7 @@ class FilePermissionTests(unittest.TestCase):

@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
@unittest.skipIf(is_emscripten, "Emscripten's umask is a stub.")
def test_creation_mode(self):
mask = 0o022
with temp_umask(mask), _ready_to_import() as (name, path):
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_importlib/extension/test_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class FinderTests(abc.FinderTests):

"""Test the finder for extension modules."""

def setUp(self):
if not self.machinery.EXTENSION_SUFFIXES:
raise unittest.SkipTest("Requires dynamic loading support.")

def find_spec(self, fullname):
importer = self.machinery.FileFinder(util.EXTENSIONS.path,
(self.machinery.ExtensionFileLoader,
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_importlib/extension/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import importlib
from test.support.script_helper import assert_python_failure


class LoaderTests(abc.LoaderTests):

"""Test load_module() for extension modules."""

def setUp(self):
if not self.machinery.EXTENSION_SUFFIXES:
raise unittest.SkipTest("Requires dynamic loading support.")
self.loader = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name,
util.EXTENSIONS.file_path)

Expand Down Expand Up @@ -91,6 +94,8 @@ class MultiPhaseExtensionModuleTests(abc.LoaderTests):
# Test loading extension modules with multi-phase initialization (PEP 489).

def setUp(self):
if not self.machinery.EXTENSION_SUFFIXES:
raise unittest.SkipTest("Requires dynamic loading support.")
self.name = '_testmultiphase'
finder = self.machinery.FileFinder(None)
self.spec = importlib.util.find_spec(self.name)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_json/test_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from test.support.script_helper import assert_python_ok


@support.requires_subprocess()
class TestTool(unittest.TestCase):
data = """

Expand Down
12 changes: 12 additions & 0 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
except ImportError:
pass


class BaseTest(unittest.TestCase):

"""Base class for logging tests."""
Expand Down Expand Up @@ -626,6 +627,9 @@ def test_path_objects(self):
os.unlink(fn)

@unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
@unittest.skipIf(
support.is_emscripten, "Emscripten cannot fstat unlinked files."
)
def test_race(self):
# Issue #14632 refers.
def remove_loop(fname, tries):
Expand Down Expand Up @@ -1058,6 +1062,7 @@ class TestUnixDatagramServer(TestUDPServer):

# - end of server_helper section

@support.requires_working_socket()
class SMTPHandlerTest(BaseTest):
# bpo-14314, bpo-19665, bpo-34092: don't wait forever
TIMEOUT = support.LONG_TIMEOUT
Expand Down Expand Up @@ -1681,6 +1686,7 @@ def test_defaults_do_no_interpolation(self):
os.unlink(fn)


@support.requires_working_socket()
class SocketHandlerTest(BaseTest):

"""Test for SocketHandler objects."""
Expand Down Expand Up @@ -1795,6 +1801,7 @@ def tearDown(self):
SocketHandlerTest.tearDown(self)
os_helper.unlink(self.address)

@support.requires_working_socket()
class DatagramHandlerTest(BaseTest):

"""Test for DatagramHandler."""
Expand Down Expand Up @@ -1876,6 +1883,7 @@ def tearDown(self):
DatagramHandlerTest.tearDown(self)
os_helper.unlink(self.address)

@support.requires_working_socket()
class SysLogHandlerTest(BaseTest):

"""Test for SysLogHandler using UDP."""
Expand Down Expand Up @@ -1985,6 +1993,7 @@ def tearDown(self):
self.server_class.address_family = socket.AF_INET
super(IPv6SysLogHandlerTest, self).tearDown()

@support.requires_working_socket()
class HTTPHandlerTest(BaseTest):
"""Test for HTTPHandler."""

Expand Down Expand Up @@ -3261,6 +3270,7 @@ def setup_via_listener(self, text, verify=None):
logging.config.stopListening()
threading_helper.join_thread(t)

@support.requires_working_socket()
def test_listen_config_10_ok(self):
with support.captured_stdout() as output:
self.setup_via_listener(json.dumps(self.config10))
Expand All @@ -3280,6 +3290,7 @@ def test_listen_config_10_ok(self):
('ERROR', '4'),
], stream=output)

@support.requires_working_socket()
def test_listen_config_1_ok(self):
with support.captured_stdout() as output:
self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
Expand All @@ -3294,6 +3305,7 @@ def test_listen_config_1_ok(self):
# Original logger output is empty.
self.assert_log_lines([])

@support.requires_working_socket()
def test_listen_verify(self):

def verify_fail(stuff):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ def test_add_and_close(self):
self.assertEqual(contents, f.read())
self._box = self._factory(self._path)

@unittest.skipUnless(hasattr(os, 'fork'), "Test needs fork().")
@support.requires_fork()
@unittest.skipUnless(hasattr(socket, 'socketpair'), "Test needs socketpair().")
def test_lock_conflict(self):
# Fork off a child process that will lock the mailbox temporarily,
Expand Down
10 changes: 9 additions & 1 deletion Lib/test/test_mmap.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from test.support import (requires, _2G, _4G, gc_collect, cpython_only)
from test.support import (
requires, _2G, _4G, gc_collect, cpython_only, is_emscripten
)
from test.support.import_helper import import_module
from test.support.os_helper import TESTFN, unlink
import unittest
Expand All @@ -21,6 +23,12 @@ def random_tagname(length=10):
suffix = ''.join(random.choices(string.ascii_uppercase, k=length))
return f'{tagname_prefix}_{suffix}'

# Python's mmap module dup()s the file descriptor. Emscripten's FS layer
# does not materialize file changes through a dupped fd to a new mmap.
if is_emscripten:
raise unittest.SkipTest("incompatible with Emscripten's mmap emulation.")


class MmapTests(unittest.TestCase):

def setUp(self):
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from contextlib import ExitStack, redirect_stdout
from io import StringIO
from test import support
from test.support import os_helper
# This little helper class is essential for testing pdb under doctest.
from test.test_doctest import _FakeInput
Expand Down Expand Up @@ -1363,6 +1364,7 @@ def test_pdb_issue_43318():
"""


@support.requires_subprocess()
class PdbTestCase(unittest.TestCase):
def tearDown(self):
os_helper.unlink(os_helper.TESTFN)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_peg_generator/test_c_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ def test_parse(self):
"""


@support.requires_subprocess()
class TestCParser(unittest.TestCase):
def setUp(self):
self._backup_config_vars = dict(sysconfig._CONFIG_VARS)
Expand Down
Loading