Skip to content

Commit 91a8bd7

Browse files
authored
test(daemon): Skip the 3.12 election crash
The access violation of #838 is not specific to 3.12.4: the same crash, in the same daemon-thread read, has failed three main-branch runs in two days on the matrix's current 3.12 (3.12.10), while 3.13 and 3.14 stay clean. Skip the eight-client stress on every Windows CPython 3.12 rather than one patch release, and keep the head of a crashing frontend's stderr, which is where faulthandler prints the faulting thread and which a tail slice has now discarded twice.
1 parent 3a7de97 commit 91a8bd7

1 file changed

Lines changed: 39 additions & 4 deletions

File tree

tests/test_daemon_election.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,19 @@ def _run_frontend(profile: Path) -> dict[str, object]:
15511551
return json.loads(result.stdout.strip().splitlines()[-1])
15521552

15531553

1554+
def _both_ends(text: str, *, head: int = 3000, tail: int = 2000) -> str:
1555+
"""Keep a long diagnostic's beginning as well as its end.
1556+
1557+
A tail slice is the wrong half for a native crash: ``faulthandler`` prints
1558+
the exception and the faulting thread *first*, so #838 has twice been
1559+
reported with everything below it and the fault location cut off.
1560+
"""
1561+
if len(text) <= head + tail:
1562+
return text
1563+
dropped = len(text) - head - tail
1564+
return f"{text[:head]}\n[... {dropped} characters ...]\n{text[-tail:]}"
1565+
1566+
15541567
def _reap_frontends(frontends: list[subprocess.Popen[str]]) -> None:
15551568
"""Kill direct frontends together, then reap them without reading pipes."""
15561569
for frontend in frontends:
@@ -1640,6 +1653,28 @@ def _stop(pid: object) -> None:
16401653
os.kill(pid, signal.SIGKILL)
16411654

16421655

1656+
def test_a_native_crash_report_keeps_the_faulting_thread():
1657+
# Twice now (#838) a Windows access violation has been reported with the
1658+
# fault cut off, because the assertion kept the last 2000 characters and
1659+
# faulthandler prints the faulting thread before every other one.
1660+
crash = (
1661+
"Windows fatal exception: access violation\n\n"
1662+
"Current thread 0x00001734 (most recent call first):\n"
1663+
' File "daemon_election.py", line 706 in collect\n'
1664+
+ "".join(
1665+
f' File "threading.py", line {line} in run\n' for line in range(4000)
1666+
)
1667+
+ "the last line of the report\n"
1668+
)
1669+
1670+
kept = _both_ends(crash)
1671+
1672+
assert "Current thread 0x00001734" in kept
1673+
assert "line 706 in collect" in kept
1674+
assert kept.endswith("the last line of the report\n")
1675+
assert len(kept) < len(crash)
1676+
1677+
16431678
@pytest.mark.parametrize(("exit_code", "expected"), [(259, True), (7, False)])
16441679
def test_windows_owner_liveness_is_a_query(
16451680
exit_code: int, expected: bool, monkeypatch: pytest.MonkeyPatch
@@ -4770,10 +4805,10 @@ def track_stop(pid: object) -> None:
47704805
@pytest.mark.skipif(
47714806
os.name == "nt"
47724807
and sys.implementation.name == "cpython"
4773-
and sys.version_info[:3] == (3, 12, 4),
4808+
and sys.version_info[:2] == (3, 12),
47744809
reason=(
4775-
"CPython 3.12.4 intermittently access-violates under this "
4776-
"daemon-thread I/O stress"
4810+
"CPython 3.12 intermittently access-violates under this "
4811+
"daemon-thread I/O stress (#838)"
47774812
),
47784813
)
47794814
def test_many_clients_starting_at_once_elect_exactly_one_owner(
@@ -4833,7 +4868,7 @@ def test_many_clients_starting_at_once_elect_exactly_one_owner(
48334868

48344869
for frontend in running:
48354870
out, err = frontend.communicate(timeout=300)
4836-
assert frontend.returncode == 0, err[-2000:]
4871+
assert frontend.returncode == 0, _both_ends(err)
48374872
result = json.loads(out.strip().splitlines()[-1])
48384873
results.append(result)
48394874
owners.add(result["pid"])

0 commit comments

Comments
 (0)