Skip to content

Commit deeaac4

Browse files
authored
bpo-40280: Skip socket, fork, subprocess tests on Emscripten (GH-31986)
- Add requires_fork and requires_subprocess to more tests - Skip extension import tests if dlopen is not available - Don't assume that _testcapi is a shared extension - Skip a lot of socket tests that don't work on Emscripten - Skip mmap tests, mmap emulation is incomplete - venv does not work yet - Cannot get libc from executable The "entire" test suite is now passing on Emscripten with EMSDK from git head (91 suites are skipped).
1 parent a25a985 commit deeaac4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+238
-23
lines changed

Lib/test/lock_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from test.support import threading_helper
1616

1717

18-
requires_fork = unittest.skipUnless(hasattr(os, 'fork'),
18+
requires_fork = unittest.skipUnless(support.has_fork_support,
1919
"platform doesn't support fork "
2020
"(no _at_fork_reinit method)")
2121

Lib/test/pythoninfo.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import re
77
import sys
88
import traceback
9+
import unittest
910
import warnings
1011

1112

@@ -615,7 +616,7 @@ def collect_resource(info_add):
615616
def collect_test_socket(info_add):
616617
try:
617618
from test import test_socket
618-
except ImportError:
619+
except (ImportError, unittest.SkipTest):
619620
return
620621

621622
# all check attributes like HAVE_SOCKET_CAN

Lib/test/support/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"requires_IEEE_754", "requires_zlib",
4343
"has_fork_support", "requires_fork",
4444
"has_subprocess_support", "requires_subprocess",
45+
"has_socket_support", "requires_working_socket",
4546
"anticipate_failure", "load_package_tests", "detect_api_mismatch",
4647
"check__all__", "skip_if_buggy_ucrt_strfptime",
4748
"check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer",
@@ -520,6 +521,21 @@ def requires_subprocess():
520521
"""Used for subprocess, os.spawn calls, fd inheritance"""
521522
return unittest.skipUnless(has_subprocess_support, "requires subprocess support")
522523

524+
# Emscripten's socket emulation has limitation. WASI doesn't have sockets yet.
525+
has_socket_support = not is_emscripten and not is_wasi
526+
527+
def requires_working_socket(*, module=False):
528+
"""Skip tests or modules that require working sockets
529+
530+
Can be used as a function/class decorator or to skip an entire module.
531+
"""
532+
msg = "requires socket support"
533+
if module:
534+
if not has_socket_support:
535+
raise unittest.SkipTest(msg)
536+
else:
537+
return unittest.skipUnless(has_socket_support, msg)
538+
523539
# Does strftime() support glibc extension like '%4Y'?
524540
has_strftime_extensions = False
525541
if sys.platform != "win32":

Lib/test/test_asyncgen.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import contextlib
55

66
from test.support.import_helper import import_module
7-
from test.support import gc_collect
7+
from test.support import gc_collect, requires_working_socket
88
asyncio = import_module("asyncio")
99

1010

11+
requires_working_socket(module=True)
12+
1113
_no_default = object()
1214

1315

Lib/test/test_asynchat.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import asynchat
1919
import asyncore
2020

21+
support.requires_working_socket(module=True)
22+
2123
HOST = socket_helper.HOST
2224
SERVER_QUIT = b'QUIT\n'
2325

Lib/test/test_asyncio/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
2+
from test import support
23
from test.support import load_package_tests
34
from test.support import import_helper
45

6+
support.requires_working_socket(module=True)
57

68
# Skip tests if we don't have concurrent.futures.
79
import_helper.import_module('concurrent.futures')

Lib/test/test_asyncore.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
if support.PGO:
1919
raise unittest.SkipTest("test is not helpful for PGO")
2020

21+
support.requires_working_socket(module=True)
22+
2123
import warnings
2224
with warnings.catch_warnings():
2325
warnings.simplefilter('ignore', DeprecationWarning)

Lib/test/test_contextlib_async.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from test.test_contextlib import TestBaseExitStack
1010

11+
support.requires_working_socket(module=True)
1112

1213
def _async_test(func):
1314
"""Decorator to turn an async function into a test case."""

Lib/test/test_doctest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
import types
1919
import contextlib
2020

21+
22+
if not support.has_subprocess_support:
23+
raise unittest.SkipTest("test_CLI requires subprocess support.")
24+
25+
2126
# NOTE: There are some additional tests relating to interaction with
2227
# zipimport in the test_zipimport_support test module.
2328

@@ -455,7 +460,7 @@ def basics(): r"""
455460
>>> tests = finder.find(sample_func)
456461
457462
>>> print(tests) # doctest: +ELLIPSIS
458-
[<DocTest sample_func from test_doctest.py:28 (1 example)>]
463+
[<DocTest sample_func from test_doctest.py:33 (1 example)>]
459464
460465
The exact name depends on how test_doctest was invoked, so allow for
461466
leading path components.

Lib/test/test_docxmlrpc.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import sys
55
import threading
66
import unittest
7+
from test import support
8+
9+
support.requires_working_socket(module=True)
710

811
def make_request_and_skipIf(condition, reason):
912
# If we skip the test, we have to make a request because

Lib/test/test_ftplib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import asyncore
3030
import asynchat
3131

32+
support.requires_working_socket(module=True)
3233

3334
TIMEOUT = support.LOOPBACK_TIMEOUT
3435
DEFAULT_ENCODING = 'utf-8'

Lib/test/test_httplib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from test.support import socket_helper
2020
from test.support import warnings_helper
2121

22+
support.requires_working_socket(module=True)
2223

2324
here = os.path.dirname(__file__)
2425
# Self-signed cert file for 'localhost'

Lib/test/test_httpservers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from test.support import os_helper
3434
from test.support import threading_helper
3535

36+
support.requires_working_socket(module=True)
3637

3738
class NoLogRequestHandler:
3839
def log_message(self, *args):

Lib/test/test_imaplib.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import socket
1212

1313
from test.support import (verbose,
14-
run_with_tz, run_with_locale, cpython_only)
14+
run_with_tz, run_with_locale, cpython_only,
15+
requires_working_socket)
1516
from test.support import hashlib_helper
1617
from test.support import threading_helper
1718
from test.support import warnings_helper
@@ -23,6 +24,8 @@
2324
except ImportError:
2425
ssl = None
2526

27+
support.requires_working_socket(module=True)
28+
2629
CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "keycert3.pem")
2730
CAFILE = os.path.join(os.path.dirname(__file__) or os.curdir, "pycacert.pem")
2831

Lib/test/test_import/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from test.support import os_helper
2222
from test.support import (
23-
STDLIB_DIR, is_jython, swap_attr, swap_item, cpython_only)
23+
STDLIB_DIR, is_jython, swap_attr, swap_item, cpython_only, is_emscripten)
2424
from test.support.import_helper import (
2525
forget, make_legacy_pyc, unlink, unload, DirsOnSysPath, CleanImport)
2626
from test.support.os_helper import (
@@ -101,8 +101,17 @@ def test_from_import_missing_attr_has_name_and_so_path(self):
101101
with self.assertRaises(ImportError) as cm:
102102
from _testcapi import i_dont_exist
103103
self.assertEqual(cm.exception.name, '_testcapi')
104-
self.assertEqual(cm.exception.path, _testcapi.__file__)
105-
self.assertRegex(str(cm.exception), r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|pyd)\)")
104+
if hasattr(_testcapi, "__file__"):
105+
self.assertEqual(cm.exception.path, _testcapi.__file__)
106+
self.assertRegex(
107+
str(cm.exception),
108+
r"cannot import name 'i_dont_exist' from '_testcapi' \(.*\.(so|pyd)\)"
109+
)
110+
else:
111+
self.assertEqual(
112+
str(cm.exception),
113+
"cannot import name 'i_dont_exist' from '_testcapi' (unknown location)"
114+
)
106115

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

526535
@unittest.skipUnless(os.name == 'posix',
527536
"test meaningful only on posix systems")
537+
@unittest.skipIf(is_emscripten, "Emscripten's umask is a stub.")
528538
def test_creation_mode(self):
529539
mask = 0o022
530540
with temp_umask(mask), _ready_to_import() as (name, path):

Lib/test/test_importlib/extension/test_finder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ class FinderTests(abc.FinderTests):
1010

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

13+
def setUp(self):
14+
if not self.machinery.EXTENSION_SUFFIXES:
15+
raise unittest.SkipTest("Requires dynamic loading support.")
16+
1317
def find_spec(self, fullname):
1418
importer = self.machinery.FileFinder(util.EXTENSIONS.path,
1519
(self.machinery.ExtensionFileLoader,

Lib/test/test_importlib/extension/test_loader.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
import importlib
1313
from test.support.script_helper import assert_python_failure
1414

15+
1516
class LoaderTests(abc.LoaderTests):
1617

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

1920
def setUp(self):
21+
if not self.machinery.EXTENSION_SUFFIXES:
22+
raise unittest.SkipTest("Requires dynamic loading support.")
2023
self.loader = self.machinery.ExtensionFileLoader(util.EXTENSIONS.name,
2124
util.EXTENSIONS.file_path)
2225

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

9396
def setUp(self):
97+
if not self.machinery.EXTENSION_SUFFIXES:
98+
raise unittest.SkipTest("Requires dynamic loading support.")
9499
self.name = '_testmultiphase'
95100
finder = self.machinery.FileFinder(None)
96101
self.spec = importlib.util.find_spec(self.name)

Lib/test/test_json/test_tool.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from test.support.script_helper import assert_python_ok
1111

1212

13+
@support.requires_subprocess()
1314
class TestTool(unittest.TestCase):
1415
data = """
1516

Lib/test/test_logging.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
except ImportError:
7676
pass
7777

78+
7879
class BaseTest(unittest.TestCase):
7980

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

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

10591063
# - end of server_helper section
10601064

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

16831688

1689+
@support.requires_working_socket()
16841690
class SocketHandlerTest(BaseTest):
16851691

16861692
"""Test for SocketHandler objects."""
@@ -1795,6 +1801,7 @@ def tearDown(self):
17951801
SocketHandlerTest.tearDown(self)
17961802
os_helper.unlink(self.address)
17971803

1804+
@support.requires_working_socket()
17981805
class DatagramHandlerTest(BaseTest):
17991806

18001807
"""Test for DatagramHandler."""
@@ -1876,6 +1883,7 @@ def tearDown(self):
18761883
DatagramHandlerTest.tearDown(self)
18771884
os_helper.unlink(self.address)
18781885

1886+
@support.requires_working_socket()
18791887
class SysLogHandlerTest(BaseTest):
18801888

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

1996+
@support.requires_working_socket()
19881997
class HTTPHandlerTest(BaseTest):
19891998
"""Test for HTTPHandler."""
19901999

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

3273+
@support.requires_working_socket()
32643274
def test_listen_config_10_ok(self):
32653275
with support.captured_stdout() as output:
32663276
self.setup_via_listener(json.dumps(self.config10))
@@ -3280,6 +3290,7 @@ def test_listen_config_10_ok(self):
32803290
('ERROR', '4'),
32813291
], stream=output)
32823292

