mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-23 16:36:23 +00:00
Add an opt-in battery indicator to the CLI and TUI status bars, shown as the first element and colour-coded by charge (green/yellow/orange/red, or green while charging). Off by default and a no-op on machines without a battery. - agent/battery.py: shared psutil-backed reader with a short TTL cache, category bucketing, and a compact 🔋/⚡ label. Fails open to "unavailable" everywhere. - CLI: /battery [on|off|status] toggle persisted to display.battery, rendered first in every status-bar width tier. - TUI: /battery slash command, config sync, a system.battery RPC polled while enabled, and a pinned first segment in StatusRule.
115 lines
3.5 KiB
Python
115 lines
3.5 KiB
Python
"""Behavior tests for the status-bar battery helper (agent/battery.py)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import types
|
|
|
|
import pytest
|
|
|
|
from agent import battery as battery_mod
|
|
from agent.battery import (
|
|
BatteryStatus,
|
|
battery_category,
|
|
battery_glyph,
|
|
format_battery,
|
|
read_battery,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_cache():
|
|
battery_mod.clear_cache()
|
|
yield
|
|
battery_mod.clear_cache()
|
|
|
|
|
|
def _fake_psutil(percent, plugged):
|
|
"""Install a fake psutil module whose sensors_battery returns a reading."""
|
|
mod = types.ModuleType("psutil")
|
|
reading = types.SimpleNamespace(percent=percent, power_plugged=plugged)
|
|
mod.sensors_battery = lambda: reading # type: ignore[attr-defined]
|
|
return mod
|
|
|
|
|
|
def test_read_battery_no_psutil(monkeypatch):
|
|
# Force the import inside read_battery to fail.
|
|
monkeypatch.setitem(sys.modules, "psutil", None)
|
|
status = read_battery(use_cache=False)
|
|
assert status.available is False
|
|
assert status.percent is None
|
|
|
|
|
|
def test_read_battery_no_battery(monkeypatch):
|
|
mod = types.ModuleType("psutil")
|
|
mod.sensors_battery = lambda: None # type: ignore[attr-defined]
|
|
monkeypatch.setitem(sys.modules, "psutil", mod)
|
|
|
|
status = read_battery(use_cache=False)
|
|
assert status.available is False
|
|
|
|
|
|
def test_read_battery_reads_and_clamps(monkeypatch):
|
|
monkeypatch.setitem(sys.modules, "psutil", _fake_psutil(87.6, False))
|
|
status = read_battery(use_cache=False)
|
|
assert status.available is True
|
|
assert status.percent == 88 # rounded
|
|
assert status.plugged is False
|
|
|
|
|
|
def test_read_battery_clamps_out_of_range(monkeypatch):
|
|
monkeypatch.setitem(sys.modules, "psutil", _fake_psutil(150, True))
|
|
status = read_battery(use_cache=False)
|
|
assert status.percent == 100
|
|
assert status.plugged is True
|
|
|
|
|
|
def test_read_battery_caches(monkeypatch):
|
|
monkeypatch.setitem(sys.modules, "psutil", _fake_psutil(50, False))
|
|
first = read_battery(use_cache=True)
|
|
assert first.percent == 50
|
|
|
|
# Swap the reading; a cached call must still return the first value.
|
|
monkeypatch.setitem(sys.modules, "psutil", _fake_psutil(10, True))
|
|
cached = read_battery(use_cache=True)
|
|
assert cached.percent == 50
|
|
|
|
# Bypassing the cache picks up the new reading.
|
|
fresh = read_battery(use_cache=False)
|
|
assert fresh.percent == 10
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"percent,plugged,expected",
|
|
[
|
|
(100, False, "good"),
|
|
(51, False, "good"),
|
|
(50, False, "warn"),
|
|
(21, False, "warn"),
|
|
(20, False, "bad"),
|
|
(11, False, "bad"),
|
|
(10, False, "critical"),
|
|
(1, False, "critical"),
|
|
# On AC power the level never reads as low.
|
|
(5, True, "good"),
|
|
],
|
|
)
|
|
def test_battery_category_thresholds(percent, plugged, expected):
|
|
status = BatteryStatus(available=True, percent=percent, plugged=plugged)
|
|
assert battery_category(status) == expected
|
|
|
|
|
|
def test_battery_category_unavailable_is_dim():
|
|
assert battery_category(BatteryStatus(available=False)) == "dim"
|
|
assert battery_category(BatteryStatus(available=True, percent=None)) == "dim"
|
|
|
|
|
|
def test_format_and_glyph():
|
|
on_battery = BatteryStatus(available=True, percent=82, plugged=False)
|
|
charging = BatteryStatus(available=True, percent=82, plugged=True)
|
|
|
|
assert battery_glyph(on_battery) == "\U0001f50b" # 🔋
|
|
assert battery_glyph(charging) == "\u26a1" # ⚡
|
|
assert format_battery(on_battery) == "\U0001f50b 82%"
|
|
assert format_battery(charging) == "\u26a1 82%"
|
|
assert format_battery(BatteryStatus(available=False)) == ""
|