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:
2026-09-11 08:12:33 +02:00
parent b47d67299c
commit 4adde135d7
16 changed files with 2967 additions and 0 deletions
View File
+429
View File
@@ -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