初始化奇妙小屏幕控制器项目
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
from app.monitoring.resources import ResourceMonitor
|
||||
|
||||
|
||||
def write_proc_snapshot(
|
||||
proc_root,
|
||||
*,
|
||||
cpu_line: str,
|
||||
core_lines: tuple[str, ...],
|
||||
process_ticks: tuple[int, int],
|
||||
rss_pages: int = 100,
|
||||
mem_total_kib: int = 100_000,
|
||||
mem_available_kib: int = 40_000,
|
||||
) -> None:
|
||||
self_dir = proc_root / "self"
|
||||
self_dir.mkdir(parents=True, exist_ok=True)
|
||||
(proc_root / "stat").write_text(
|
||||
"\n".join((cpu_line, *core_lines, "")),
|
||||
encoding="ascii",
|
||||
)
|
||||
utime, stime = process_ticks
|
||||
process_stat = f"1 (matrix app) S 1 2 3 4 5 6 7 8 9 10 {utime} {stime} 0 0\n"
|
||||
(self_dir / "stat").write_text(process_stat, encoding="ascii")
|
||||
(self_dir / "statm").write_text(f"1000 {rss_pages} 0 0 0 0 0\n", encoding="ascii")
|
||||
(proc_root / "meminfo").write_text(
|
||||
f"MemTotal: {mem_total_kib} kB\n"
|
||||
f"MemAvailable: {mem_available_kib} kB\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
|
||||
|
||||
def test_resource_monitor_uses_delta_cpu_and_current_memory(tmp_path):
|
||||
proc_root = tmp_path / "proc"
|
||||
sampled_at = datetime(2026, 7, 21, 8, 0, tzinfo=timezone.utc)
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 100 0 100 800 0 0 0 0 0 0",
|
||||
core_lines=(
|
||||
"cpu0 25 0 25 200 0 0 0 0 0 0",
|
||||
"cpu1 25 0 25 200 0 0 0 0 0 0",
|
||||
"cpu2 25 0 25 200 0 0 0 0 0 0",
|
||||
"cpu3 25 0 25 200 0 0 0 0 0 0",
|
||||
),
|
||||
process_ticks=(20, 5),
|
||||
)
|
||||
monitor = ResourceMonitor(proc_root=proc_root, page_size=4096, now=lambda: sampled_at)
|
||||
|
||||
first = monitor.sample_now()
|
||||
|
||||
assert first["status"] == "ok"
|
||||
assert first["sampled_at"] == "2026-07-21T08:00:00.000Z"
|
||||
assert first["cpu"] == {
|
||||
"application_percent": None,
|
||||
"total_percent": None,
|
||||
"logical_cpu_count": 4,
|
||||
"application_core_equivalent": None,
|
||||
"cores_percent": [None, None, None, None],
|
||||
}
|
||||
assert first["memory"]["application_bytes"] == 409_600
|
||||
assert first["memory"]["application_percent"] == pytest.approx(0.4)
|
||||
assert first["memory"]["total_percent"] == pytest.approx(60.0)
|
||||
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 130 0 120 850 0 0 0 0 0 0",
|
||||
core_lines=(
|
||||
"cpu0 35 0 30 210 0 0 0 0 0 0",
|
||||
"cpu1 35 0 30 210 0 0 0 0 0 0",
|
||||
"cpu2 30 0 30 215 0 0 0 0 0 0",
|
||||
"cpu3 30 0 30 215 0 0 0 0 0 0",
|
||||
),
|
||||
process_ticks=(24, 6),
|
||||
)
|
||||
second = monitor.sample_now()
|
||||
|
||||
assert second["cpu"]["application_percent"] == pytest.approx(5.0)
|
||||
assert second["cpu"]["application_core_equivalent"] == pytest.approx(0.2)
|
||||
assert second["cpu"]["total_percent"] == pytest.approx(50.0)
|
||||
assert second["cpu"]["cores_percent"] == pytest.approx([60.0, 60.0, 40.0, 40.0])
|
||||
assert second["error_code"] is None
|
||||
|
||||
|
||||
def test_resource_monitor_reports_application_using_more_than_one_core(tmp_path):
|
||||
proc_root = tmp_path / "proc"
|
||||
first_cores = tuple(f"cpu{index} 100 0 0 0" for index in range(4))
|
||||
second_cores = tuple(f"cpu{index} 200 0 0 0" for index in range(4))
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 400 0 0 0",
|
||||
core_lines=first_cores,
|
||||
process_ticks=(20, 0),
|
||||
)
|
||||
monitor = ResourceMonitor(proc_root=proc_root, page_size=4096)
|
||||
monitor.sample_now()
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 800 0 0 0",
|
||||
core_lines=second_cores,
|
||||
process_ticks=(170, 0),
|
||||
)
|
||||
|
||||
status = monitor.sample_now()
|
||||
|
||||
assert status["cpu"]["application_percent"] == pytest.approx(37.5)
|
||||
assert status["cpu"]["application_core_equivalent"] == pytest.approx(1.5)
|
||||
assert status["cpu"]["cores_percent"] == pytest.approx([100.0] * 4)
|
||||
|
||||
|
||||
def test_resource_monitor_discards_cpu_delta_when_core_set_changes(tmp_path):
|
||||
proc_root = tmp_path / "proc"
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 200 0 0 0",
|
||||
core_lines=("cpu0 100 0 0 0", "cpu1 100 0 0 0"),
|
||||
process_ticks=(10, 0),
|
||||
)
|
||||
monitor = ResourceMonitor(proc_root=proc_root, page_size=4096)
|
||||
monitor.sample_now()
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 600 0 0 0",
|
||||
core_lines=tuple(f"cpu{index} 150 0 0 0" for index in range(4)),
|
||||
process_ticks=(50, 0),
|
||||
)
|
||||
|
||||
status = monitor.sample_now()
|
||||
|
||||
assert status["cpu"]["logical_cpu_count"] == 4
|
||||
assert status["cpu"]["application_percent"] is None
|
||||
assert status["cpu"]["application_core_equivalent"] is None
|
||||
assert status["cpu"]["total_percent"] is None
|
||||
assert status["cpu"]["cores_percent"] == [None, None, None, None]
|
||||
|
||||
|
||||
def test_resource_monitor_clamps_percentages_and_returns_defensive_copies(tmp_path):
|
||||
proc_root = tmp_path / "proc"
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 100 0 0 0 0 0 0 0 0 0",
|
||||
core_lines=tuple(f"cpu{index} 25 0 0 0" for index in range(4)),
|
||||
process_ticks=(0, 0),
|
||||
rss_pages=50_000,
|
||||
mem_total_kib=100_000,
|
||||
mem_available_kib=0,
|
||||
)
|
||||
monitor = ResourceMonitor(proc_root=proc_root, page_size=4096)
|
||||
monitor.sample_now()
|
||||
write_proc_snapshot(
|
||||
proc_root,
|
||||
cpu_line="cpu 200 0 0 0 0 0 0 0 0 0",
|
||||
core_lines=tuple(f"cpu{index} 50 0 0 0" for index in range(4)),
|
||||
process_ticks=(300, 0),
|
||||
rss_pages=50_000,
|
||||
mem_total_kib=100_000,
|
||||
mem_available_kib=0,
|
||||
)
|
||||
|
||||
status = monitor.sample_now()
|
||||
status["cpu"]["total_percent"] = -1
|
||||
status["cpu"]["cores_percent"][0] = -1
|
||||
status["memory"]["application_percent"] = -1
|
||||
cached = monitor.get_status()
|
||||
|
||||
assert cached["cpu"]["application_percent"] == 100.0
|
||||
assert cached["cpu"]["application_core_equivalent"] == 4.0
|
||||
assert cached["cpu"]["total_percent"] == 100.0
|
||||
assert cached["cpu"]["cores_percent"] == [100.0] * 4
|
||||
assert cached["memory"]["application_percent"] == 100.0
|
||||
assert cached["memory"]["total_percent"] == 100.0
|
||||
|
||||
|
||||
def test_resource_monitor_failure_is_isolated_and_does_not_create_files(tmp_path):
|
||||
proc_root = tmp_path / "missing-proc"
|
||||
monitor = ResourceMonitor(proc_root=proc_root, poll_interval=0.01, page_size=4096)
|
||||
|
||||
status = monitor.sample_now()
|
||||
monitor.start()
|
||||
monitor.close()
|
||||
|
||||
assert status["status"] == "error"
|
||||
assert status["error_code"] == "proc_read_error"
|
||||
assert status["cpu"] == {
|
||||
"application_percent": None,
|
||||
"total_percent": None,
|
||||
"logical_cpu_count": None,
|
||||
"application_core_equivalent": None,
|
||||
"cores_percent": [],
|
||||
}
|
||||
assert status["memory"] == {
|
||||
"application_bytes": None,
|
||||
"application_percent": None,
|
||||
"total_percent": None,
|
||||
}
|
||||
assert not proc_root.exists()
|
||||
Reference in New Issue
Block a user