Files
ServerStatus/clients/test_client_metrics.py
T
2026-08-20 14:09:26 +08:00

154 lines
7.0 KiB
Python

import runpy
import sys
import threading
import time
import types
import unittest
from collections import namedtuple
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from unittest import mock
CLIENT_DIR = Path(__file__).resolve().parent
def load_client(filename):
if filename == "client-psutil.py" and "psutil" not in sys.modules:
try:
__import__("psutil")
except ImportError:
sys.modules["psutil"] = types.ModuleType("psutil")
return runpy.run_path(str(CLIENT_DIR / filename))
class ClientMetricTests(unittest.TestCase):
def test_psutil_counter_reads_are_serialized_and_keep_nowrap_enabled(self):
client = load_client("client-psutil.py")
active = 0
maximum_active = 0
calls = []
state_lock = threading.Lock()
def fake_counters(*args, **kwargs):
nonlocal active, maximum_active
with state_lock:
active += 1
maximum_active = max(maximum_active, active)
calls.append((args, kwargs))
time.sleep(0.002)
with state_lock:
active -= 1
return {}
with mock.patch.object(client["psutil"], "net_io_counters", side_effect=fake_counters, create=True):
with ThreadPoolExecutor(max_workers=12) as executor:
list(executor.map(lambda _index: client["_get_net_io_counters"](), range(48)))
self.assertEqual(maximum_active, 1)
self.assertEqual(len(calls), 48)
self.assertTrue(all(kwargs == {"pernic": True} for _args, kwargs in calls))
def test_psutil_totals_exclude_virtual_interfaces(self):
client = load_client("client-psutil.py")
counters = namedtuple("Counters", "bytes_sent bytes_recv")
values = {
"eth0": counters(500, 1000),
"ens5": counters(300, 700),
"wlo1": counters(200, 400),
"lo": counters(9000, 9000),
"docker0": counters(8000, 8000),
"veth123": counters(7000, 7000),
}
with mock.patch.object(client["psutil"], "net_io_counters", return_value=values, create=True):
self.assertEqual(client["liuliang"](), (2100, 1000))
def test_linux_totals_read_proc_and_exclude_virtual_interfaces(self):
client = load_client("client-linux.py")
proc_net_dev = """Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
eth0: 1000 10 0 0 0 0 0 0 500 5 0 0 0 0 0 0
ens5: 700 7 0 0 0 0 0 0 300 3 0 0 0 0 0 0
wlo1: 400 4 0 0 0 0 0 0 200 2 0 0 0 0 0 0
lo: 9000 9 0 0 0 0 0 0 9000 9 0 0 0 0 0 0
veth123: 8000 8 0 0 0 0 0 0 8000 8 0 0 0 0 0 0
"""
with mock.patch("builtins.open", mock.mock_open(read_data=proc_net_dev)):
self.assertEqual(client["liuliang"](), (2100, 1000))
def test_interface_filter_keeps_wlo_and_excludes_virtual_interfaces(self):
ignored = (
"lo", "lo0", "Loopback Pseudo-Interface 1", "tun0", "docker0",
"veth123", "br-test", "vmbr0", "vnet0", "kube-ipvs0",
)
included = ("wlo1", "eth0", "enp3s0", "bond0")
for filename in ("client-linux.py", "client-psutil.py"):
with self.subTest(client=filename):
client = load_client(filename)
predicate = client["is_ignored_network_interface"]
self.assertTrue(all(predicate(name) for name in ignored))
self.assertTrue(all(not predicate(name) for name in included))
def test_linux_totals_keep_one_way_interfaces(self):
client = load_client("client-linux.py")
proc_net_dev = """Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
eth0: 1000 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ens5: 0 0 0 0 0 0 0 0 300 3 0 0 0 0 0 0
"""
with mock.patch("builtins.open", mock.mock_open(read_data=proc_net_dev)):
self.assertEqual(client["liuliang"](), (1000, 300))
def test_network_speed_starts_and_resets_at_zero(self):
for filename in ("client-linux.py", "client-psutil.py"):
with self.subTest(client=filename):
client = load_client(filename)
state = client["update_net_speed"].__globals__["netSpeed"]
state.update({"clock": 0.0, "diff": 0.0, "avgrx": 0, "avgtx": 0, "netrx": 0, "nettx": 0})
self.assertEqual(client["update_net_speed"](1000, 1000, 100.0), (0, 0))
self.assertEqual(client["update_net_speed"](1400, 1300, 102.0), (200, 150))
self.assertEqual(client["update_net_speed"](10, 1500, 103.0), (0, 200))
self.assertEqual(client["update_net_speed"](5, 4, 104.0), (0, 0))
def test_os_detection_uses_linux_distribution_id(self):
os_release = 'NAME="Alpine Linux"\nID=alpine\nVERSION_ID=3.22\n'
for filename in ("client-linux.py", "client-psutil.py"):
with self.subTest(client=filename):
client = load_client(filename)
with mock.patch.object(client["platform"], "system", return_value="Linux"), \
mock.patch("builtins.open", mock.mock_open(read_data=os_release)):
self.assertEqual(client["get_os_name"](), "alpine")
def test_os_detection_has_platform_fallbacks(self):
psutil_client = load_client("client-psutil.py")
with mock.patch.object(psutil_client["platform"], "system", return_value="Windows Server 2022"):
self.assertEqual(psutil_client["get_os_name"](), "windows")
linux_client = load_client("client-linux.py")
with mock.patch.object(linux_client["platform"], "system", return_value="FreeBSD"):
self.assertEqual(linux_client["get_os_name"](), "freebsd")
def test_cpu_model_prefers_specific_model_and_has_vendor_fallback(self):
linux_client = load_client("client-linux.py")
linux_globals = linux_client["get_cpu_model"].__globals__
with mock.patch.dict(linux_globals, {
"get_cpuinfo_values": lambda: {"model name": "AMD EPYC 7B13"},
"get_lscpu_info": lambda: {"vendor id": "AuthenticAMD", "architecture": "x86_64"},
}):
self.assertEqual(linux_client["get_cpu_model"](), "AMD EPYC 7B13")
psutil_client = load_client("client-psutil.py")
platform_module = psutil_client["platform"]
uname = types.SimpleNamespace(processor="", machine="x86_64")
with mock.patch.object(platform_module, "processor", return_value=""), \
mock.patch.object(platform_module, "uname", return_value=uname), \
mock.patch.object(platform_module, "machine", return_value="x86_64"), \
mock.patch.object(platform_module, "platform", return_value="Linux GenuineIntel"):
self.assertEqual(psutil_client["get_cpu_model"](), "GenuineIntel")
if __name__ == "__main__":
unittest.main()