From 657015852ad9a85d2bfc01ec800856be71ce135b Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Sat, 24 Oct 2026 00:06:52 +0200 Subject: [PATCH] test(web-app-stalwart): decide the IMAP parallelism instead of racing it `test_parallel_sessions_append_every_message_once` asserted that more than one of four sessions had appended something. ThreadPoolExecutor promises a thread count, never that every worker takes an item from a shared iterator, so a loaded host where one thread drains all 40 messages before its siblings start fails the assertion with nothing broken. CI did exactly that in run 40023932276; 25 local runs pinned to a single CPU did not. The sessions now meet at a barrier before their first append. A pool that runs them concurrently releases all four, and a serialised one reaches the timeout and leaves the later sessions empty, which the assertion still catches: with max_workers forced to 1 the test fails with the same "1 not greater than 1" CI reported. Co-Authored-By: Claude Opus 5 (1M context) --- .../files/test_migrate_from_mailu.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/unit/python/roles/web-app-stalwart/files/test_migrate_from_mailu.py b/tests/unit/python/roles/web-app-stalwart/files/test_migrate_from_mailu.py index 2e7888e70e..ba062ea1c0 100644 --- a/tests/unit/python/roles/web-app-stalwart/files/test_migrate_from_mailu.py +++ b/tests/unit/python/roles/web-app-stalwart/files/test_migrate_from_mailu.py @@ -1,7 +1,9 @@ +import contextlib import importlib.util import os import sys import tempfile +import threading import time import unittest from pathlib import Path @@ -74,7 +76,23 @@ class _FakeImap: class _SlowImap(_FakeImap): + """A session that waits for its siblings before it appends anything. + + `ThreadPoolExecutor` promises a thread count, never that every worker + receives an item from a shared iterator, so counting the sessions that + happened to append is a race rather than a check. + """ + + def __init__(self, barrier=None, **kwargs): + super().__init__(**kwargs) + self._barrier = barrier + self._waited = False + def append(self, mailbox, flags, date_time, message): + if self._barrier is not None and not self._waited: + self._waited = True + with contextlib.suppress(threading.BrokenBarrierError): + self._barrier.wait(timeout=10) time.sleep(0.002) return super().append(mailbox, flags, date_time, message) @@ -141,7 +159,8 @@ class TestMigrateAccount(unittest.TestCase): (self.account / "cur" / "99.host:2,").write_bytes( b"Message-ID: <5@x>\r\n\r\nduplicate" ) - imaps = [_SlowImap() for _ in range(4)] + barrier = threading.Barrier(4) + imaps = [_SlowImap(barrier=barrier) for _ in range(4)] stats = self.m.AccountStats() self.m.migrate_account(imaps, self.account, stats) appended = [entry for imap in imaps for entry in imap.appended]