3293+
@support.requires_working_socket()
32833294
def test_listen_config_1_ok(self):
32843295
with support.captured_stdout() as output:
32853296
self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
@@ -3294,6 +3305,7 @@ def test_listen_config_1_ok(self):
32943305
# Original logger output is empty.
32953306
self.assert_log_lines([])
32963307

3308+
@support.requires_working_socket()
32973309
def test_listen_verify(self):
32983310

32993311
def verify_fail(stuff):

Lib/test/test_mailbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,7 @@ def test_add_and_close(self):
10611061
self.assertEqual(contents, f.read())
10621062
self._box = self._factory(self._path)
10631063

1064-
@unittest.skipUnless(hasattr(os, 'fork'), "Test needs fork().")
1064+
@support.requires_fork()
10651065
@unittest.skipUnless(hasattr(socket, 'socketpair'), "Test needs socketpair().")
10661066
def test_lock_conflict(self):
10671067
# Fork off a child process that will lock the mailbox temporarily,

Lib/test/test_mmap.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from test.support import (requires, _2G, _4G, gc_collect, cpython_only)
1+
from test.support import (
2+
requires, _2G, _4G, gc_collect, cpython_only, is_emscripten
3+
)
24
from test.support.import_helper import import_module
35
from test.support.os_helper import TESTFN, unlink
46
import unittest
@@ -21,6 +23,12 @@ def random_tagname(length=10):
2123
suffix = ''.join(random.choices(string.ascii_uppercase, k=length))
2224
return f'{tagname_prefix}_{suffix}'
2325

