642 lines
26 KiB
Python
642 lines
26 KiB
Python
"""
|
|
Расширенный тест с мониторингом использования ресурсов.
|
|
Отслеживает CPU, RAM, диск и сеть во время выполнения.
|
|
"""
|
|
|
|
import asyncio
|
|
import sys
|
|
import random
|
|
import time
|
|
import json
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from typing import Dict, List, Optional
|
|
from dataclasses import dataclass, field
|
|
from contextlib import asynccontextmanager
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
import psutil
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.dates as mdates
|
|
from config.settings import settings
|
|
from core.logger import setup_logger
|
|
from managers.proxy_manager import ProxyManager
|
|
from services.browser_service import BrowserService
|
|
|
|
logger = setup_logger(
|
|
name="test_resources",
|
|
log_level="DEBUG",
|
|
log_file="logs/test_resources.log"
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class ResourceSnapshot:
|
|
"""Снимок состояния ресурсов в момент времени."""
|
|
timestamp: float
|
|
cpu_percent: float
|
|
memory_percent: float
|
|
memory_used_mb: float
|
|
memory_available_mb: float
|
|
process_memory_mb: float
|
|
process_cpu_percent: float
|
|
disk_read_mb: float
|
|
disk_write_mb: float
|
|
net_sent_mb: float
|
|
net_recv_mb: float
|
|
open_files: int
|
|
threads: int
|
|
|
|
|
|
@dataclass
|
|
class TestPhase:
|
|
"""Фаза теста с метриками."""
|
|
name: str
|
|
start_time: float
|
|
end_time: float = 0
|
|
snapshots: List[ResourceSnapshot] = field(default_factory=list)
|
|
|
|
@property
|
|
def duration(self) -> float:
|
|
return self.end_time - self.start_time if self.end_time else 0
|
|
|
|
@property
|
|
def avg_cpu(self) -> float:
|
|
if not self.snapshots:
|
|
return 0
|
|
return sum(s.cpu_percent for s in self.snapshots) / len(self.snapshots)
|
|
|
|
@property
|
|
def avg_memory_mb(self) -> float:
|
|
if not self.snapshots:
|
|
return 0
|
|
return sum(s.process_memory_mb for s in self.snapshots) / len(self.snapshots)
|
|
|
|
@property
|
|
def peak_memory_mb(self) -> float:
|
|
if not self.snapshots:
|
|
return 0
|
|
return max(s.process_memory_mb for s in self.snapshots)
|
|
|
|
|
|
class ResourceMonitor:
|
|
"""
|
|
Монитор ресурсов системы.
|
|
Собирает метрики CPU, RAM, диска и сети.
|
|
"""
|
|
|
|
def __init__(self, interval: float = 0.5):
|
|
"""
|
|
Args:
|
|
interval: Интервал сбора метрик в секундах
|
|
"""
|
|
self.interval = interval
|
|
self.process = psutil.Process()
|
|
self._monitoring = False
|
|
self._task: Optional[asyncio.Task] = None
|
|
self._snapshots: List[ResourceSnapshot] = []
|
|
self._last_disk = psutil.disk_io_counters()
|
|
self._last_net = psutil.net_io_counters()
|
|
self._start_time = time.time()
|
|
|
|
# Получаем начальные значения для расчета дельты
|
|
self._baseline_memory = self.process.memory_info().rss / (1024 * 1024)
|
|
logger.info(f"📊 Базовая память процесса: {self._baseline_memory:.1f} MB")
|
|
|
|
async def start(self):
|
|
"""Запуск мониторинга."""
|
|
self._monitoring = True
|
|
self._task = asyncio.create_task(self._monitor_loop())
|
|
logger.info("📊 Мониторинг ресурсов запущен")
|
|
|
|
async def stop(self) -> List[ResourceSnapshot]:
|
|
"""Остановка мониторинга."""
|
|
self._monitoring = False
|
|
if self._task:
|
|
self._task.cancel()
|
|
try:
|
|
await self._task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
|
|
logger.info(f"📊 Мониторинг остановлен. Собрано снимков: {len(self._snapshots)}")
|
|
return self._snapshots
|
|
|
|
async def _monitor_loop(self):
|
|
"""Цикл сбора метрик."""
|
|
while self._monitoring:
|
|
try:
|
|
snapshot = await self._collect_snapshot()
|
|
self._snapshots.append(snapshot)
|
|
await asyncio.sleep(self.interval)
|
|
except asyncio.CancelledError:
|
|
break
|
|
except Exception as e:
|
|
logger.error(f"Ошибка сбора метрик: {e}")
|
|
await asyncio.sleep(1)
|
|
|
|
async def _collect_snapshot(self) -> ResourceSnapshot:
|
|
"""Сбор одного снимка метрик."""
|
|
# CPU
|
|
cpu_percent = psutil.cpu_percent(interval=None)
|
|
process_cpu = self.process.cpu_percent(interval=None)
|
|
|
|
# Memory
|
|
memory = psutil.virtual_memory()
|
|
process_mem = self.process.memory_info()
|
|
|
|
# Disk (delta)
|
|
current_disk = psutil.disk_io_counters()
|
|
disk_read = (current_disk.read_bytes - self._last_disk.read_bytes) / (1024 * 1024)
|
|
disk_write = (current_disk.write_bytes - self._last_disk.write_bytes) / (1024 * 1024)
|
|
self._last_disk = current_disk
|
|
|
|
# Network (delta)
|
|
current_net = psutil.net_io_counters()
|
|
net_sent = (current_net.bytes_sent - self._last_net.bytes_sent) / (1024 * 1024)
|
|
net_recv = (current_net.bytes_recv - self._last_net.bytes_recv) / (1024 * 1024)
|
|
self._last_net = current_net
|
|
|
|
return ResourceSnapshot(
|
|
timestamp=time.time() - self._start_time,
|
|
cpu_percent=cpu_percent,
|
|
memory_percent=memory.percent,
|
|
memory_used_mb=memory.used / (1024 * 1024),
|
|
memory_available_mb=memory.available / (1024 * 1024),
|
|
process_memory_mb=process_mem.rss / (1024 * 1024),
|
|
process_cpu_percent=process_cpu,
|
|
disk_read_mb=disk_read,
|
|
disk_write_mb=disk_write,
|
|
net_sent_mb=net_sent,
|
|
net_recv_mb=net_recv,
|
|
open_files=len(self.process.open_files()),
|
|
threads=self.process.num_threads()
|
|
)
|
|
|
|
|
|
class ResourceTestRunner:
|
|
"""
|
|
Тест с отслеживанием ресурсов.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.monitor = ResourceMonitor(interval=0.3) # Каждые 300мс
|
|
self.proxy_manager: Optional[ProxyManager] = None
|
|
self.browser_service: Optional[BrowserService] = None
|
|
self.phases: List[TestPhase] = []
|
|
|
|
async def setup(self):
|
|
"""Инициализация."""
|
|
self.proxy_manager = ProxyManager()
|
|
self.browser_service = BrowserService(self.proxy_manager)
|
|
|
|
# Вывод информации о системе
|
|
self._print_system_info()
|
|
|
|
def _print_system_info(self):
|
|
"""Вывод информации о системе."""
|
|
logger.info("="*60)
|
|
logger.info("💻 ИНФОРМАЦИЯ О СИСТЕМЕ")
|
|
logger.info("="*60)
|
|
|
|
cpu_count = psutil.cpu_count()
|
|
memory = psutil.virtual_memory()
|
|
disk = psutil.disk_usage('/')
|
|
|
|
logger.info(f"🖥️ CPU:")
|
|
logger.info(f" • Ядер: {cpu_count}")
|
|
logger.info(f" • Частота: {psutil.cpu_freq().current:.0f} MHz")
|
|
|
|
logger.info(f"🧠 Память:")
|
|
logger.info(f" • Всего: {memory.total / (1024**3):.1f} GB")
|
|
logger.info(f" • Доступно: {memory.available / (1024**3):.1f} GB")
|
|
|
|
logger.info(f"💾 Диск:")
|
|
logger.info(f" • Всего: {disk.total / (1024**3):.1f} GB")
|
|
logger.info(f" • Свободно: {disk.free / (1024**3):.1f} GB")
|
|
|
|
@asynccontextmanager
|
|
async def _measure_phase(self, phase_name: str):
|
|
"""Контекстный менеджер для измерения фазы."""
|
|
phase = TestPhase(name=phase_name, start_time=time.time())
|
|
|
|
# Запускаем мониторинг если не запущен
|
|
was_monitoring = self.monitor._monitoring
|
|
if not was_monitoring:
|
|
await self.monitor.start()
|
|
|
|
try:
|
|
yield phase
|
|
finally:
|
|
if not was_monitoring:
|
|
phase.snapshots = await self.monitor.stop()
|
|
phase.end_time = time.time()
|
|
|
|
# Логируем статистику фазы
|
|
logger.info(f"\n📊 Фаза: {phase_name}")
|
|
logger.info(f" • Длительность: {phase.duration:.1f}s")
|
|
logger.info(f" • Средний CPU: {phase.avg_cpu:.1f}%")
|
|
logger.info(f" • Средняя память: {phase.avg_memory_mb:.1f} MB")
|
|
logger.info(f" • Пиковая память: {phase.peak_memory_mb:.1f} MB")
|
|
|
|
self.phases.append(phase)
|
|
|
|
async def test_initialization(self):
|
|
"""Тест инициализации."""
|
|
async with self._measure_phase("Инициализация") as phase:
|
|
logger.info("🔧 Тест инициализации компонентов...")
|
|
|
|
# Создаем браузер
|
|
browser = None
|
|
try:
|
|
from camoufox.async_api import AsyncCamoufox
|
|
browser = await AsyncCamoufox(headless=True).__aenter__()
|
|
await asyncio.sleep(1) # Ждем полной инициализации
|
|
except Exception as e:
|
|
logger.error(f"Ошибка: {e}")
|
|
finally:
|
|
if browser:
|
|
await browser.__aexit__(None, None, None)
|
|
|
|
await asyncio.sleep(2) # Ждем стабилизации метрик
|
|
|
|
async def test_single_visit(self, url: str, reading_time: int):
|
|
"""Тест одного посещения."""
|
|
async with self._measure_phase(f"Посещение {url}") as phase:
|
|
logger.info(f"🌐 Посещение: {url}")
|
|
logger.info(f" Время чтения: {reading_time}s")
|
|
|
|
result = await self.browser_service.visit_page(url, reading_time)
|
|
|
|
if result.success:
|
|
logger.info(f" ✅ Успешно")
|
|
else:
|
|
logger.error(f" ❌ Ошибка: {result.error}")
|
|
|
|
await asyncio.sleep(2) # Ждем освобождения ресурсов
|
|
|
|
async def test_sequential_visits(self, count: int):
|
|
"""Тест последовательных посещений."""
|
|
async with self._measure_phase(f"{count} последовательных посещений") as phase:
|
|
test_urls = [
|
|
"https://httpbin.org/headers",
|
|
"https://httpbin.org/ip",
|
|
"https://httpbin.org/user-agent",
|
|
"https://www.example.com",
|
|
]
|
|
|
|
for i in range(count):
|
|
url = test_urls[i % len(test_urls)]
|
|
reading = random.randint(5, 15)
|
|
|
|
logger.info(f"📍 Посещение {i+1}/{count}: {url}")
|
|
|
|
try:
|
|
result = await self.browser_service.visit_page(url, reading)
|
|
status = "✅" if result.success else "❌"
|
|
logger.info(f" {status} Память: {self._get_current_memory():.1f} MB")
|
|
except Exception as e:
|
|
logger.error(f" ❌ Ошибка: {e}")
|
|
|
|
# Пауза между посещениями
|
|
if i < count - 1:
|
|
delay = random.randint(3, 8)
|
|
logger.info(f" ⏳ Пауза {delay}s")
|
|
await asyncio.sleep(delay)
|
|
|
|
async def test_concurrent_browsers(self, count: int):
|
|
"""Тест параллельных браузеров."""
|
|
async with self._measure_phase(f"{count} параллельных браузеров") as phase:
|
|
logger.info(f"🌐 Запуск {count} браузеров одновременно...")
|
|
|
|
async def open_browser(idx):
|
|
try:
|
|
url = f"https://httpbin.org/headers"
|
|
result = await self.browser_service.visit_page(url, 10)
|
|
logger.info(f" Браузер {idx}: {'✅' if result.success else '❌'} "
|
|
f"(память: {self._get_current_memory():.1f} MB)")
|
|
except Exception as e:
|
|
logger.error(f" Браузер {idx}: ❌ {e}")
|
|
|
|
# Запускаем параллельно
|
|
tasks = [open_browser(i) for i in range(count)]
|
|
await asyncio.gather(*tasks)
|
|
|
|
await asyncio.sleep(3) # Ждем освобождения
|
|
|
|
def _get_current_memory(self) -> float:
|
|
"""Текущее использование памяти процессом в MB."""
|
|
return psutil.Process().memory_info().rss / (1024 * 1024)
|
|
|
|
async def run_comprehensive_test(self, target_url: str = None):
|
|
"""Запуск комплексного теста."""
|
|
logger.info("\n" + "🚀"*30)
|
|
logger.info("КОМПЛЕКСНЫЙ ТЕСТ С МОНИТОРИНГОМ РЕСУРСОВ")
|
|
logger.info("🚀"*30 + "\n")
|
|
|
|
await self.setup()
|
|
|
|
# Фаза 1: Инициализация
|
|
await self.test_initialization()
|
|
|
|
# Фаза 2: Одиночное посещение
|
|
url = target_url or "https://httpbin.org/headers"
|
|
await self.test_single_visit(url, 10)
|
|
|
|
# Фаза 3: Последовательные посещения
|
|
await self.test_sequential_visits(5)
|
|
|
|
# Фаза 4: Параллельные браузеры (осторожно!)
|
|
logger.warning("⚠️ Тест параллельных браузеров (может использовать много памяти)")
|
|
await self.test_concurrent_browsers(3)
|
|
|
|
# Вывод результатов
|
|
self._print_results()
|
|
self._generate_charts()
|
|
self._save_results_json()
|
|
|
|
def _print_results(self):
|
|
"""Вывод результатов теста."""
|
|
logger.info("\n" + "="*60)
|
|
logger.info("📊 ИТОГОВЫЕ РЕЗУЛЬТАТЫ")
|
|
logger.info("="*60)
|
|
|
|
for phase in self.phases:
|
|
logger.info(f"\n📈 {phase.name}:")
|
|
logger.info(f" • Длительность: {phase.duration:.1f}s")
|
|
logger.info(f" • Средний CPU: {phase.avg_cpu:.1f}%")
|
|
logger.info(f" • Средняя RAM: {phase.avg_memory_mb:.1f} MB")
|
|
logger.info(f" • Пиковая RAM: {phase.peak_memory_mb:.1f} MB")
|
|
logger.info(f" • Снимков: {len(phase.snapshots)}")
|
|
|
|
def _generate_charts(self):
|
|
"""Генерация графиков использования ресурсов."""
|
|
logger.info("\n📊 Генерация графиков...")
|
|
|
|
try:
|
|
# Собираем все снимки
|
|
all_snapshots = []
|
|
phase_boundaries = []
|
|
current_time = 0
|
|
|
|
for phase in self.phases:
|
|
for snapshot in phase.snapshots:
|
|
snapshot.timestamp += current_time
|
|
all_snapshots.append(snapshot)
|
|
|
|
if phase.snapshots:
|
|
current_time = phase.snapshots[-1].timestamp
|
|
phase_boundaries.append(current_time)
|
|
|
|
if not all_snapshots:
|
|
logger.warning("Нет данных для графиков")
|
|
return
|
|
|
|
# Создаем графики
|
|
fig, axes = plt.subplots(3, 2, figsize=(15, 12))
|
|
fig.suptitle('Использование ресурсов во время теста', fontsize=14, fontweight='bold')
|
|
|
|
timestamps = [s.timestamp for s in all_snapshots]
|
|
|
|
# CPU
|
|
ax = axes[0, 0]
|
|
ax.plot(timestamps, [s.cpu_percent for s in all_snapshots],
|
|
color='blue', alpha=0.7, linewidth=1)
|
|
ax.fill_between(timestamps, [s.cpu_percent for s in all_snapshots],
|
|
alpha=0.2, color='blue')
|
|
ax.set_title('CPU Usage (%)')
|
|
ax.set_ylabel('%')
|
|
ax.grid(True, alpha=0.3)
|
|
self._add_phase_lines(ax, phase_boundaries)
|
|
|
|
# Process CPU
|
|
ax = axes[0, 1]
|
|
ax.plot(timestamps, [s.process_cpu_percent for s in all_snapshots],
|
|
color='orange', alpha=0.7, linewidth=1)
|
|
ax.set_title('Process CPU Usage (%)')
|
|
ax.set_ylabel('%')
|
|
ax.grid(True, alpha=0.3)
|
|
self._add_phase_lines(ax, phase_boundaries)
|
|
|
|
# Memory
|
|
ax = axes[1, 0]
|
|
ax.plot(timestamps, [s.process_memory_mb for s in all_snapshots],
|
|
color='green', alpha=0.7, linewidth=1)
|
|
ax.fill_between(timestamps, [s.process_memory_mb for s in all_snapshots],
|
|
alpha=0.2, color='green')
|
|
ax.set_title('Process Memory (MB)')
|
|
ax.set_ylabel('MB')
|
|
ax.grid(True, alpha=0.3)
|
|
self._add_phase_lines(ax, phase_boundaries)
|
|
|
|
# Peak memory annotation
|
|
peak_idx = max(range(len(all_snapshots)),
|
|
key=lambda i: all_snapshots[i].process_memory_mb)
|
|
peak_mem = all_snapshots[peak_idx].process_memory_mb
|
|
ax.annotate(f'Peak: {peak_mem:.1f} MB',
|
|
xy=(timestamps[peak_idx], peak_mem),
|
|
xytext=(10, 10), textcoords='offset points',
|
|
bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.7),
|
|
arrowprops=dict(arrowstyle='->'))
|
|
|
|
# System Memory
|
|
ax = axes[1, 1]
|
|
ax.plot(timestamps, [s.memory_percent for s in all_snapshots],
|
|
color='red', alpha=0.7, linewidth=1)
|
|
ax.set_title('System Memory Usage (%)')
|
|
ax.set_ylabel('%')
|
|
ax.grid(True, alpha=0.3)
|
|
self._add_phase_lines(ax, phase_boundaries)
|
|
|
|
# Network
|
|
ax = axes[2, 0]
|
|
ax.plot(timestamps, [s.net_recv_mb for s in all_snapshots],
|
|
color='purple', alpha=0.7, linewidth=1, label='Received')
|
|
ax.plot(timestamps, [s.net_sent_mb for s in all_snapshots],
|
|
color='pink', alpha=0.7, linewidth=1, label='Sent')
|
|
ax.set_title('Network Traffic (MB/s)')
|
|
ax.set_ylabel('MB/s')
|
|
ax.legend()
|
|
ax.grid(True, alpha=0.3)
|
|
self._add_phase_lines(ax, phase_boundaries)
|
|
|
|
# Threads & Files
|
|
ax = axes[2, 1]
|
|
ax2 = ax.twinx()
|
|
|
|
ax.plot(timestamps, [s.threads for s in all_snapshots],
|
|
color='brown', alpha=0.7, linewidth=1, label='Threads')
|
|
ax2.plot(timestamps, [s.open_files for s in all_snapshots],
|
|
color='gray', alpha=0.7, linewidth=1, label='Open Files')
|
|
|
|
ax.set_title('Threads & Open Files')
|
|
ax.set_ylabel('Threads', color='brown')
|
|
ax2.set_ylabel('Open Files', color='gray')
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
lines1, labels1 = ax.get_legend_handles_labels()
|
|
lines2, labels2 = ax2.get_legend_handles_labels()
|
|
ax.legend(lines1 + lines2, labels1 + labels2)
|
|
|
|
self._add_phase_lines(ax, phase_boundaries)
|
|
|
|
plt.tight_layout()
|
|
|
|
# Сохраняем
|
|
chart_file = f"test_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
|
|
plt.savefig(chart_file, dpi=150, bbox_inches='tight')
|
|
plt.close()
|
|
|
|
logger.info(f"✅ Графики сохранены: {chart_file}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Ошибка создания графиков: {e}")
|
|
|
|
def _add_phase_lines(self, ax, boundaries):
|
|
"""Добавление линий фаз на график."""
|
|
for boundary in boundaries:
|
|
ax.axvline(x=boundary, color='black', linestyle='--', alpha=0.3)
|
|
|
|
def _save_results_json(self):
|
|
"""Сохранение результатов в JSON."""
|
|
try:
|
|
results = {
|
|
"test_time": datetime.now().isoformat(),
|
|
"system_info": {
|
|
"cpu_count": psutil.cpu_count(),
|
|
"total_memory_gb": psutil.virtual_memory().total / (1024**3),
|
|
},
|
|
"phases": []
|
|
}
|
|
|
|
for phase in self.phases:
|
|
phase_data = {
|
|
"name": phase.name,
|
|
"duration_seconds": phase.duration,
|
|
"avg_cpu_percent": phase.avg_cpu,
|
|
"avg_memory_mb": phase.avg_memory_mb,
|
|
"peak_memory_mb": phase.peak_memory_mb,
|
|
"snapshots_count": len(phase.snapshots),
|
|
"memory_timeline": [
|
|
{
|
|
"time": s.timestamp,
|
|
"cpu": s.cpu_percent,
|
|
"memory_mb": s.process_memory_mb
|
|
}
|
|
for s in phase.snapshots[::5] # Каждый 5-й для компактности
|
|
]
|
|
}
|
|
results["phases"].append(phase_data)
|
|
|
|
json_file = f"test_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
|
with open(json_file, 'w', encoding='utf-8') as f:
|
|
json.dump(results, f, indent=2, ensure_ascii=False)
|
|
|
|
logger.info(f"✅ JSON результаты сохранены: {json_file}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Ошибка сохранения JSON: {e}")
|
|
|
|
|
|
async def run_memory_test():
|
|
"""
|
|
Специальный тест для измерения потребления памяти.
|
|
Показывает сколько памяти используется в каждый момент.
|
|
"""
|
|
runner = ResourceTestRunner()
|
|
await runner.setup()
|
|
|
|
print("\n" + "="*60)
|
|
print("🧠 ТЕСТ ПОТРЕБЛЕНИЯ ПАМЯТИ")
|
|
print("="*60)
|
|
|
|
# Тест 1: Базовое потребление
|
|
print("\n1️⃣ Базовое потребление (без браузеров):")
|
|
for i in range(5):
|
|
mem = runner._get_current_memory()
|
|
print(f" {i+1}. {mem:.1f} MB")
|
|
await asyncio.sleep(1)
|
|
|
|
# Тест 2: С одним браузером
|
|
print("\n2️⃣ Запуск браузера и посещение:")
|
|
for i in range(3):
|
|
mem_before = runner._get_current_memory()
|
|
print(f" До посещения: {mem_before:.1f} MB")
|
|
|
|
result = await runner.browser_service.visit_page(
|
|
"https://httpbin.org/headers", 5
|
|
)
|
|
|
|
mem_after = runner._get_current_memory()
|
|
print(f" После посещения: {mem_after:.1f} MB "
|
|
f"(Δ = {mem_after - mem_before:+.1f} MB)")
|
|
|
|
await asyncio.sleep(3)
|
|
|
|
# Тест 3: Параллельные браузеры
|
|
print("\n3️⃣ Параллельные браузеры:")
|
|
for count in [1, 2, 3, 5]:
|
|
mem_before = runner._get_current_memory()
|
|
|
|
async def visit_page(idx):
|
|
return await runner.browser_service.visit_page(
|
|
"https://httpbin.org/headers", 3
|
|
)
|
|
|
|
tasks = [visit_page(i) for i in range(count)]
|
|
await asyncio.gather(*tasks)
|
|
|
|
await asyncio.sleep(2)
|
|
mem_during = runner._get_current_memory()
|
|
print(f" {count} браузеров: было {mem_before:.1f} MB, "
|
|
f"стало {mem_during:.1f} MB "
|
|
f"(Δ = {mem_during - mem_before:+.1f} MB)")
|
|
|
|
await asyncio.sleep(5) # Ждем освобождения памяти
|
|
|
|
print("\n✅ Тест памяти завершен")
|
|
|
|
|
|
async def main():
|
|
"""Точка входа."""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Тест с мониторингом ресурсов")
|
|
parser.add_argument("--url", type=str, help="URL для тестирования")
|
|
parser.add_argument("--quick", action="store_true", help="Быстрый тест")
|
|
parser.add_argument("--memory", action="store_true", help="Тест только памяти")
|
|
parser.add_argument("--full", action="store_true", help="Полный тест")
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
if args.memory:
|
|
await run_memory_test()
|
|
elif args.quick:
|
|
runner = ResourceTestRunner()
|
|
await runner.setup()
|
|
await runner.test_single_visit(
|
|
args.url or "https://httpbin.org/headers", 10
|
|
)
|
|
runner._print_results()
|
|
runner._generate_charts()
|
|
else:
|
|
# Полный тест по умолчанию
|
|
runner = ResourceTestRunner()
|
|
await runner.run_comprehensive_test(args.url)
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n⚠️ Тест прерван")
|
|
except Exception as e:
|
|
logger.error(f"Ошибка: {e}", exc_info=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("""
|
|
╔══════════════════════════════════════════════════════════════╗
|
|
║ 🧪 ТЕСТ С МОНИТОРИНГОМ РЕСУРСОВ (CPU, RAM, DISK) ║
|
|
╚══════════════════════════════════════════════════════════════╝
|
|
""")
|
|
|
|
asyncio.run(main()) |