v1.0.1 - corrige le demarrage et ajoute le build Windows
L'application ne demarrait pas du tout : gui/app.py importait pyqtSignal, qui est le nom PyQt du signal, absent de PySide6. Trois autres defauts bloquaient juste derriere, une fois celui-ci leve : - MPLBACKEND etait fixe a 'module://backend_interagg', le backend interne de PyCharm, indisponible hors de cet IDE ; - gui/app.py et monitor/monitor.py utilisaient des imports relatifs remontant au-dela du paquet racine charge par main.py ; - core/ n'avait pas de __init__.py. Corrige aussi get_statistics(), dont le retour anticipe "historique vide" omettait connection_loss_count et uptime_percentage : le KeyError qui en resultait cassait silencieusement tout l'onglet Statistiques a chaque rafraichissement. Les deux chemins renvoient desormais les memes cles. Ajoute le build Windows (StabilityTester.spec + make build-windows), qui produit un .exe autonome depuis Linux via Wine. PySide6 y est epingle a 6.9.3 : a partir de 6.10, Qt6Core.dll depend de l'ICU systeme de Windows que Wine n'implemente pas. La sortie standard est reconfiguree en UTF-8 dans main.py, les emojis des messages faisant echouer cp1252 sous Windows. make test etait casse par la meme confusion de paquet et passe a nouveau. Makefile : libgl1-mesa-glx, retire des depots Debian/Ubuntu recents, remplace par libgl1 et les dependances Qt6 manquantes.
This commit is contained in:
@@ -0,0 +1,429 @@
|
||||
"""
|
||||
Module de test réseau - Effectue divers tests de connectivité et de performance
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import requests
|
||||
import socket
|
||||
import json
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
# Import optionnel de speedtest
|
||||
try:
|
||||
import speedtest
|
||||
SPEEDTEST_AVAILABLE = True
|
||||
except ImportError:
|
||||
SPEEDTEST_AVAILABLE = False
|
||||
speedtest = None
|
||||
|
||||
# Import optionnel de ping3
|
||||
try:
|
||||
import ping3
|
||||
PING3_AVAILABLE = True
|
||||
except ImportError:
|
||||
PING3_AVAILABLE = False
|
||||
ping3 = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class TestResult:
|
||||
"""Résultat d'un test réseau"""
|
||||
timestamp: datetime
|
||||
test_type: str
|
||||
success: bool
|
||||
latency_ms: Optional[float] = None
|
||||
download_speed: Optional[float] = None # en Mbps
|
||||
upload_speed: Optional[float] = None # en Mbps
|
||||
packet_loss: Optional[float] = None # en %
|
||||
jitter: Optional[float] = None # en ms
|
||||
error_message: Optional[str] = None
|
||||
server: Optional[str] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
'timestamp': self.timestamp.isoformat(),
|
||||
'test_type': self.test_type,
|
||||
'success': self.success,
|
||||
'latency_ms': self.latency_ms,
|
||||
'download_speed': self.download_speed,
|
||||
'upload_speed': self.upload_speed,
|
||||
'packet_loss': self.packet_loss,
|
||||
'jitter': self.jitter,
|
||||
'error_message': self.error_message,
|
||||
'server': self.server
|
||||
}
|
||||
|
||||
|
||||
class NetworkTester:
|
||||
"""
|
||||
Classe pour effectuer divers tests de connectivité et de performance réseau
|
||||
"""
|
||||
|
||||
# Serveurs de test par défaut
|
||||
PING_SERVERS = [
|
||||
"8.8.8.8", # Google DNS
|
||||
"1.1.1.1", # Cloudflare DNS
|
||||
"9.9.9.9", # Quad9 DNS
|
||||
"8.8.4.4", # Google DNS secondaire
|
||||
"208.67.222.222" # OpenDNS
|
||||
]
|
||||
|
||||
SPEEDTEST_SERVERS = [
|
||||
{"name": "Paris", "id": 12345}, # ID à adapter selon votre région
|
||||
{"name": "Lyon", "id": 67890},
|
||||
]
|
||||
|
||||
# URL pour tester le téléchargement
|
||||
DOWNLOAD_TEST_URL = "https://speedtest.ftp.otenet.gr/files/test1Mb.db"
|
||||
UPLOAD_TEST_URL = "https://httpbin.org/post"
|
||||
|
||||
def __init__(self, timeout: int = 5):
|
||||
"""
|
||||
Initialisation du testeur réseau
|
||||
|
||||
Args:
|
||||
timeout: Timeout en secondes pour les tests
|
||||
"""
|
||||
self.timeout = timeout
|
||||
self.last_test_time = None
|
||||
|
||||
def ping_test(self, server: str = None, count: int = 4) -> TestResult:
|
||||
"""
|
||||
Effectue un test de ping vers un serveur
|
||||
|
||||
Args:
|
||||
server: Adresse IP ou hostname à pinger (par défaut, utilise le premier serveur)
|
||||
count: Nombre de paquets à envoyer
|
||||
|
||||
Returns:
|
||||
TestResult: Résultat du test
|
||||
"""
|
||||
if server is None:
|
||||
server = self.PING_SERVERS[0]
|
||||
|
||||
if not PING3_AVAILABLE:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="ping",
|
||||
success=False,
|
||||
error_message="ping3 non installé. Installez avec: pip install ping3"
|
||||
)
|
||||
|
||||
try:
|
||||
# Utilisation de ping3 pour plus de fiabilité
|
||||
start_time = time.time()
|
||||
|
||||
# Effectuer le ping
|
||||
latency_ms = ping3.ping(server, unit='ms', timeout=self.timeout)
|
||||
|
||||
if latency_ms is None or latency_ms is False:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="ping",
|
||||
success=False,
|
||||
error_message=f"Échec du ping vers {server}",
|
||||
server=server
|
||||
)
|
||||
|
||||
# Calcul du jitter (variation de latence)
|
||||
latencies = []
|
||||
for _ in range(count):
|
||||
lat = ping3.ping(server, unit='ms', timeout=self.timeout)
|
||||
if lat is not None and lat is not False:
|
||||
latencies.append(lat)
|
||||
|
||||
jitter = max(latencies) - min(latencies) if latencies else 0
|
||||
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="ping",
|
||||
success=True,
|
||||
latency_ms=latency_ms,
|
||||
jitter=jitter,
|
||||
server=server
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="ping",
|
||||
success=False,
|
||||
error_message=f"Erreur lors du ping: {str(e)}",
|
||||
server=server
|
||||
)
|
||||
|
||||
def ping_all_servers(self) -> list:
|
||||
"""
|
||||
Ping tous les serveurs configurés
|
||||
|
||||
Returns:
|
||||
Liste de TestResult pour chaque serveur
|
||||
"""
|
||||
results = []
|
||||
for server in self.PING_SERVERS:
|
||||
result = self.ping_test(server, count=2)
|
||||
results.append(result)
|
||||
time.sleep(0.5) # Petite pause entre les tests
|
||||
return results
|
||||
|
||||
def connection_test(self, url: str = "https://www.google.com") -> TestResult:
|
||||
"""
|
||||
Teste la connectivité HTTP vers une URL
|
||||
|
||||
Args:
|
||||
url: URL à tester
|
||||
|
||||
Returns:
|
||||
TestResult: Résultat du test
|
||||
"""
|
||||
try:
|
||||
start_time = time.time()
|
||||
response = requests.get(url, timeout=self.timeout)
|
||||
latency = (time.time() - start_time) * 1000 # en ms
|
||||
|
||||
if response.status_code < 400:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="http_connection",
|
||||
success=True,
|
||||
latency_ms=latency,
|
||||
server=url
|
||||
)
|
||||
else:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="http_connection",
|
||||
success=False,
|
||||
error_message=f"Code HTTP: {response.status_code}",
|
||||
server=url
|
||||
)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="http_connection",
|
||||
success=False,
|
||||
error_message=f"Erreur de connexion: {str(e)}",
|
||||
server=url
|
||||
)
|
||||
|
||||
def dns_test(self, domain: str = "google.com") -> TestResult:
|
||||
"""
|
||||
Teste la résolution DNS
|
||||
|
||||
Args:
|
||||
domain: Domaine à résoudre
|
||||
|
||||
Returns:
|
||||
TestResult: Résultat du test
|
||||
"""
|
||||
try:
|
||||
start_time = time.time()
|
||||
# Résolution DNS
|
||||
ip_addresses = socket.getaddrinfo(domain, None)
|
||||
latency = (time.time() - start_time) * 1000
|
||||
|
||||
if ip_addresses:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="dns_resolution",
|
||||
success=True,
|
||||
latency_ms=latency,
|
||||
server=domain
|
||||
)
|
||||
else:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="dns_resolution",
|
||||
success=False,
|
||||
error_message="Aucune adresse IP trouvée",
|
||||
server=domain
|
||||
)
|
||||
|
||||
except socket.gaierror as e:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="dns_resolution",
|
||||
success=False,
|
||||
error_message=f"Erreur DNS: {str(e)}",
|
||||
server=domain
|
||||
)
|
||||
|
||||
def download_speed_test(self, url: str = None, duration: int = 5) -> TestResult:
|
||||
"""
|
||||
Teste la vitesse de téléchargement
|
||||
|
||||
Args:
|
||||
url: URL du fichier à télécharger
|
||||
duration: Durée du test en secondes
|
||||
|
||||
Returns:
|
||||
TestResult: Résultat du test
|
||||
"""
|
||||
if url is None:
|
||||
url = self.DOWNLOAD_TEST_URL
|
||||
|
||||
try:
|
||||
start_time = time.time()
|
||||
response = requests.get(url, stream=True, timeout=self.timeout)
|
||||
|
||||
# Lire les données par blocs pour mesurer la vitesse
|
||||
bytes_received = 0
|
||||
chunk_size = 1024 * 1024 # 1 Mo
|
||||
|
||||
while time.time() - start_time < duration:
|
||||
chunk = response.raw.read(chunk_size)
|
||||
if not chunk:
|
||||
break
|
||||
bytes_received += len(chunk)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
speed_mbps = (bytes_received * 8) / (1024 * 1024) / elapsed if elapsed > 0 else 0
|
||||
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="download_speed",
|
||||
success=True,
|
||||
download_speed=round(speed_mbps, 2),
|
||||
server=url
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="download_speed",
|
||||
success=False,
|
||||
error_message=f"Erreur de téléchargement: {str(e)}",
|
||||
server=url
|
||||
)
|
||||
|
||||
def upload_speed_test(self, url: str = None, data_size: int = 1024 * 1024) -> TestResult:
|
||||
"""
|
||||
Teste la vitesse d'upload
|
||||
|
||||
Args:
|
||||
url: URL pour le test d'upload
|
||||
data_size: Taille des données à uploader en octets
|
||||
|
||||
Returns:
|
||||
TestResult: Résultat du test
|
||||
"""
|
||||
if url is None:
|
||||
url = self.UPLOAD_TEST_URL
|
||||
|
||||
try:
|
||||
# Créer des données aléatoires
|
||||
data = b'0' * data_size
|
||||
|
||||
start_time = time.time()
|
||||
response = requests.post(url, data=data, timeout=self.timeout * 2)
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
speed_mbps = (data_size * 8) / (1024 * 1024) / elapsed if elapsed > 0 else 0
|
||||
|
||||
if response.status_code < 400:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="upload_speed",
|
||||
success=True,
|
||||
upload_speed=round(speed_mbps, 2),
|
||||
server=url
|
||||
)
|
||||
else:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="upload_speed",
|
||||
success=False,
|
||||
error_message=f"Code HTTP: {response.status_code}",
|
||||
server=url
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="upload_speed",
|
||||
success=False,
|
||||
error_message=f"Erreur d'upload: {str(e)}",
|
||||
server=url
|
||||
)
|
||||
|
||||
def full_speed_test(self) -> TestResult:
|
||||
"""
|
||||
Effectue un test complet de vitesse (download + upload) avec speedtest-cli
|
||||
|
||||
Returns:
|
||||
TestResult: Résultat du test
|
||||
"""
|
||||
if not SPEEDTEST_AVAILABLE:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="full_speed_test",
|
||||
success=False,
|
||||
error_message="speedtest-cli non installé. Installez avec: pip install speedtest-cli"
|
||||
)
|
||||
|
||||
try:
|
||||
st = speedtest.Speedtest()
|
||||
|
||||
# Sélection du meilleur serveur
|
||||
st.get_best_server()
|
||||
|
||||
# Test de téléchargement
|
||||
st.download()
|
||||
download_speed = st.results.dict()["download"] / 1024 / 1024 # en Mbps
|
||||
|
||||
# Test d'upload
|
||||
st.upload()
|
||||
upload_speed = st.results.dict()["upload"] / 1024 / 1024 # en Mbps
|
||||
|
||||
# Latence (ping)
|
||||
latency = st.results.dict()["ping"]
|
||||
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="full_speed_test",
|
||||
success=True,
|
||||
latency_ms=latency,
|
||||
download_speed=round(download_speed, 2),
|
||||
upload_speed=round(upload_speed, 2),
|
||||
server=st.results.dict()["server"]["name"]
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return TestResult(
|
||||
timestamp=datetime.now(),
|
||||
test_type="full_speed_test",
|
||||
success=False,
|
||||
error_message=f"Erreur lors du speedtest: {str(e)}"
|
||||
)
|
||||
|
||||
def comprehensive_test(self) -> Dict[str, TestResult]:
|
||||
"""
|
||||
Effectue une série complète de tests réseau
|
||||
|
||||
Returns:
|
||||
Dictionnaire avec les résultats de tous les tests
|
||||
"""
|
||||
results = {}
|
||||
|
||||
# Test de connectivité
|
||||
results['connection'] = self.connection_test()
|
||||
|
||||
# Test DNS
|
||||
results['dns'] = self.dns_test()
|
||||
|
||||
# Tests de ping
|
||||
results['ping_google'] = self.ping_test("8.8.8.8")
|
||||
results['ping_cloudflare'] = self.ping_test("1.1.1.1")
|
||||
|
||||
# Test de vitesse de téléchargement (rapide)
|
||||
results['download'] = self.download_speed_test(duration=3)
|
||||
|
||||
# Test de vitesse d'upload (rapide)
|
||||
results['upload'] = self.upload_speed_test(data_size=512 * 1024)
|
||||
|
||||
return results
|
||||
Reference in New Issue
Block a user