26+
# Python's mmap module dup()s the file descriptor. Emscripten's FS layer
27+
# does not materialize file changes through a dupped fd to a new mmap.
28+
if is_emscripten:
29+
raise unittest.SkipTest("incompatible with Emscripten's mmap emulation.")
30+
31+
2432
class MmapTests(unittest.TestCase):
2533

2634
def setUp(self):

Lib/test/test_pdb.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from contextlib import ExitStack, redirect_stdout
1515
from io import StringIO
16+
from test import support
1617
from test.support import os_helper
1718
# This little helper class is essential for testing pdb under doctest.
1819
from test.test_doctest import _FakeInput
@@ -1363,6 +1364,7 @@ def test_pdb_issue_43318():
13631364
"""
13641365

13651366

1367+
@support.requires_subprocess()
13661368
class PdbTestCase(unittest.TestCase):
13671369
def tearDown(self):
13681370
os_helper.unlink(os_helper.TESTFN)

Lib/test/test_peg_generator/test_c_parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def test_parse(self):
7070
"""
7171

7272

73+
@support.requires_subprocess()
7374
class TestCParser(unittest.TestCase):
7475
def setUp(self):
7576
self._backup_config_vars = dict(sysconfig._CONFIG_VARS)

0 commit comments

Comments
 (0)