diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 86748b46de..1f15504724 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -205,10 +205,15 @@ jobs: # pytest once per directory `SHARDS` names, then once over the rest, # and every run starts its workers afresh. Each run but the last writes # a JUnit file of its own, beside junit.xml; the step fails if any did. + # + # #2600: run whole, tests/protocols took the workers past the runner's + # 16 GB, and its tail stalled past tests._support.time_limit's 30 s. So + # its internet, application and misc subpackages run first, each a shard + # of its own, and `tests/protocols` then takes the remainder. env: COVERAGE: ${{ matrix.coverage }} LATEST: ${{ matrix.python-version == needs.python-versions.outputs.latest }} - SHARDS: ${{ startsWith(matrix.python-version, 'pypy') && 'tests/protocols tests/corekit' || '' }} + SHARDS: ${{ startsWith(matrix.python-version, 'pypy') && 'tests/protocols/internet tests/protocols/application tests/protocols/misc tests/protocols tests/corekit' || '' }} run: | if [ "$LATEST" = "true" ]; then if ! skipped=$(python -c "from tests._tiers import project_pin_paths; print(' '.join(project_pin_paths()))"); then diff --git a/docs/source/contributing/workflows.rst b/docs/source/contributing/workflows.rst index 520654e75b..4cf70efc97 100644 --- a/docs/source/contributing/workflows.rst +++ b/docs/source/contributing/workflows.rst @@ -1046,12 +1046,21 @@ extras cap |cryptography|_ below 46, as 45.0.7 is the newest release with a Linu On PyPy an xdist worker's RSS grows with every test it runs, rather than in a few heavy modules, so a PyPy leg's ``Run unit tests`` hands its pytest command to :file:`util/pytest_shards.py`, as GraalPy's leg does (see below), which runs it once per -directory its ``SHARDS`` names, ``tests/protocols`` and then ``tests/corekit``, and once -more over the rest, ignoring each (:issue:`2005`). +directory its ``SHARDS`` names and once more over the rest, ignoring each +(:issue:`2005`). The directories are ``tests/protocols/internet``, +``tests/protocols/application``, ``tests/protocols/misc``, ``tests/protocols`` and +``tests/corekit``, in that order, so ``tests/protocols`` runs after its own +subpackages and takes only what they left. Each run starts its workers afresh. Measured on PyPy 8.0.1 at ``-n 4``, one run over the whole ``ALL`` tier took each worker to 5.4-5.9 GB and the workers together past 20 GB, beyond the runner's 16 GB; split, the largest run peaked at 2.6 GB a -worker and 8.5 GB in all, and the tier took 383 s rather than 493 s. A smaller +worker and 8.5 GB in all, and the tier took 383 s rather than 493 s. With +:issue:`2543`'s checksum tests, ``tests/protocols`` run whole outgrew the runner as +well, and on CI the tests at its tail stalled past ``tests._support.time_limit``'s +30-second deadline, which counts wall-clock time, so it is split (:issue:`2600`). +Measured on PyPy 7.3.19 at ``-n 4``, its one run peaked at 17.7 GiB and took 267 s, +its slowest test 16.4 s; split, the largest of its runs peaked at 5.7 GiB, they took +224 s together, and the slowest test 11.9 s. A smaller ``-n`` would only hand each worker more tests, and a ``PYPY_GC_MAX`` heap cap turns the kills into ``MemoryError``\ s. Each run but the last writes a JUnit file of its own beside ``junit.xml``, which ``Upload test timings`` takes too, and the step diff --git a/tests/project/test_pypy_worker_memory_2005.py b/tests/project/test_pypy_worker_memory_2005.py index 32614a7f73..cb76f87682 100644 --- a/tests/project/test_pypy_worker_memory_2005.py +++ b/tests/project/test_pypy_worker_memory_2005.py @@ -9,6 +9,10 @@ own script with a stub ``python``, as :mod:`tests.project.test_sweeps_on_latest_only_1732` does. +#2600: run whole, ``tests/protocols`` outgrew the runner too, so its +``internet``, ``application`` and ``misc`` subpackages are shards of their own +ahead of it, and it takes the remainder. + """ from __future__ import annotations @@ -33,8 +37,9 @@ #: The ``test`` job's KTLO twin, which runs its steps by YAML alias. KTLO = ('unit-tests.yml', 'test-ktlo') -#: The directories a PyPy leg runs on their own, in order. -SHARDS = ['tests/protocols', 'tests/corekit'] +#: The directories a PyPy leg runs on their own, in order; #2600 split ``tests/protocols``. +SHARDS = ['tests/protocols/internet', 'tests/protocols/application', 'tests/protocols/misc', 'tests/protocols', + 'tests/corekit'] #: The page that says how the legs run. DOC = ROOT / 'docs' / 'source' / 'contributing' / 'workflows.rst' @@ -61,10 +66,13 @@ def test_the_ktlo_twin_runs_the_same_step(self) -> None: self.assertEqual(job(*KTLO)['steps'], job(*JOB)['steps']) def test_each_shard_is_a_directory_the_tier_keeps(self) -> None: - for shard in SHARDS: + """#2600: and each keeps unit-tier modules no earlier shard took, so its run collects a test.""" + for index, shard in enumerate(SHARDS): with self.subTest(shard=shard): self.assertTrue((ROOT / shard).is_dir()) - self.assertTrue([module for module in _tiers.all_tier_paths() if module.startswith(shard + '/')]) + earlier = tuple(taken + '/' for taken in SHARDS[:index]) + self.assertTrue([module for module in _tiers.all_tier_paths() if module.startswith(shard + '/') + and not module.startswith(earlier) and _tiers.is_unit_tier(module)]) @unittest.skipIf(shutil.which('bash') is None, 'the step is a bash script')