From 16910fc26413913bdbc7cd2b81813f1649a3a0c7 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 15:32:40 +0000 Subject: [PATCH] Improve test coverage for _markupbase, _aix_support, _ios_support and enhance README.rst - Added Lib/test/test_markupbase.py with comprehensive tests for ParserBase. - Added Lib/test/test_aix_support.py using mocks to test AIX platform logic. - Added Lib/test/test_ios_support.py using mocks to test iOS platform logic. - Updated README.rst with a "Quick Start for Contributors" section including build, test, and dev environment setup instructions. - Removed temporary analysis files. --- Lib/test/test_aix_support.py | 62 ++++++++++ Lib/test/test_ios_support.py | 62 ++++++++++ Lib/test/test_markupbase.py | 218 +++++++++++++++++++++++++++++++++++ README.rst | 17 +++ 4 files changed, 359 insertions(+) create mode 100644 Lib/test/test_aix_support.py create mode 100644 Lib/test/test_ios_support.py create mode 100644 Lib/test/test_markupbase.py diff --git a/Lib/test/test_aix_support.py b/Lib/test/test_aix_support.py new file mode 100644 index 000000000000000..dcb54c004565275 --- /dev/null +++ b/Lib/test/test_aix_support.py @@ -0,0 +1,62 @@ +import unittest +from unittest import mock +from test import support +import _aix_support + +class TestAIXSupport(unittest.TestCase): + def test_aix_vrtl(self): + # _aix_vrtl takes the last character of the version string as the first element of the list + self.assertEqual(_aix_support._aix_vrtl("7.1.4.34"), [7, 1, 4]) + self.assertEqual(_aix_support._aix_vrtl("6.1.0.0"), [6, 1, 0]) + self.assertEqual(_aix_support._aix_vrtl("1.2.3.4"), [1, 2, 3]) + + def test_aix_tag(self): + with mock.patch("sys.maxsize", 2**31 - 1): + self.assertEqual(_aix_support._aix_tag([6, 1, 7], 1415), "aix-6107-1415-32") + with mock.patch("sys.maxsize", 2**63 - 1): + self.assertEqual(_aix_support._aix_tag([6, 1, 7], 1415), "aix-6107-1415-64") + # Default build date + with mock.patch("sys.maxsize", 2**63 - 1): + self.assertEqual(_aix_support._aix_tag([7, 1, 0], 0), "aix-7100-9988-64") + + @mock.patch("subprocess.check_output") + def test_aix_bos_rte(self, mock_check_output): + mock_check_output.return_value = b"something:something:7.1.4.34:something:something:something:1806" + vrmf, bd = _aix_support._aix_bos_rte() + self.assertEqual(vrmf, "7.1.4.34") + self.assertEqual(bd, 1806) + + # No build date + mock_check_output.return_value = b"something:something:7.1.4.34:something:something:something:" + vrmf, bd = _aix_support._aix_bos_rte() + self.assertEqual(bd, 9988) + + @mock.patch("_aix_support._aix_bos_rte") + @mock.patch("sys.maxsize", 2**63 - 1) + def test_aix_platform(self, mock_bos_rte): + mock_bos_rte.return_value = ("7.1.4.34", 1806) + self.assertEqual(_aix_support.aix_platform(), "aix-7104-1806-64") + + @mock.patch("sysconfig.get_config_var") + def test_aix_bgt(self, mock_get_config_var): + mock_get_config_var.return_value = "powerpc-ibm-aix7.1.4.0" + self.assertEqual(_aix_support._aix_bgt(), [7, 1, 4]) + + mock_get_config_var.return_value = None + with self.assertRaises(ValueError): + _aix_support._aix_bgt() + + @mock.patch("sysconfig.get_config_var") + @mock.patch("_aix_support._aix_bgt") + @mock.patch("sys.maxsize", 2**63 - 1) + def test_aix_buildtag(self, mock_aix_bgt, mock_get_config_var): + mock_aix_bgt.return_value = [7, 1, 4] + mock_get_config_var.return_value = "1806" + self.assertEqual(_aix_support.aix_buildtag(), "aix-7104-1806-64") + + mock_get_config_var.return_value = "invalid" + with self.assertRaises(ValueError): + _aix_support.aix_buildtag() + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_ios_support.py b/Lib/test/test_ios_support.py new file mode 100644 index 000000000000000..a78b93ca7ffd375 --- /dev/null +++ b/Lib/test/test_ios_support.py @@ -0,0 +1,62 @@ +import unittest +from unittest import mock +from test import support + +# Import the module under test. It's already in the path when running via regrtest. +import _ios_support + +class TestIOSSupport(unittest.TestCase): + def test_get_platform_ios_no_objc(self): + with mock.patch("_ios_support.objc", None): + with mock.patch("sys.implementation._multiarch", "arm64-ios"): + self.assertIsNone(_ios_support.get_platform_ios()) + + def test_get_platform_ios_simulator(self): + # We need to mock objc and its methods + mock_objc = mock.Mock() + mock_objc.objc_getClass.return_value = 1 + mock_objc.sel_registerName.return_value = 2 + + # mock_objc.objc_msgSend.side_effect is used multiple times + mock_objc.objc_msgSend.side_effect = [ + 10, # device + 20, # systemVersion + 30, # systemName + 40, # model + b"iOS", # system + b"15.0", # release + b"iPhone" # model + ] + + # We also need to mock c_void_p and c_char_p if they are missing in the environment + # as the module uses them from its own namespace. + with mock.patch("_ios_support.objc", mock_objc): + with mock.patch("_ios_support.c_void_p", "c_void_p", create=True): + with mock.patch("_ios_support.c_char_p", "c_char_p", create=True): + with mock.patch("sys.implementation._multiarch", "x86_64-ios-simulator"): + result = _ios_support.get_platform_ios() + self.assertEqual(result, ("iOS", "15.0", "iPhone", True)) + + def test_get_platform_ios_device(self): + mock_objc = mock.Mock() + mock_objc.objc_getClass.return_value = 1 + mock_objc.sel_registerName.return_value = 2 + mock_objc.objc_msgSend.side_effect = [ + 10, # device + 20, # systemVersion + 30, # systemName + 40, # model + b"iOS", # system + b"15.0", # release + b"iPhone" # model + ] + + with mock.patch("_ios_support.objc", mock_objc): + with mock.patch("_ios_support.c_void_p", "c_void_p", create=True): + with mock.patch("_ios_support.c_char_p", "c_char_p", create=True): + with mock.patch("sys.implementation._multiarch", "arm64-ios"): + result = _ios_support.get_platform_ios() + self.assertEqual(result, ("iOS", "15.0", "iPhone", False)) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_markupbase.py b/Lib/test/test_markupbase.py new file mode 100644 index 000000000000000..235f99d33be1f3e --- /dev/null +++ b/Lib/test/test_markupbase.py @@ -0,0 +1,218 @@ +import unittest +from _markupbase import ParserBase + +class MockParser(ParserBase): + def __init__(self): + super().__init__() + self.rawdata = "" + self.reset() + self.decls = [] + self.comments = [] + + def handle_decl(self, decl): + self.decls.append(decl) + + def unknown_decl(self, data): + self.decls.append(data) + + def handle_comment(self, data): + self.comments.append(data) + +class TestMarkupBase(unittest.TestCase): + def test_init_fail(self): + with self.assertRaisesRegex(RuntimeError, "_markupbase.ParserBase must be subclassed"): + ParserBase() + + def test_updatepos(self): + parser = MockParser() + parser.rawdata = "abc\ndef\nghi" + parser.updatepos(0, 4) + self.assertEqual(parser.getpos(), (2, 0)) + parser.updatepos(4, 6) + self.assertEqual(parser.getpos(), (2, 2)) + parser.updatepos(6, 8) + self.assertEqual(parser.getpos(), (3, 0)) + + # No-op if i >= j + pos = parser.getpos() + parser.updatepos(8, 8) + self.assertEqual(parser.getpos(), pos) + parser.updatepos(9, 8) + self.assertEqual(parser.getpos(), pos) + + def test_parse_comment(self): + parser = MockParser() + parser.rawdata = "" + n = parser.parse_comment(0) + self.assertEqual(n, len(parser.rawdata)) + self.assertEqual(parser.comments, ["comment"]) + + # No report + parser = MockParser() + parser.rawdata = "" + n = parser.parse_comment(0, report=0) + self.assertEqual(n, len(parser.rawdata)) + self.assertEqual(parser.comments, []) + + # Incomplete + parser = MockParser() + parser.rawdata = " ]>" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + # Subset with percentage (parameter entity) + parser = MockParser() + parser.rawdata = "" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + def test_scan_name_errors(self): + parser = MockParser() + parser.rawdata = "" + n = parser.parse_declaration(0) + self.assertEqual(parser.decls, ["ELEMENT name ANY"]) + + def test_parse_doctype_attlist(self): + parser = MockParser() + # Simplified attlist: name type [value] [#constraint] + parser.rawdata = " ]>" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + # Enumerated type + parser.rawdata = " ]>" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + def test_parse_doctype_notation(self): + parser = MockParser() + parser.rawdata = " ]>" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + def test_parse_doctype_entity(self): + parser = MockParser() + parser.rawdata = " ]>" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + parser.rawdata = " ]>" + n = parser.parse_declaration(0) + self.assertEqual(n, len(parser.rawdata)) + + def test_errors_in_subset(self): + parser = MockParser() + # Unexpected char in subset + parser.rawdata = "" + with self.assertRaises(AssertionError): + parser.parse_declaration(0) + + # Unknown declaration in subset + parser.rawdata = " ]>" + with self.assertRaises(AssertionError): + parser.parse_declaration(0) + + # Unexpected char after subset + parser.rawdata = "" + with self.assertRaises(AssertionError): + parser.parse_declaration(0) + +if __name__ == "__main__": + unittest.main() diff --git a/README.rst b/README.rst index c507bf3b16ea4af..966e62527f68ed2 100644 --- a/README.rst +++ b/README.rst @@ -57,6 +57,23 @@ On Unix, Linux, BSD, macOS, and Cygwin:: This will install Python as ``python3``. +Quick Start for Contributors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1. **Clone the repository:** + ``git clone https://github.com/python/cpython.git`` +2. **Build Python:** + ``./configure --with-pydebug && make -j`` +3. **Run tests:** + - To run all tests: ``./python -m test`` + - To run a specific test: ``./python -m test test_os`` +4. **Create a virtual environment for development:** + ``./python -m venv venv && source venv/bin/activate`` +5. **Install development tools:** + ``python -m pip install pre-commit ruff`` +6. **Install pre-commit hooks:** + ``pre-commit install`` + You can pass many options to the configure script; run ``./configure --help`` to find out more. On macOS case-insensitive file systems and on Cygwin, the executable is called ``python.exe``; elsewhere it's just ``python``.