diff --git a/codecarbon/core/powermetrics.py b/codecarbon/core/powermetrics.py index bffc19ce4..935262ea9 100644 --- a/codecarbon/core/powermetrics.py +++ b/codecarbon/core/powermetrics.py @@ -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: """ @@ -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", diff --git a/tests/test_powermetrics.py b/tests/test_powermetrics.py index b20f5df2c..834ba6428 100644 --- a/tests/test_powermetrics.py +++ b/tests/test_powermetrics.py @@ -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" @@ -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( @@ -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")