Обнаружение непротестированного кода на основе CPG, приоритизация тестирования, рекомендации по генерации тестов, импорт покрытия и гибридный анализ.
Содержание¶
- Быстрый старт
- Как это работает
- Архитектура
- Определение намерения
- Реестр обработчиков
- Режимы обнаружения
- Эвристическое обнаружение
- Гибридное обнаружение
- Обработчики
- UntestedCodeHandler
- TestGenerationHandler
- TestPriorityHandler
- Анализ влияния
- Рекомендации на основе CPG
- Импорт покрытия
- Конфигурация
- Использование CLI
- REST API
- Сценарии использования
- Примеры вопросов
- Связанные сценарии
Быстрый старт¶
/select 07
Как это работает¶
Архитектура¶
Модуль покрытия тестами (src/workflow/scenarios/coverage_handlers/, 13 файлов) состоит из 6 компонентов:
Запрос пользователя
|
v
CoverageIntentDetector (5 типов намерений, двуязычный)
|
v
HandlerRegistry (диспетчеризация по приоритету)
|
+---> TestGenerationHandler (приоритет 5)
| генерация рекомендаций по тестам для конкретных функций
|
+---> UntestedCodeHandler (приоритет 10)
| поиск непротестированного кода (эвристика или гибрид)
|
+---> TestPriorityHandler (приоритет 20)
| ранжирование непротестированных функций по критичности
|
v
CoverageReportFormatter (двуязычный вывод в Markdown)
|
v
CallGraphAnalyzer ──> ImpactAnalysis (impact_score, вызывающие, вызываемые)
| Компонент | Модуль | Назначение |
|---|---|---|
CoverageIntentDetector |
intent_detector.py |
Определение намерения запроса среди 5 типов с морфологическим сопоставлением |
TestGenerationHandler |
handlers/test_generator.py |
Генерация рекомендаций по тестам для именованных функций |
UntestedCodeHandler |
handlers/untested.py |
Поиск непротестированного кода (эвристика + гибрид) |
TestPriorityHandler |
handlers/priority.py |
Ранжирование непротестированных функций по 4 факторам |
CoverageReportFormatter |
formatters/coverage_report.py |
Форматирование отчётов с FormatterLocalization (EN/RU) |
TestCoverageScanner |
handlers/coverage_scanner.py |
Сканирование интерфейсного покрытия (двойное: edges_call + эвристика по путям) |
Определение намерения¶
CoverageIntentDetector классифицирует запросы в 5 типов намерений с морфологическим сопоставлением и границами кириллических слов:
| Тип намерения | Приоритет | Ключевые слова EN | Ключевые слова RU |
|---|---|---|---|
test_generation |
5 | generate test, create test, write test, test suite, mutation test, stress test, property-based, chaos engineering | сгенерировать тест, создать тест, написать тест, модульный тест, стресс-тесты, мутационное тестирование |
untested_code_scan |
10 | untested, no test, missing test, not covered, uncovered | непротестированный, без теста, нет тестов, непокрытый |
test_priority |
20 | test priority, should test, critical test, high priority test | приоритет теста, протестировать, критический тест, важный тест |
coverage_gap |
30 | coverage gap, low coverage, coverage report, test coverage | пробел покрытия, низкое покрытие, отчет покрытия, покрытие тестами |
coverage_improvement |
40 | improve coverage, increase coverage, better coverage | улучшить покрытие, увеличить покрытие, повысить покрытие |
Дополнительное извлечение:
- _extract_criticality(query) — возвращает "critical", "high", "medium" или "all"
- _extract_scope(query) — возвращает "method", "class", "module" или "all"
Реестр обработчиков¶
Обработчики регистрируются через HandlerRegistry("coverage") с диспетчеризацией по приоритету. Меньший приоритет = более высокий приоритет обработки:
@coverage_registry.register(priority=5)
class TestGenerationHandlerRegistered(TestGenerationHandler): ...
@coverage_registry.register(priority=10)
class UntestedCodeHandlerRegistered(UntestedCodeHandler): ...
@coverage_registry.register(priority=20)
class TestPriorityHandlerRegistered(TestPriorityHandler): ...
Каждый обработчик реализует can_handle(query_info) -> bool и handle(query_info) -> HandlerResult. Реестр перебирает обработчики по приоритету и использует первый подходящий.
Режимы обнаружения¶
Эвристическое обнаружение¶
Режим по умолчанию, когда данные покрытия не импортированы. Методы без вызывающих test_* в edges_call помечаются как непротестированные:
-- Методы без вызовов из тестов
SELECT m.id, m.name, m.full_name
FROM nodes_method m
WHERE NOT EXISTS (
SELECT 1 FROM edges_call ec
JOIN nodes_method caller ON ec.source_id = caller.id
WHERE ec.target_id = m.id
AND caller.name LIKE 'test_%'
)
Гибридное обнаружение¶
Автоматически активируется при наличии колонки coverage_percent в nodes_method (заполняется через coverage import):
- Методы с
coverage_percent < 1.0→ помечаются по данным выполнения - Методы с
NULL coverage_percent→ откат на эвристический анализ - Каждый кандидат получает метку
detection_method:"runtime"или"heuristic" - Оценка покрытия использует
AVG(coverage_percent)из данных выполнения
Проверка _has_coverage_data() гарантирует отсутствие изменений при отсутствии импортированных данных.
Обработчики¶
UntestedCodeHandler¶
Обрабатывает намерение untested_code_scan. Ключевые методы:
| Метод | Описание |
|---|---|
can_handle(query_info) |
Возвращает True при type == "untested_code_scan" |
handle(query_info) |
Поиск непротестированного кода, классификация по критичности, генерация рекомендаций |
_find_untested_functions() |
Эвристика + необязательное обнаружение по данным покрытия |
_classify_by_criticality(candidates) |
Группировка по уровню риска (critical/high/medium/low) |
_estimate_coverage(candidates) |
Расчёт процента покрытия |
_has_coverage_data() |
Проверка наличия колонки coverage_percent |
Обогащает топ-20 кандидатов рекомендациями на основе CPG (покрытие ветвей, граничные значения параметров, пути ошибок).
TestGenerationHandler¶
Обрабатывает намерение test_generation. Ключевые методы:
| Метод | Описание |
|---|---|
can_handle(query_info) |
Возвращает True при type == "test_generation" и извлекаемых именах функций |
handle(query_info) |
Генерация рекомендаций по тестам для конкретных функций |
_extract_function_names(query) |
Извлечение имён функций через приоритетные шаблоны |
_search_functions_by_keywords(query) |
Концептуальный поиск функций (например, «тесты для выполнения запросов») |
_get_function_info(func_name) |
Регистронезависимый поиск функции |
_get_function_callees(func_name) |
Зависимости для мокирования |
_get_function_callers(func_name) |
Тестовые сценарии из вызывающих |
_suggest_test_approach(...) |
Генерация стратегии (модульные/интеграционные/граничные тесты) |
TestPriorityHandler¶
Обрабатывает намерение test_priority. Ранжирует непротестированные функции по 4 факторам:
| Фактор | Баллы | Условие |
|---|---|---|
| Критичность модуля | +3.0 | Модули api, interface, core |
| Критичность модуля | +2.0 | Модули main, engine, system |
| Сложность | +1.5 | Длина сигнатуры > 200 символов |
| Сложность | +1.0 | Длина сигнатуры > 100 символов |
| Публичный API | +1.0 | Имя без префикса подчёркивания |
| Количество вызывающих | +2.0 | Выше порога высокой конкуренции |
| Количество вызывающих | +1.0 | Выше среднего порога |
Оценка преобразуется в уровень приоритета через _score_to_rating(): high (≥ порога), medium, low.
Анализ влияния¶
Workflow использует CallGraphAnalyzer (Graph Method #2) из src/analysis/callgraph/analyzer.py для анализа влияния непротестированных методов.
Датакласс ImpactAnalysis:
| Поле | Тип | Описание |
|---|---|---|
method_name |
str | Анализируемый метод |
direct_callers |
list[str] | Методы, вызывающие напрямую |
transitive_callers |
list[str] | Все транзитивные вызывающие |
direct_callees |
list[str] | Методы, вызываемые напрямую |
transitive_callees |
list[str] | Все транзитивные вызываемые |
impact_score |
float | Оценка влияния 0.0–1.0 |
3 категории инсайтов в state["metadata"]:
| Инсайт | Условие |
|---|---|
high_impact_untested |
impact_score > thresholds.high_impact |
untested_entry_points |
Много вызывающих + мало вызываемых |
critical_untested |
callers > min_callers && impact_score > impact_score_medium |
Рекомендации на основе CPG¶
Для каждого непротестированного метода (топ-20 по критичности) обработчик генерирует конкретные рекомендации:
Анализ покрытия ветвей¶
Подсчитывает управляющие структуры (IF, FOR, WHILE, SWITCH) из nodes_control_structure и оценивает число тестовых сценариев.
Анализ граничных значений параметров¶
Сопоставляет типы параметров из nodes_param с рекомендациями по граничным значениям:
| Тип | Граничные тесты |
|---|---|
int, long, size_t, float |
zero, negative, max, min |
char*, string, str |
empty, null, very long, special chars |
Указатели (*, ptr) |
null pointer |
bool |
true, false |
| Вариативные параметры | zero args, one arg, many args |
Анализ путей ошибок¶
Подсчитывает блоки TRY в nodes_control_structure и операторы RETURN в nodes_return. Множественные return указывают на пути обработки ошибок.
Импорт покрытия¶
Импорт данных покрытия из внешних инструментов для включения гибридного обнаружения.
Поддерживаемые форматы¶
| Формат | Инструмент | Тип файла |
|---|---|---|
pytest-cov |
pytest-cov (--cov-report=json) |
JSON |
lcov |
gcov / lcov / geninfo | Текст (.info, .lcov) |
cobertura |
Cobertura, JaCoCo, coverage.py XML | XML |
Как это работает¶
- Парсер читает отчёт покрытия и извлекает построчные данные по каждому файлу
- Импортёр добавляет колонку
coverage_percentвnodes_method(если отсутствует) - Каждый метод сопоставляется с данными покрытия через суффиксное сопоставление имени файла и пересечение диапазона строк с покрытыми строками
coverage_percentвычисляется какпокрытые_строки / всего_строк * 100
Нормализация путей: Отчёты покрытия часто содержат абсолютные пути, а CPG хранит относительные. Импортёр нормализует пути (убирает ./, конвертирует обратные слэши) и использует суффиксное сопоставление. Параметр --source-root позволяет обрезать общий префикс.
Конфигурация¶
Параметры покрытия из get_unified_config():
Пороговые значения¶
| Параметр | По умолчанию | Описание |
|---|---|---|
coverage_high |
0.75 | Порог высокого покрытия |
coverage_low |
0.25 | Порог низкого покрытия |
test_coverage_minimum |
50 | Минимально требуемое покрытие % |
test_coverage_good |
80 | Хорошее покрытие % |
high_impact |
— | Мин. оценка для высоковлиятельных методов |
min_callers |
— | Мин. вызывающих для критической классификации |
impact_score_medium |
— | Порог среднего влияния |
Весовые коэффициенты¶
| Параметр | По умолчанию | Описание |
|---|---|---|
coverage_base_score |
5.0 | Базовая оценка приоритета |
coverage_critical_module |
3.0 | Бонус за критический модуль |
coverage_important_module |
2.0 | Бонус за важный модуль |
coverage_long_signature |
1.5 | Бонус за сложную сигнатуру (>200 символов) |
coverage_medium_signature |
1.0 | Бонус за среднюю сигнатуру (>100 символов) |
Лимиты обработчиков¶
| Параметр | По умолчанию | Описание |
|---|---|---|
display_items |
15 | Элементов в отчёте |
summary_items |
5 | Элементов в сводке |
query_medium |
30 | Лимит среднего запроса (анализ влияния) |
cpg_results |
50 | Лимит результатов CPG |
retrieved_functions |
25 | Функций для оценки бенчмарка |
priority_functions |
10 | Размер приоритетного списка |
Использование CLI¶
# Импорт JSON-отчёта pytest-cov
python -m src.cli.import_commands coverage import --file coverage.json --format pytest-cov --db data/projects/postgres.duckdb
# Импорт lcov trace файла
python -m src.cli.import_commands coverage import --file lcov.info --format lcov
# Импорт Cobertura XML (например, из Java/C# инструментов)
python -m src.cli.import_commands coverage import --file coverage.xml --format cobertura --source-root /project
# Просмотр импортированных данных
python -m src.cli.import_commands coverage show
python -m src.cli.import_commands coverage show --uncovered-only
REST API¶
Запросы покрытия тестами обрабатываются через сценарный роутер:
| Метод | Эндпоинт | Описание |
|---|---|---|
POST |
/api/v1/scenarios/test_coverage/query |
Выполнение анализа покрытия |
Модель запроса (ScenarioQueryRequest):
| Поле | Тип | Описание |
|---|---|---|
query |
str | Запрос на анализ (1–10000 символов) |
session_id |
str? | Идентификатор сессии |
language |
str | "en" или "ru" (по умолчанию: "en") |
Модель ответа (ScenarioQueryResponse):
| Поле | Тип | Описание |
|---|---|---|
answer |
str | Форматированный результат анализа |
scenario_id |
str | "test_coverage" |
confidence |
float | Уверенность определения намерения |
evidence |
list[dict] | Подтверждающие факты |
processing_time_ms |
float | Время обработки |
Пример:
curl -X POST http://localhost:8000/api/v1/scenarios/test_coverage/query \
-H "Content-Type: application/json" \
-d '{"query": "Find untested functions", "language": "en"}'
Сценарии использования¶
Поиск непротестированного кода¶
> Какие функции не покрыты тестами?
## Пробелы в покрытии
**Режим обнаружения:** Эвристика
**Всего непротестированных:** 234 функции
**Оценка покрытия:** 78%
### Критические (executor)
- ExecParallelHashJoinNewBatch()
- ExecReScanGather()
### Высокий приоритет (storage)
- heap_lock_updated_tuple()
- heap_abort_speculative()
### Рекомендации для `heap_lock_updated_tuple`
1. Добавьте 5 тестовых сценариев для покрытия ветвей (IF: 3, SWITCH: 1)
2. Протестируйте граничные значения параметра `flags` (zero, negative, max)
3. Протестируйте обработку ошибок: 2 try/catch блока
Приоритизация тестирования¶
> Какие критические функции нужно протестировать в первую очередь?
## Рейтинг приоритетов тестирования
| Функция | Баллы | Приоритет | Причина |
|---------|-------|-----------|---------|
| heap_lock_updated_tuple | 8.5 | high | критический модуль, 23 вызывающих, публичный API |
| ExecParallelHashJoinNewBatch | 7.0 | high | модуль engine, сложная сигнатура |
| AtEOXact_RelationCache | 5.0 | medium | системный модуль, 4 вызывающих |
Генерация тестовых случаев¶
> Сгенерировать тестовые случаи для heap_insert
## Рекомендации по тестам: heap_insert()
**Файл:** src/backend/access/heap/heapam.c:2156
**Стратегия:**
- Модульные тесты: тестировать heap_insert изолированно, мокируя 5 зависимостей
- Интеграционные тесты: тестировать через 23 вызывающие функции
- Граничные случаи: тестировать граничные условия, null-ввод, обработку ошибок
**Зависимости для мокирования:**
- RelationGetBufferForTuple()
- heap_prepare_insert()
- XLogInsert()
**Тестовые сценарии из вызывающих функций:**
- simple_heap_insert() использует heap_insert для...
- toast_save_datum() использует heap_insert для...
Гибридное обнаружение (с данными покрытия)¶
> Найти непротестированный код
## Пробелы в покрытии (Гибрид)
**Режим обнаружения:** Данные выполнения + Эвристика
**Оценка покрытия:** 62.3% (по данным выполнения)
| Метод | Обнаружение | Покрытие | Причина |
|-------|-------------|----------|---------|
| parse_query() | Данные выполнения | 0.0% | Нет покрытых строк |
| exec_plan() | Данные выполнения | 12.5% | Частичное покрытие |
| helper_func() | Эвристика | --- | Нет вызовов из тестов |
Примеры вопросов¶
Обнаружение непротестированного кода: - «Какие функции не покрыты тестами?» - «Найти непротестированный код» - «Показать функции без тестов» - «Какой код не покрыт тестами?»
Приоритизация тестирования: - «Какие критические функции нужно протестировать в первую очередь?» - «Что нужно протестировать в первую очередь?» - «Приоритет тестирования»
Генерация тестов: - «Сгенерировать тестовые случаи для heap_insert» - «Создать тесты для функции palloc» - «Какие граничные случаи следует протестировать в ExecInitNode?» - «Написать мутационные тесты для парсера запросов»
Обзор покрытия: - «Показать пробелы в покрытии» - «Отчёт покрытия» - «Как улучшить тестовое покрытие?»
Связанные сценарии¶
- Аудит безопасности (S02) — Тестирование безопасности
- Рефакторинг (S05) — Поиск неиспользуемого кода и рефакторинг
- Отладка (S15) — Отладка проблем в коде