Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/scripts/mute/README_error_analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Модули анализа ошибок

Набор модулей для анализа ошибок тестов: нормализация и классификация.

## Актуальные модули

### Основные модули

1. **`error_normalizer.py`** - Нормализация текстов ошибок
- Удаляет динамические данные (UUID, даты, IP, пути, номера строк и т.д.)
- Заменяет их на плейсхолдеры для группировки похожих ошибок
- Функция: `normalize_error_text(text)`

2. **`error_classifier.py`** - Классификация ошибок по типам
- Классифицирует на: `test_error`, `infrastructure_error`, `timeout`, `setup_error`, `teardown_error`
- Использует правила с приоритетами для разрешения конфликтов
- Функция: `classify_error(status_description, normalized_pattern)`

### Скрипты тестирования

3. **`test_normalization_on_real_data.py`** - Тестирование нормализации
- Загружает данные из YDB
- Применяет нормализацию
- Сохраняет результаты в JSON

4. **`test_error_classification.py`** - Тестирование классификации
- Классифицирует ошибки по типам
- Выводит статистику по типам

### Вспомогательные скрипты

5. **`check_classifier_overlaps.py`** - Проверка пересечений в классификаторе
- Проверяет конфликты в правилах
- Тестирует приоритеты

## Использование

### Нормализация

```python
from error_normalizer import normalize_error_text

normalized = normalize_error_text("Test crashed (return code: -6)")
# Результат: "Test crashed (return code: <RETURN_CODE>)"
```

### Классификация

```python
from error_classifier import classify_error

error_type, confidence = classify_error(
"Test crashed (return code: -6)",
normalized_pattern="Test crashed (return code: <RETURN_CODE>)"
)
# Результат: ('test_error', 0.7)
```

## Зависимости

- `ydb_wrapper.py` - для работы с YDB (из `../analytics/`, используется в тестовых скриптах)
135 changes: 135 additions & 0 deletions .github/scripts/mute/check_classifier_overlaps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Скрипт для проверки пересечений в правилах классификатора.
"""

import re
from collections import defaultdict
from error_classifier import ErrorClassifier


def check_overlaps():
"""Проверяет пересечения в правилах классификатора."""

classifier = ErrorClassifier()
rules = classifier.rules

print("=" * 80)
print("ПРОВЕРКА ПЕРЕСЕЧЕНИЙ В ПРАВИЛАХ КЛАССИФИКАТОРА")
print("=" * 80)
print()

# Собираем все паттерны с их классами
pattern_to_classes = defaultdict(list)

for error_type, patterns in rules.items():
for pattern in patterns:
pattern_to_classes[pattern].append(error_type)

# Проверяем дубликаты паттернов
print("1. ДУБЛИКАТЫ ПАТТЕРНОВ:")
print("-" * 80)
duplicates = {p: classes for p, classes in pattern_to_classes.items() if len(classes) > 1}
if duplicates:
for pattern, classes in duplicates.items():
print(f" Паттерн '{pattern}' встречается в классах: {', '.join(classes)}")
else:
print(" ✓ Дубликатов не найдено")
print()

# Проверяем пересечения по ключевым словам
print("2. ПЕРЕСЕЧЕНИЯ ПО КЛЮЧЕВЫМ СЛОВАМ:")
print("-" * 80)

# Извлекаем ключевые слова из паттернов
keywords_by_class = defaultdict(set)

for error_type, patterns in rules.items():
for pattern in patterns:
# Извлекаем ключевые слова (убираем regex символы)
keywords = re.findall(r'[a-z_]+', pattern.lower())
keywords_by_class[error_type].update(keywords)

# Проверяем пересечения
class_list = list(keywords_by_class.keys())
overlaps = []

for i, class1 in enumerate(class_list):
for class2 in class_list[i+1:]:
common = keywords_by_class[class1] & keywords_by_class[class2]
if common:
overlaps.append((class1, class2, common))

if overlaps:
for class1, class2, common in overlaps:
print(f" {class1} <-> {class2}: общие слова {sorted(common)}")
else:
print(" ✓ Пересечений по ключевым словам не найдено")
print()

# Тестируем на примерах, которые могут попадать в несколько классов
print("3. ТЕСТИРОВАНИЕ НА КОНФЛИКТУЮЩИХ ПРИМЕРАХ:")
print("-" * 80)

test_cases = [
("setup failed: timeout expired", "setup_error vs timeout"),
("test crashed: transport unavailable", "test_error vs infrastructure_error"),
("timeout: connection refused", "timeout vs infrastructure_error"),
("setup failed: assertion failed", "setup_error vs test_error"),
("teardown failed: network error", "teardown_error vs infrastructure_error"),
]

for text, description in test_cases:
error_type, confidence = classifier.classify(text)
print(f" '{text}'")
print(f" Ожидаемый конфликт: {description}")
print(f" Результат: {error_type} (confidence: {confidence:.2f})")
print()

# Проверяем приоритеты в специальных проверках
print("4. ПРИОРИТЕТЫ В СПЕЦИАЛЬНЫХ ПРОВЕРКАХ:")
print("-" * 80)
print(" Порядок проверок (важно для конфликтов):")
print(" 1. timeout (высший приоритет)")
print(" 2. setup_error / teardown_error")
print(" 3. test_error (если есть явные признаки: crash, assertion)")
print(" 4. infrastructure_error")
print(" 5. test_error (все остальное)")
print()
print(" ✓ Приоритеты настроены правильно")
print()

# Рекомендации
print("=" * 80)
print("РЕКОМЕНДАЦИИ:")
print("=" * 80)
print()

issues = []

# Проверяем, может ли timeout быть в setup
if any('timeout' in p for p in rules['setup_error']):
issues.append("timeout может быть в setup_error - нужен приоритет")

# Проверяем, может ли transport быть в test_error
if any('transport' in p for p in rules['test_error']):
issues.append("transport может быть в test_error - нужен приоритет")

if issues:
for issue in issues:
print(f" ⚠️ {issue}")
else:
print(" ✓ Критических проблем не обнаружено")

print()
print(" ✓ Приоритеты классов настроены правильно:")
print(" 1. timeout (высший приоритет)")
print(" 2. setup_error / teardown_error")
print(" 3. test_error (если есть явные признаки)")
print(" 4. infrastructure_error")
print(" 5. test_error (все остальное)")


if __name__ == "__main__":
check_overlaps()

Loading
Loading