mirror of
https://github.com/simonw/datasette.git
synced 2026-10-21 08:47:01 +02:00
check_resources() listed /proc/self/fd, which only exists on Linux, so on macOS every scenario crashed at the end with FileNotFoundError. macOS now lists /dev/fd and resolves each descriptor with fcntl F_GETPATH, which, like /proc, still reports files that have been deleted (psutil's open_files() leaves those out). The temp directory is compared via realpath(), since macOS reports /var/folders as /private/var/folders. The test now fails with the harness's stderr when it exits without a summary line, instead of an IndexError that hid the real traceback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
75 lines
2.4 KiB
Python
75 lines
2.4 KiB
Python
"""
|
|
A fast, fixed-seed subset of the connection torture harness
|
|
(tests/connection_torture.py). Each scenario runs in its own process, so a
|
|
segfault or a hang fails one test instead of killing the test run.
|
|
|
|
Several event loops (threads) drive one Datasette with extreme settings
|
|
(smallest pool, 1ms idle timeout, 10ms schema polling) through reads,
|
|
writes of every kind, DDL, isolated functions, analyze_sql, cancellation,
|
|
add/remove_database, scratch databases, external schema changes, files
|
|
replaced and deleted, Database.close() and Datasette.close() mid-flight,
|
|
and non-threaded mode. See the harness docstring for what is checked.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
HARNESS = os.path.join(os.path.dirname(__file__), "connection_torture.py")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"scenario,seed",
|
|
[
|
|
("mixed", 101),
|
|
("lifecycle", 102),
|
|
("close", 103),
|
|
("cancel", 104),
|
|
("pool", 105),
|
|
("nothreads", 106),
|
|
("writes", 107),
|
|
],
|
|
)
|
|
def test_connection_torture(scenario, seed):
|
|
try:
|
|
proc = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
HARNESS,
|
|
scenario,
|
|
"--seed",
|
|
str(seed),
|
|
"--duration",
|
|
"1.5",
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=150,
|
|
check=False,
|
|
)
|
|
except subprocess.TimeoutExpired as e: # pragma: no cover
|
|
pytest.fail(f"{scenario} hung:\n{(e.stderr or '')[-4000:]}")
|
|
if proc.returncode < 0: # pragma: no cover
|
|
pytest.fail(
|
|
f"{scenario} crashed with {signal.Signals(-proc.returncode).name}:\n"
|
|
f"{proc.stderr[-4000:]}"
|
|
)
|
|
lines = proc.stdout.strip().splitlines()
|
|
if not lines:
|
|
pytest.fail(
|
|
f"{scenario} exited {proc.returncode} without a summary:\n"
|
|
f"{proc.stderr[-4000:]}"
|
|
)
|
|
summary = json.loads(lines[-1])
|
|
problems = "\n".join(
|
|
f"[{p['kind']}] {p['message'][:600]}" for p in summary["problems"]
|
|
)
|
|
assert proc.returncode == 0, f"{scenario} seed {seed}:\n{problems}"
|
|
assert summary["ok"]
|
|
# It did something (the slow-query scenarios do a few dozen operations
|
|
# in 1.5s on a busy machine, the others hundreds)
|
|
assert sum(summary["counts"].values()) > 10
|