import os
import re
import subprocess
import sys
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def repo_version(root=REPO_ROOT):
with open(os.path.join(root, "Cargo.toml"), encoding="utf-8") as fh:
text = fh.read()
package = text.split("[package]", 1)[1].split("\n[", 1)[0] if "[package]" in text else ""
m = re.search(r'^version\s*=\s*"([^"]+)"', package, re.M)
if not m:
raise ValueError("no [package] version in Cargo.toml")
return f"v{m.group(1)}"
def extra_for(version, power=None):
return f"{version}; {power}" if power else version
def parse_extra(extra):
version, _, power = extra.partition("; ")
return version, (power or None)
def power_state():
try:
if sys.platform.startswith("linux"):
return _linux_power("/sys/class/power_supply")
if sys.platform == "darwin":
out = subprocess.run(["pmset", "-g", "batt"], capture_output=True, text=True,
timeout=5).stdout
return _pmset_power(out)
if sys.platform == "win32":
return _windows_power()
except (OSError, subprocess.SubprocessError, ValueError):
return None
return None
def _linux_power(root):
if not os.path.isdir(root):
return None
mains, usb, has_battery = [], [], False
for name in sorted(os.listdir(root)):
base = os.path.join(root, name)
try:
kind = _read(os.path.join(base, "type"))
except OSError:
continue
if kind == "Mains":
try:
mains.append(_read(os.path.join(base, "online")) == "1")
except OSError:
pass
elif kind == "USB" and _read_or(os.path.join(base, "scope"), "System") == "System":
usb.append(_read_or(os.path.join(base, "online"), "0") == "1")
elif kind == "Battery" and _read_or(os.path.join(base, "scope"), "System") == "System":
has_battery = True
if any(mains):
return "AC"
if mains:
return "battery"
if any(usb):
return "AC"
if usb and has_battery:
return "battery"
return None if has_battery else "AC"
def _pmset_power(out):
first = out.splitlines()[0] if out else ""
if "'AC Power'" in first:
return "AC"
if "'Battery Power'" in first:
return "battery"
return None
def _windows_power():
import ctypes
class SYSTEM_POWER_STATUS(ctypes.Structure):
_fields_ = [("ACLineStatus", ctypes.c_ubyte), ("BatteryFlag", ctypes.c_ubyte),
("BatteryLifePercent", ctypes.c_ubyte), ("SystemStatusFlag", ctypes.c_ubyte),
("BatteryLifeTime", ctypes.c_ulong), ("BatteryFullLifeTime", ctypes.c_ulong)]
status = SYSTEM_POWER_STATUS()
if not ctypes.windll.kernel32.GetSystemPowerStatus(ctypes.byref(status)):
return None
return {1: "AC", 0: "battery"}.get(status.ACLineStatus)
def _read(path):
with open(path, encoding="utf-8") as fh:
return fh.read().strip()
def _read_or(path, default):
try:
return _read(path)
except OSError:
return default
def _self_test():
import tempfile
failures = []
def check(name, cond, detail=""):
print(f" {'ok ' if cond else 'FAIL'} {name}{'' if cond else ': ' + detail}")
if not cond:
failures.append(name)
check("extra without power", extra_for("v0.20.6") == "v0.20.6")
check("extra with power", extra_for("v0.20.6", "battery") == "v0.20.6; battery")
check("extra round-trips", parse_extra(extra_for("v1.2.3", "AC")) == ("v1.2.3", "AC"))
check("extra without power round-trips", parse_extra("v1.2.3") == ("v1.2.3", None))
with tempfile.TemporaryDirectory() as tmp:
with open(os.path.join(tmp, "Cargo.toml"), "w", encoding="utf-8") as fh:
fh.write('[workspace]\nmembers = ["x"]\n\n[package]\nname = "retch-cli"\n'
'version = "0.20.6"\n\n[dependencies]\nclap = { version = "4" }\n')
check("version from [package]", repo_version(tmp) == "v0.20.6", repo_version(tmp))
check("this repo has a version", re.fullmatch(r"v\d+\.\d+\.\d+", repo_version()) is not None)
def supplies(entries):
root = tempfile.mkdtemp()
for name, files in entries.items():
os.makedirs(os.path.join(root, name))
for f, v in files.items():
with open(os.path.join(root, name, f), "w", encoding="utf-8") as fh:
fh.write(v + "\n")
return root
laptop = {"AC0": {"type": "Mains", "online": "0"},
"BAT0": {"type": "Battery"},
"hid-0018:04F3:4631.0002-battery-7": {"type": "Battery", "scope": "Device",
"online": "1"},
"hidpp_battery_0": {"type": "Battery", "scope": "Device", "online": "1"},
"ucsi-source-psy-USBC000:001": {"type": "USB", "scope": "System", "online": "0"},
"ucsi-source-psy-USBC000:002": {"type": "USB", "scope": "System", "online": "1"}}
check("laptop on battery", _linux_power(supplies(laptop)) == "battery")
laptop["AC0"]["online"] = "1"
check("laptop on AC", _linux_power(supplies(laptop)) == "AC")
desktop = {"hidpp_battery_0": {"type": "Battery", "scope": "Device"}}
check("desktop with only a mouse battery is AC", _linux_power(supplies(desktop)) == "AC")
check("no power_supply dir is unknown", _linux_power("/nonexistent/ps") is None)
usbc = {"BAT0": {"type": "Battery"},
"ucsi-source-psy-USBC000:001": {"type": "USB", "scope": "System", "online": "1"}}
check("USB-C-only laptop charging is AC", _linux_power(supplies(usbc)) == "AC")
usbc["ucsi-source-psy-USBC000:001"]["online"] = "0"
check("USB-C-only laptop unplugged is battery", _linux_power(supplies(usbc)) == "battery")
check("battery but no Mains entry is unknown",
_linux_power(supplies({"BAT0": {"type": "Battery", "scope": "System"}})) is None)
check("pmset AC", _pmset_power("Now drawing from 'AC Power'\n -InternalBattery-0") == "AC")
check("pmset battery", _pmset_power("Now drawing from 'Battery Power'\n") == "battery")
check("pmset unknown", _pmset_power("") is None)
print(f"bench_meta self-test: {'FAILED' if failures else 'ok'}")
return 1 if failures else 0
if __name__ == "__main__":
sys.exit(_self_test())