- C1: trace только на повторе 0 при числе повторов > 1 (lib/trace.ts shouldTrace) в scroll и scroll-coauthor: admin-контроль, control4x и PERF_S2_REPEATS=1 больше не теряют единственную запись (traced=1). - I1: масштаб 2 - PERF_LOAD_TIMEOUT_MS=40 мин и PERF_TEST_TIMEOUT_MS=90 мин, если не заданы. - I2: шаг aggregate падает при сбое perfkit.aggregate/копирования и если summary.json не создан; тесты run-all (perf/tests/test_run_all.py). - M2: после modals_profiling останавливается preview :8081. - README, design.md: решения раунда. Refs #202 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
3.8 KiB
Python
89 lines
3.8 KiB
Python
"""run-all.sh: сухой режим и шаг aggregate (стенд не нужен: aggregate работает только с файлами)."""
|
||
import json
|
||
import os
|
||
import shutil
|
||
import subprocess
|
||
import uuid
|
||
|
||
import pytest
|
||
|
||
from perfkit import PERF_DIR, REPORT_DATA_DIR, RESULTS_DIR
|
||
|
||
SCRIPT = PERF_DIR / "run-all.sh"
|
||
|
||
|
||
def _run(**env):
|
||
e = {k: v for k, v in os.environ.items() if not k.startswith("PERF_")}
|
||
e.update(env)
|
||
return subprocess.run(["bash", str(SCRIPT)], env=e, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL)
|
||
|
||
|
||
def _step_line(out: str, scale: int, step: str) -> str:
|
||
block = out.split(f"=== масштаб {scale},", 1)[1].split("ИТОГО масштаб", 1)[0]
|
||
return next(l for l in block.splitlines() if l.strip().startswith(step + " "))
|
||
|
||
|
||
def test_dry_run_plan_and_s2_load_timeout():
|
||
p = _run(PERF_DRY_RUN="1", PERF_RUN_TAG="t")
|
||
assert p.returncode == 0, p.stderr
|
||
assert "ИТОГО масштаб 1" in p.stdout and "ИТОГО масштаб 2" in p.stdout
|
||
# свод 2 на масштабе 2 (~150-190 МБ): ожидание загрузки 40 мин, если не задано пользователем
|
||
assert "PERF_LOAD_TIMEOUT_MS=2400000" in _step_line(p.stdout, 2, "scroll")
|
||
assert "PERF_LOAD_TIMEOUT_MS" not in _step_line(p.stdout, 1, "scroll")
|
||
p2 = _run(PERF_DRY_RUN="1", PERF_RUN_TAG="t", PERF_SCALES="2", PERF_LOAD_TIMEOUT_MS="123")
|
||
assert "PERF_LOAD_TIMEOUT_MS=123" in _step_line(p2.stdout, 2, "scroll")
|
||
|
||
|
||
def test_dry_run_modals_profiling_stops_web_profiling():
|
||
p = _run(PERF_DRY_RUN="1", PERF_RUN_TAG="t", PERF_SCALES="1", PERF_STEPS="modals_profiling")
|
||
line = _step_line(p.stdout, 1, "modals_profiling")
|
||
assert "web-profiling.pid" in line and "kill" in line
|
||
|
||
|
||
@pytest.fixture
|
||
def run_id():
|
||
rid = f"pytest-runall-{uuid.uuid4().hex[:8]}"
|
||
yield rid
|
||
shutil.rmtree(RESULTS_DIR / rid, ignore_errors=True)
|
||
shutil.rmtree(REPORT_DATA_DIR / rid, ignore_errors=True)
|
||
|
||
|
||
def _write(rid: str, text: str):
|
||
d = RESULTS_DIR / rid
|
||
d.mkdir(parents=True)
|
||
(d / "scroll.jsonl").write_text(text, encoding="utf-8")
|
||
|
||
|
||
def _status(rid: str) -> dict:
|
||
return json.loads((RESULTS_DIR / rid / "run-all.status.json").read_text(encoding="utf-8"))["steps"]
|
||
|
||
|
||
def test_aggregate_step_fails_when_aggregate_fails(run_id):
|
||
_write(run_id, "не json\n")
|
||
(REPORT_DATA_DIR / run_id).mkdir(parents=True) # возобновление: каталог report-data уже есть от прошлого aggregate
|
||
p = _run(PERF_SCALES="1", PERF_SKIP_RESET="1", PERF_STEPS="aggregate", PERF_RUN_ID_S1=run_id)
|
||
assert p.returncode == 1, p.stdout
|
||
assert _status(run_id)["aggregate"]["status"] == "FAIL"
|
||
|
||
|
||
def test_aggregate_step_ok_and_summary_in_report_data(run_id):
|
||
rec = {"run_id": run_id, "scenario": "scroll", "target": "x@y", "role": "rf", "scale": 1, "cpu_rate": 1, "repeat": 1,
|
||
"metrics": {"main_thread_ms": 10.0}, "artifacts": [], "incomplete": False, "ts": "x"}
|
||
_write(run_id, json.dumps(rec) + "\n")
|
||
p = _run(PERF_SCALES="1", PERF_SKIP_RESET="1", PERF_STEPS="aggregate", PERF_RUN_ID_S1=run_id)
|
||
assert p.returncode == 0, p.stdout
|
||
assert _status(run_id)["aggregate"]["status"] == "OK"
|
||
assert (REPORT_DATA_DIR / run_id / "summary.json").exists()
|
||
|
||
|
||
def test_aggregate_step_fails_without_summary(run_id, monkeypatch):
|
||
"""Если aggregate не создал summary.json (например, отработал «пусто» не туда), шаг - FAIL."""
|
||
_write(run_id, "")
|
||
(REPORT_DATA_DIR / run_id).mkdir(parents=True)
|
||
fake = RESULTS_DIR / run_id / "fakepy"
|
||
fake.write_text("#!/bin/sh\nexit 0\n")
|
||
fake.chmod(0o755)
|
||
p = _run(PERF_SCALES="1", PERF_SKIP_RESET="1", PERF_STEPS="aggregate", PERF_RUN_ID_S1=run_id, PERF_PY=str(fake))
|
||
assert p.returncode == 1, p.stdout
|
||
assert _status(run_id)["aggregate"]["status"] == "FAIL"
|