From 41d5391c551e2012f13d56ff4bcc1df15c2821d3 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Thu, 29 Feb 2024 21:32:50 +0000 Subject: [PATCH] gh-71052: Add test exclusions to support running the test suite on Android (#115918) --- Lib/test/support/__init__.py | 13 +++++++++++-- Lib/test/support/os_helper.py | 17 +++++++++++++++++ Lib/test/test_asyncio/test_subprocess.py | 4 ++-- Lib/test/test_compileall.py | 2 +- Lib/test/test_fcntl.py | 3 ++- Lib/test/test_interpreters/test_lifecycle.py | 1 + Lib/test/test_os.py | 11 ++++++++--- Lib/test/test_pathlib/test_pathlib.py | 2 +- Lib/test/test_posix.py | 12 ++++++------ Lib/test/test_pty.py | 8 +++----- Lib/test/test_venv.py | 11 +++++------ ...024-02-25-16-28-26.gh-issue-71052.lSb9EC.rst | 1 + 12 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2024-02-25-16-28-26.gh-issue-71052.lSb9EC.rst diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 401b2ce1fe2..14e3766a343 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -532,24 +532,33 @@ else: is_emscripten = sys.platform == "emscripten" is_wasi = sys.platform == "wasi" -# Apple mobile platforms (iOS/tvOS/watchOS) are POSIX-like but do not -# have subprocess or fork support. is_apple_mobile = sys.platform in {"ios", "tvos", "watchos"} is_apple = is_apple_mobile or sys.platform == "darwin" has_fork_support = hasattr(os, "fork") and not ( + # WASM and Apple mobile platforms do not support subprocesses. is_emscripten or is_wasi or is_apple_mobile + + # Although Android supports fork, it's unsafe to call it from Python because + # all Android apps are multi-threaded. + or is_android ) def requires_fork(): return unittest.skipUnless(has_fork_support, "requires working os.fork()") has_subprocess_support = not ( + # WASM and Apple mobile platforms do not support subprocesses. is_emscripten or is_wasi or is_apple_mobile + + # Although Android supports subproceses, they're almost never useful in + # practice (see PEP 738). And most of the tests that use them are calling + # sys.executable, which won't work when Python is embedded in an Android app. + or is_android ) def requires_subprocess(): diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index 22787e32b5f..ffa5fc52e9b 100644 --- a/Lib/test/support/os_helper.py +++ b/Lib/test/support/os_helper.py @@ -198,6 +198,23 @@ def skip_unless_symlink(test): return test if ok else unittest.skip(msg)(test) +_can_hardlink = None + +def can_hardlink(): + global _can_hardlink + if _can_hardlink is None: + # Android blocks hard links using SELinux + # (https://stackoverflow.com/q/32365690). + _can_hardlink = hasattr(os, "link") and not support.is_android + return _can_hardlink + + +def skip_unless_hardlink(test): + ok = can_hardlink() + msg = "requires hardlink support" + return test if ok else unittest.skip(msg)(test) + + _can_xattr = None diff --git a/Lib/test/test_asyncio/test_subprocess.py b/Lib/test/test_asyncio/test_subprocess.py index f50a9ebc031..890c0acc172 100644 --- a/Lib/test/test_asyncio/test_subprocess.py +++ b/Lib/test/test_asyncio/test_subprocess.py @@ -13,6 +13,8 @@ from test.test_asyncio import utils as test_utils from test import support from test.support import os_helper +if not support.has_subprocess_support: + raise unittest.SkipTest("test module requires subprocess") if support.MS_WINDOWS: import msvcrt @@ -47,7 +49,6 @@ class TestSubprocessTransport(base_subprocess.BaseSubprocessTransport): self._proc.pid = -1 -@support.requires_subprocess() class SubprocessTransportTests(test_utils.TestCase): def setUp(self): super().setUp() @@ -111,7 +112,6 @@ class SubprocessTransportTests(test_utils.TestCase): transport.close() -@support.requires_subprocess() class SubprocessMixin: def test_stdin_stdout(self): diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index 0ec6013dc11..14c2af19e3e 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -977,7 +977,7 @@ class CommandLineTestsNoSourceEpoch(CommandLineTestsBase, -@unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') +@os_helper.skip_unless_hardlink class HardlinkDedupTestsBase: # Test hardlink_dupes parameter of compileall.compile_dir() diff --git a/Lib/test/test_fcntl.py b/Lib/test/test_fcntl.py index 6d734d05245..8b4ed4a9e3a 100644 --- a/Lib/test/test_fcntl.py +++ b/Lib/test/test_fcntl.py @@ -129,7 +129,8 @@ class TestFcntl(unittest.TestCase): fcntl.fcntl(BadFile(INT_MIN - 1), fcntl.F_SETFL, os.O_NONBLOCK) @unittest.skipIf( - platform.machine().startswith('arm') and platform.system() == 'Linux', + any(platform.machine().startswith(name) for name in {"arm", "aarch"}) + and platform.system() in {"Linux", "Android"}, "ARM Linux returns EINVAL for F_NOTIFY DN_MULTISHOT") def test_fcntl_64_bit(self): # Issue #1309352: fcntl shouldn't fail when the third arg fits in a diff --git a/Lib/test/test_interpreters/test_lifecycle.py b/Lib/test/test_interpreters/test_lifecycle.py index 67b6f439c31..becf003e2e5 100644 --- a/Lib/test/test_interpreters/test_lifecycle.py +++ b/Lib/test/test_interpreters/test_lifecycle.py @@ -164,6 +164,7 @@ class StartupTests(TestBase): class FinalizationTests(TestBase): + @support.requires_subprocess() def test_gh_109793(self): # Make sure finalization finishes and the correct error code # is reported, even when subinterpreters get cleaned up at the end. diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 2372ac4c21e..3b2d5fccc30 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1592,6 +1592,9 @@ class FwalkTests(WalkTests): @unittest.skipIf( support.is_emscripten, "Cannot dup stdout on Emscripten" ) + @unittest.skipIf( + support.is_android, "dup return value is unpredictable on Android" + ) def test_fd_leak(self): # Since we're opening a lot of FDs, we must be careful to avoid leaks: # we both check that calling fwalk() a large number of times doesn't @@ -2492,8 +2495,10 @@ class Pep383Tests(unittest.TestCase): # test listdir without arguments current_directory = os.getcwd() try: - os.chdir(os.sep) - self.assertEqual(set(os.listdir()), set(os.listdir(os.sep))) + # The root directory is not readable on Android, so use a directory + # we created ourselves. + os.chdir(self.dir) + self.assertEqual(set(os.listdir()), expected) finally: os.chdir(current_directory) @@ -4838,7 +4843,7 @@ class TestScandir(unittest.TestCase): os.name == 'nt') def test_attributes(self): - link = hasattr(os, 'link') + link = os_helper.can_hardlink() symlink = os_helper.can_symlink() dirname = os.path.join(self.path, "dir") diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index c0dcf314da4..7e44ae61a5e 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -796,7 +796,7 @@ class PathTest(test_pathlib_abc.DummyPathTest, PurePathTest): self.assertFileNotFound(p.stat) self.assertFileNotFound(p.unlink) - @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present") + @os_helper.skip_unless_hardlink def test_hardlink_to(self): P = self.cls(self.base) target = P / 'fileA' diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index a45f620e18d..2706d5eb6d9 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1270,9 +1270,10 @@ class PosixTester(unittest.TestCase): self.assertIn(mine, possible_schedulers) try: parent = posix.sched_getscheduler(os.getppid()) - except OSError as e: - if e.errno != errno.EPERM: - raise + except PermissionError: + # POSIX specifies EPERM, but Android returns EACCES. Both errno + # values are mapped to PermissionError. + pass else: self.assertIn(parent, possible_schedulers) self.assertRaises(OSError, posix.sched_getscheduler, -1) @@ -1287,9 +1288,8 @@ class PosixTester(unittest.TestCase): try: posix.sched_setscheduler(0, mine, param) posix.sched_setparam(0, param) - except OSError as e: - if e.errno != errno.EPERM: - raise + except PermissionError: + pass self.assertRaises(OSError, posix.sched_setparam, -1, param) self.assertRaises(OSError, posix.sched_setscheduler, -1, mine, param) diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index 3f2bac0155f..dee94533c74 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -1,7 +1,6 @@ -import sys import unittest from test.support import ( - is_apple_mobile, is_emscripten, is_wasi, reap_children, verbose + is_android, is_apple_mobile, is_emscripten, is_wasi, reap_children, verbose ) from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink @@ -9,9 +8,8 @@ from test.support.os_helper import TESTFN, unlink # Skip these tests if termios is not available import_module('termios') -# Skip tests on WASM platforms, plus iOS/tvOS/watchOS -if is_apple_mobile or is_emscripten or is_wasi: - raise unittest.SkipTest(f"pty tests not required on {sys.platform}") +if is_android or is_apple_mobile or is_emscripten or is_wasi: + raise unittest.SkipTest("pty is not available on this platform") import errno import os diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index ba31beb81e8..f60c662d322 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -19,8 +19,9 @@ import sysconfig import tempfile from test.support import (captured_stdout, captured_stderr, skip_if_broken_multiprocessing_synchronize, verbose, - requires_subprocess, is_apple_mobile, is_emscripten, - is_wasi, requires_venv_with_pip, TEST_HOME_DIR, + requires_subprocess, is_android, is_apple_mobile, + is_emscripten, is_wasi, + requires_venv_with_pip, TEST_HOME_DIR, requires_resource, copy_python_src_ignore) from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree) import unittest @@ -39,10 +40,8 @@ requireVenvCreate = unittest.skipUnless( or sys._base_executable != sys.executable, 'cannot run venv.create from within a venv on this platform') -# Skip tests on WASM platforms, plus iOS/tvOS/watchOS -if is_apple_mobile or is_emscripten or is_wasi: - raise unittest.SkipTest(f"venv tests not required on {sys.platform}") - +if is_android or is_apple_mobile or is_emscripten or is_wasi: + raise unittest.SkipTest("venv is not available on this platform") @requires_subprocess() def check_output(cmd, encoding=None): diff --git a/Misc/NEWS.d/next/Tests/2024-02-25-16-28-26.gh-issue-71052.lSb9EC.rst b/Misc/NEWS.d/next/Tests/2024-02-25-16-28-26.gh-issue-71052.lSb9EC.rst new file mode 100644 index 00000000000..9d3467ca7e7 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2024-02-25-16-28-26.gh-issue-71052.lSb9EC.rst @@ -0,0 +1 @@ +Add test exclusions to support running the test suite on Android.