Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions codecarbon/core/powermetrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,17 +118,19 @@ def _setup_cli(self) -> None:
"""
Setup cli command to run Powermetrics
"""
if self._system.startswith("darwin"):
cpu_model = detect_cpu_model()
if cpu_model.startswith("Apple"):
if shutil.which(self._osx_silicon_exec):
self._cli = self._osx_silicon_exec
else:
raise FileNotFoundError(
f"Powermetrics executable not found on {self._system}"
)
else:
if not self._system.startswith("darwin"):
raise SystemError("Platform not supported by Powermetrics")
cpu_model = detect_cpu_model() or ""
if not cpu_model.startswith("Apple"):
raise SystemError(
"Powermetrics is only supported on Apple Silicon, "
f"detected CPU: {cpu_model!r}"
)
if not shutil.which(self._osx_silicon_exec):
raise FileNotFoundError(
f"Powermetrics executable not found on {self._system}"
)
self._cli = self._osx_silicon_exec

def _log_values(self) -> None:
"""
Expand All @@ -140,10 +142,9 @@ def _log_values(self) -> None:
# Run the powermetrics command with sudo and capture its output
cmd = [
"sudo",
"powermetrics",
self._cli,
"-n",
str(self._n_points),
"",
"--samplers",
"cpu_power",
"--format",
Expand Down
52 changes: 52 additions & 0 deletions tests/test_powermetrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,40 @@ def test_setup_cli_raises_when_binary_missing_on_apple_silicon(self):
with pytest.raises(FileNotFoundError):
ApplePowermetrics()

def test_setup_cli_raises_on_intel_mac(self):
with (
mock.patch("codecarbon.core.powermetrics.sys.platform", "darwin"),
mock.patch(
"codecarbon.core.powermetrics.detect_cpu_model",
return_value="Intel(R) Core(TM) i7-9750H",
),
):
with pytest.raises(SystemError):
ApplePowermetrics()

def test_setup_cli_raises_when_cpu_model_unknown(self):
with (
mock.patch("codecarbon.core.powermetrics.sys.platform", "darwin"),
mock.patch(
"codecarbon.core.powermetrics.detect_cpu_model", return_value=None
),
):
with pytest.raises(SystemError):
ApplePowermetrics()

def test_setup_cli_sets_cli_on_apple_silicon(self):
with (
mock.patch("codecarbon.core.powermetrics.sys.platform", "darwin"),
mock.patch(
"codecarbon.core.powermetrics.detect_cpu_model", return_value="Apple M2"
),
mock.patch(
"codecarbon.core.powermetrics.shutil.which",
return_value="/usr/bin/powermetrics",
),
):
assert ApplePowermetrics()._cli == "powermetrics"

def test_log_values_returns_none_on_non_darwin(self):
powermetrics = ApplePowermetrics.__new__(ApplePowermetrics)
powermetrics._system = "linux"
Expand All @@ -221,6 +255,7 @@ def test_log_values_warns_on_nonzero_returncode(self):
powermetrics._n_points = 3
powermetrics._interval = 100
powermetrics._log_file_path = "powermetrics_log.txt"
powermetrics._cli = "powermetrics"

with (
mock.patch(
Expand All @@ -233,6 +268,23 @@ def test_log_values_warns_on_nonzero_returncode(self):
mock_call.assert_called_once()
mock_warning.assert_called_once()

def test_log_values_builds_clean_command(self):
powermetrics = ApplePowermetrics.__new__(ApplePowermetrics)
powermetrics._system = "darwin"
powermetrics._n_points = 3
powermetrics._interval = 100
powermetrics._log_file_path = "powermetrics_log.txt"
powermetrics._cli = "powermetrics"

with mock.patch(
"codecarbon.core.powermetrics.subprocess.call", return_value=0
) as mock_call:
powermetrics._log_values()

cmd = mock_call.call_args.args[0]
assert "" not in cmd
assert cmd[1] == powermetrics._cli

@mock.patch("codecarbon.core.powermetrics.ApplePowermetrics._log_values")
@mock.patch("builtins.open", side_effect=OSError("missing"))
@mock.patch("codecarbon.core.powermetrics.ApplePowermetrics._setup_cli")
Expand Down
Loading