Run the core tests from the gate and finish the layout rename
The gate now runs pytest as well, so one command answers whether the code base is broken: syntax, protocol imports, the 125 core tests, then the lab functional suites. It fails when a test fails, verified by feeding it a deliberately broken test. Two leftovers from moving the labs into experiments/ are fixed: the lab command still pointed at tests/, and the hook only watched protocol and tests, so edits under experiments/ and tools/ triggered nothing. Profiling the 3.6 s gate: 0.24 s syntax, 0.12 s imports, 0.61 s pytest, 2.7 s lab functional suites, of which Lab042's software loopback is 1.14 s because it pushes a whole image through the chain. The pytest addition is the small part. prepare_jpeg is now cached, since six checks call it and each call re-encoded the image at several qualities. README described the gate without the tests it now runs.
This commit is contained in:
@@ -12,7 +12,7 @@ argument-hint: <номер лабы> [краткая постановка]
|
|||||||
|
|
||||||
Запусти агента `Explore` с запросом: что в `protocol/` уже относится к этой задаче,
|
Запусти агента `Explore` с запросом: что в `protocol/` уже относится к этой задаче,
|
||||||
какие модули придётся трогать, где есть пересечение или дублирование, как устроены
|
какие модули придётся трогать, где есть пересечение или дублирование, как устроены
|
||||||
две-три ближайшие по смыслу лабы в `tests/`.
|
две-три ближайшие по смыслу лабы в `experiments/`.
|
||||||
|
|
||||||
Прочитай сам последние записи в `PROJECT_LOG.md` — там указано, чем должен
|
Прочитай сам последние записи в `PROJECT_LOG.md` — там указано, чем должен
|
||||||
заниматься следующий этап.
|
заниматься следующий этап.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||||
WATCHED = re.compile(r"[\\/](?:protocol|tests)[\\/][^\\/]+\.py$")
|
WATCHED = re.compile(r"[\\/](?:protocol|experiments|tests|tools)[\\/][^\\/]+\.py$")
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ python -m pip install -r requirements-hardware.txt
|
|||||||
python -m pytest tests/ -q
|
python -m pytest tests/ -q
|
||||||
```
|
```
|
||||||
|
|
||||||
**Дешёвый шлюз** — синтаксис, импорт `protocol`, функциональные проверки лабораторных:
|
**Дешёвый шлюз** — синтаксис, импорт `protocol`, быстрые проверки ядра и функциональные проверки лабораторных. Отвечает на один вопрос: не сломана ли кодовая база прямо сейчас:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python tools/quick_gate.py
|
python tools/quick_gate.py
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import csv
|
import csv
|
||||||
|
import functools
|
||||||
import io
|
import io
|
||||||
import time
|
import time
|
||||||
from dataclasses import asdict, dataclass, field
|
from dataclasses import asdict, dataclass, field
|
||||||
@@ -193,6 +194,7 @@ class FunctionalTestResult:
|
|||||||
# ==================================================================== подготовка
|
# ==================================================================== подготовка
|
||||||
|
|
||||||
|
|
||||||
|
@functools.lru_cache(maxsize=4)
|
||||||
def prepare_jpeg(
|
def prepare_jpeg(
|
||||||
source_path: Path = SOURCE_IMAGE,
|
source_path: Path = SOURCE_IMAGE,
|
||||||
width: int = IMAGE_WIDTH,
|
width: int = IMAGE_WIDTH,
|
||||||
@@ -207,6 +209,10 @@ def prepare_jpeg(
|
|||||||
заранее принятым значением, и возвращается вместе с байтами.
|
заранее принятым значением, и возвращается вместе с байтами.
|
||||||
|
|
||||||
Возвращает готовые байты JPEG в градациях серого и применённое качество.
|
Возвращает готовые байты JPEG в градациях серого и применённое качество.
|
||||||
|
|
||||||
|
Результат кэшируется: функция вызывается шестью функциональными
|
||||||
|
проверками подряд, а каждый вызов заново кодирует изображение в
|
||||||
|
нескольких вариантах качества.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not source_path.exists():
|
if not source_path.exists():
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
Проверяет за одну-две секунды то, что ломается чаще всего:
|
Проверяет за одну-две секунды то, что ломается чаще всего:
|
||||||
|
|
||||||
1. Синтаксис всех файлов в protocol, tests и tools.
|
1. Синтаксис всех файлов в protocol, tests, experiments и tools.
|
||||||
2. Импорт каждого модуля protocol.
|
2. Импорт каждого модуля protocol.
|
||||||
3. Функциональные проверки тех лаб, которым не нужны данные эксперимента.
|
3. Быстрые проверки ядра из tests под pytest.
|
||||||
|
4. Функциональные проверки тех лаб, которым не нужны данные эксперимента.
|
||||||
|
|
||||||
Полный прогон лабы шлюз не заменяет.
|
Полный прогон лабы шлюз не заменяет.
|
||||||
Он отвечает на один вопрос: не сломана ли кодовая база прямо сейчас.
|
Он отвечает на один вопрос: не сломана ли кодовая база прямо сейчас.
|
||||||
@@ -19,7 +20,10 @@ from __future__ import annotations
|
|||||||
import ast
|
import ast
|
||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
@@ -62,6 +66,41 @@ def check_protocol_imports() -> list[str]:
|
|||||||
return failures
|
return failures
|
||||||
|
|
||||||
|
|
||||||
|
def check_core_tests() -> tuple[list[str], int]:
|
||||||
|
"""
|
||||||
|
Прогнать быстрые проверки ядра из tests под pytest.
|
||||||
|
|
||||||
|
Возвращает:
|
||||||
|
failures:
|
||||||
|
Список сообщений об ошибках.
|
||||||
|
|
||||||
|
passed:
|
||||||
|
Количество пройденных проверок, ноль если разобрать вывод не удалось.
|
||||||
|
"""
|
||||||
|
|
||||||
|
environment = dict(os.environ, PYTHONIOENCODING="utf-8")
|
||||||
|
|
||||||
|
completed = subprocess.run(
|
||||||
|
[sys.executable, "-m", "pytest", "tests", "-q"],
|
||||||
|
cwd=PROJECT_ROOT,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
|
env=environment,
|
||||||
|
)
|
||||||
|
|
||||||
|
output = (completed.stdout + completed.stderr).strip()
|
||||||
|
|
||||||
|
if completed.returncode != 0:
|
||||||
|
return [f"pytest: тесты ядра не пройдены:\n{output}"], 0
|
||||||
|
|
||||||
|
match = re.search(r"(\d+) passed", output)
|
||||||
|
passed = int(match.group(1)) if match else 0
|
||||||
|
|
||||||
|
return [], passed
|
||||||
|
|
||||||
|
|
||||||
def check_functional_tests() -> tuple[list[str], int, int]:
|
def check_functional_tests() -> tuple[list[str], int, int]:
|
||||||
"""
|
"""
|
||||||
Выполнить функциональные проверки лаб, не требующие данных эксперимента.
|
Выполнить функциональные проверки лаб, не требующие данных эксперимента.
|
||||||
@@ -128,8 +167,9 @@ def main() -> int:
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
import_failures = check_protocol_imports()
|
import_failures = check_protocol_imports()
|
||||||
|
core_failures, passed = check_core_tests()
|
||||||
test_failures, executed, skipped = check_functional_tests()
|
test_failures, executed, skipped = check_functional_tests()
|
||||||
failures = import_failures + test_failures
|
failures = import_failures + core_failures + test_failures
|
||||||
|
|
||||||
if failures:
|
if failures:
|
||||||
print("Шлюз не пройден")
|
print("Шлюз не пройден")
|
||||||
@@ -137,7 +177,11 @@ def main() -> int:
|
|||||||
print(f" {failure}")
|
print(f" {failure}")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
print(f"Шлюз пройден: синтаксис в порядке, protocol импортируется, функциональных проверок выполнено {executed}, пропущено {skipped}")
|
print(
|
||||||
|
"Шлюз пройден: синтаксис в порядке, protocol импортируется, "
|
||||||
|
f"тестов ядра пройдено {passed}, "
|
||||||
|
f"функциональных проверок лаб выполнено {executed}, пропущено {skipped}"
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user