mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-31 03:12:16 +00:00
258 lines
8.2 KiB
Python
258 lines
8.2 KiB
Python
"""Tests for RaplEnergyMonitor -- mock sysfs (no real RAPL required)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from openjarvis.telemetry.energy_rapl import (
|
|
RaplEnergyMonitor,
|
|
_discover_domains,
|
|
)
|
|
from tests.telemetry.energy_test_helpers import (
|
|
assert_close_sets_uninitialized,
|
|
assert_sample_result_basics,
|
|
)
|
|
|
|
_PLAT = "openjarvis.telemetry.energy_rapl.platform.system"
|
|
_BASE = "openjarvis.telemetry.energy_rapl._RAPL_BASE"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers: build a fake sysfs directory
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _create_rapl_domain(
|
|
base: Path,
|
|
name: str,
|
|
dir_name: str,
|
|
energy_uj: int = 100000,
|
|
max_energy_uj: int = 262143328850,
|
|
) -> Path:
|
|
"""Create a single RAPL domain directory with required files."""
|
|
domain_dir = base / dir_name
|
|
domain_dir.mkdir(parents=True, exist_ok=True)
|
|
(domain_dir / "name").write_text(name)
|
|
(domain_dir / "energy_uj").write_text(str(energy_uj))
|
|
(domain_dir / "max_energy_range_uj").write_text(str(max_energy_uj))
|
|
return domain_dir
|
|
|
|
|
|
def _build_fake_sysfs(tmp_path: Path) -> Path:
|
|
"""Build a fake /sys/class/powercap/intel-rapl tree.
|
|
|
|
Creates:
|
|
intel-rapl:0/ (package-0)
|
|
intel-rapl:0/intel-rapl:0:0/ (dram)
|
|
"""
|
|
rapl_base = tmp_path / "intel-rapl"
|
|
rapl_base.mkdir()
|
|
|
|
_create_rapl_domain(
|
|
rapl_base,
|
|
"package-0",
|
|
"intel-rapl:0",
|
|
energy_uj=500000,
|
|
max_energy_uj=262143328850,
|
|
)
|
|
_create_rapl_domain(
|
|
rapl_base,
|
|
"dram",
|
|
"intel-rapl:0/intel-rapl:0:0",
|
|
energy_uj=100000,
|
|
max_energy_uj=65535999603,
|
|
)
|
|
return rapl_base
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: available()
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestAvailable:
|
|
def test_available_false_on_non_linux(self):
|
|
with patch(_PLAT, return_value="Darwin"):
|
|
assert RaplEnergyMonitor.available() is False
|
|
|
|
def test_available_false_when_no_sysfs(self):
|
|
with (
|
|
patch(_PLAT, return_value="Linux"),
|
|
patch(_BASE, Path("/nonexistent")),
|
|
):
|
|
assert RaplEnergyMonitor.available() is False
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: energy_method()
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestEnergyMethod:
|
|
def test_returns_rapl(self):
|
|
monitor = RaplEnergyMonitor.__new__(RaplEnergyMonitor)
|
|
assert monitor.energy_method() == "rapl"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: domain discovery
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestDomainDiscovery:
|
|
def test_discovers_domains_from_sysfs(self, tmp_path):
|
|
rapl_base = _build_fake_sysfs(tmp_path)
|
|
domains = _discover_domains(rapl_base)
|
|
|
|
assert len(domains) == 2
|
|
names = [d.name for d in domains]
|
|
assert "package-0" in names
|
|
assert "dram" in names
|
|
|
|
def test_discovers_no_domains_from_empty_dir(self, tmp_path):
|
|
rapl_base = tmp_path / "intel-rapl"
|
|
rapl_base.mkdir()
|
|
domains = _discover_domains(rapl_base)
|
|
assert len(domains) == 0
|
|
|
|
def test_discovers_no_domains_from_nonexistent_dir(self):
|
|
domains = _discover_domains(Path("/nonexistent/intel-rapl"))
|
|
assert len(domains) == 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: sample() normal counter delta
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSampleNormalDelta:
|
|
def test_normal_counter_delta(self, tmp_path):
|
|
"""Start reading, then update energy files, verify delta."""
|
|
rapl_base = _build_fake_sysfs(tmp_path)
|
|
|
|
with patch(_PLAT, return_value="Linux"):
|
|
monitor = RaplEnergyMonitor(
|
|
poll_interval_ms=50,
|
|
rapl_base=rapl_base,
|
|
)
|
|
assert monitor._initialized is True
|
|
assert len(monitor._domains) == 2
|
|
|
|
# Set start values
|
|
pkg_energy = rapl_base / "intel-rapl:0" / "energy_uj"
|
|
dram_energy = rapl_base / "intel-rapl:0" / "intel-rapl:0:0" / "energy_uj"
|
|
pkg_energy.write_text("500000")
|
|
dram_energy.write_text("100000")
|
|
|
|
with monitor.sample() as result:
|
|
# Simulate energy consumption during block
|
|
pkg_energy.write_text("600000")
|
|
dram_energy.write_text("120000")
|
|
|
|
# package delta: 600000 - 500000 = 100000 uJ = 0.1 J (cpu)
|
|
# dram delta: 120000 - 100000 = 20000 uJ = 0.02 J
|
|
# total: 120000 uJ = 0.12 J
|
|
assert result.cpu_energy_joules == pytest.approx(100000 / 1e6)
|
|
assert result.dram_energy_joules == pytest.approx(20000 / 1e6)
|
|
assert result.energy_joules == pytest.approx(120000 / 1e6)
|
|
assert_sample_result_basics(
|
|
result, vendor="cpu_rapl", energy_method="rapl"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: sample() counter wrap-around
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSampleWrapAround:
|
|
def test_counter_wrap_around(self, tmp_path):
|
|
"""When end < start, uses max_energy_range_uj."""
|
|
rapl_base = tmp_path / "intel-rapl"
|
|
rapl_base.mkdir()
|
|
|
|
max_energy = 1000000
|
|
_create_rapl_domain(
|
|
rapl_base,
|
|
"package-0",
|
|
"intel-rapl:0",
|
|
energy_uj=900000,
|
|
max_energy_uj=max_energy,
|
|
)
|
|
|
|
with patch(_PLAT, return_value="Linux"):
|
|
monitor = RaplEnergyMonitor(
|
|
poll_interval_ms=50,
|
|
rapl_base=rapl_base,
|
|
)
|
|
assert monitor._initialized is True
|
|
|
|
pkg_energy = rapl_base / "intel-rapl:0" / "energy_uj"
|
|
pkg_energy.write_text("900000")
|
|
|
|
with monitor.sample() as result:
|
|
# Counter wraps: end=200000 < start=900000
|
|
pkg_energy.write_text("200000")
|
|
|
|
# wrap = (max - start) + end = 100000 + 200000 = 300000 uJ
|
|
expected_uj = (max_energy - 900000) + 200000
|
|
assert result.energy_joules == pytest.approx(expected_uj / 1e6)
|
|
assert result.cpu_energy_joules == pytest.approx(
|
|
expected_uj / 1e6,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: sample() domain categorization
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSampleDomainCategorization:
|
|
def test_package_domains_categorized_as_cpu(self, tmp_path):
|
|
rapl_base = _build_fake_sysfs(tmp_path)
|
|
|
|
with patch(_PLAT, return_value="Linux"):
|
|
monitor = RaplEnergyMonitor(
|
|
poll_interval_ms=50,
|
|
rapl_base=rapl_base,
|
|
)
|
|
|
|
# Set start values
|
|
pkg_energy = rapl_base / "intel-rapl:0" / "energy_uj"
|
|
dram_energy = rapl_base / "intel-rapl:0" / "intel-rapl:0:0" / "energy_uj"
|
|
pkg_energy.write_text("1000")
|
|
dram_energy.write_text("2000")
|
|
|
|
with monitor.sample() as result:
|
|
pkg_energy.write_text("5000")
|
|
dram_energy.write_text("3000")
|
|
|
|
# package-0 delta (4000 uJ) -> cpu_energy_joules
|
|
assert result.cpu_energy_joules == pytest.approx(4000 / 1e6)
|
|
# dram delta (1000 uJ) -> dram_energy_joules
|
|
assert result.dram_energy_joules == pytest.approx(1000 / 1e6)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: close()
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestClose:
|
|
def test_close_clears_domains(self, tmp_path):
|
|
rapl_base = _build_fake_sysfs(tmp_path)
|
|
|
|
with patch(_PLAT, return_value="Linux"):
|
|
monitor = RaplEnergyMonitor(
|
|
poll_interval_ms=50,
|
|
rapl_base=rapl_base,
|
|
)
|
|
assert len(monitor._domains) == 2
|
|
assert monitor._initialized is True
|
|
|
|
assert_close_sets_uninitialized(monitor)
|
|
assert monitor._domains == []
|