Files
2026-07-17 23:36:00 +03:00

160 lines
5.1 KiB
Python

"""Parse Allure test-case JSON and metric attachments."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from benchmark_config import BenchmarkConfig, ChartTest
def attachment_path(benchmark_dir: Path, source: str) -> Path:
path = benchmark_dir / 'attachments' / source
if not path.exists():
path = benchmark_dir / 'data' / 'attachments' / source
return path
def parse_metric_attachment(attachment_file: Path, metric_keyword: str) -> Dict:
if not attachment_file.exists():
return {}
keyword = metric_keyword.lower()
try:
run_values = []
avg_value = None
for line in attachment_file.read_text(encoding='utf-8').split('\n'):
line_lower = line.lower()
if keyword not in line_lower:
continue
parts = line.split(':')
if len(parts) <= 1:
continue
try:
value = float(parts[1].strip().split()[0])
except ValueError:
continue
if 'average' in line_lower:
avg_value = value
else:
run_values.append(value)
if not run_values:
return {}
return {
'min_value': min(run_values),
'max_value': max(run_values),
'avg_value': avg_value if avg_value is not None else sum(run_values) / len(run_values),
'run_count': len(run_values),
'all_runs': ','.join(map(str, run_values)),
}
except Exception as error:
print(f'Warning: Failed to parse {metric_keyword} attachment {attachment_file}: {error}')
return {}
def find_attachment_source(test_data: Dict, keyword: str) -> Optional[str]:
keyword = keyword.lower()
def find_in_stage(stage: Dict) -> Optional[str]:
for attachment in stage.get('attachments', []):
if keyword in attachment.get('name', '').lower():
source = attachment.get('source', '')
if source:
return source
for step in stage.get('steps', []):
source = find_in_stage(step)
if source:
return source
return None
return find_in_stage(test_data.get('testStage', {}))
def _load_time_row(test_name: str, status: str, metric_data: Dict) -> Dict:
return {
'test_name': test_name,
'status': status,
'min_time': metric_data['min_value'],
'max_time': metric_data['max_value'],
'avg_time': metric_data['avg_value'],
'run_count': metric_data['run_count'],
'all_runs': metric_data['all_runs'],
}
def _resource_row(chart: ChartTest, test_name: str, status: str, metric_data: Dict) -> Dict:
return {
'test_name': test_name,
'status': status,
'metric_id': chart.test_id,
**metric_data,
}
def _attachment_keyword_for_test(chart: ChartTest, test_name: str) -> str:
for index, pattern in enumerate(chart.historical_patterns):
if pattern not in test_name:
continue
if index < len(chart.historical_attachment_keywords):
return chart.historical_attachment_keywords[index]
break
return chart.attachment_keyword
def parse_test_case_json(
json_file: Path,
benchmark_dir: Path,
config: BenchmarkConfig,
) -> Tuple[Dict, List[Dict], List[Dict], List[Dict]]:
data = json.loads(json_file.read_text(encoding='utf-8'))
test_result = {
'test_name': data.get('name', ''),
'status': data.get('status', 'unknown'),
'duration_ms': data.get('time', {}).get('duration', 0),
'retries_count': data.get('retriesCount', 0),
'flaky': data.get('flaky', False),
}
test_name = test_result['test_name']
performance_results: List[Dict] = []
cpu_results: List[Dict] = []
ram_results: List[Dict] = []
for chart in config.charts:
patterns = (
chart.source_pattern or chart.pattern,
*chart.historical_patterns,
)
if not any(pattern in test_name for pattern in patterns):
continue
attachment_keyword = _attachment_keyword_for_test(chart, test_name)
attachment_source = find_attachment_source(data, attachment_keyword)
if not attachment_source:
continue
metric_data = parse_metric_attachment(
attachment_path(benchmark_dir, attachment_source),
attachment_keyword,
)
if not metric_data:
continue
if chart.metrics_kind == 'performance':
performance_results.append(
_load_time_row(chart.pattern, test_result['status'], metric_data)
)
elif chart.metrics_kind == 'cpu':
cpu_results.append(
_resource_row(chart, chart.pattern, test_result['status'], metric_data)
)
else:
ram_results.append(
_resource_row(chart, chart.pattern, test_result['status'], metric_data)
)
return test_result, performance_results, cpu_results, ram_results