from contextlib import ExitStack from unittest.mock import MagicMock import pytest from afancontrol.pwmfan import ( FanInputDevice, LinuxFanPWMRead, LinuxFanPWMWrite, LinuxFanSpeed, PWMDevice, PWMValue, ) from afancontrol.pwmfannorm import PWMFanNorm @pytest.fixture def pwm_path(temp_path): # pwm = /sys/class/hwmon/hwmon0/pwm2 pwm_path = temp_path / "pwm2" pwm_path.write_text("0\n") return pwm_path @pytest.fixture def pwm_enable_path(temp_path): pwm_enable_path = temp_path / "pwm2_enable" pwm_enable_path.write_text("0\n") return pwm_enable_path @pytest.fixture def fan_input_path(temp_path): # fan_input = /sys/class/hwmon/hwmon0/fan2_input fan_input_path = temp_path / "fan2_input" fan_input_path.write_text("1300\n") return fan_input_path @pytest.fixture def fan_speed(fan_input_path): return LinuxFanSpeed(fan_input=FanInputDevice(str(fan_input_path))) @pytest.fixture def pwm_read(pwm_path): return LinuxFanPWMRead(pwm=PWMDevice(str(pwm_path))) @pytest.fixture def pwm_write(pwm_path): pwm_write = LinuxFanPWMWrite(pwm=PWMDevice(str(pwm_path))) # We write to the pwm_enable file values without newlines, # but when they're read back, they might contain newlines. # This hack below is to simulate just that: the written values should # contain newlines. original_pwm_enable = pwm_write._pwm_enable pwm_enable = MagicMock(wraps=original_pwm_enable) pwm_enable.write_text = lambda text: original_pwm_enable.write_text(text + "\n") pwm_write._pwm_enable = pwm_enable return pwm_write @pytest.fixture def pwmfan_norm(fan_speed, pwm_read, pwm_write): return PWMFanNorm( fan_speed, pwm_read, pwm_write, pwm_line_start=PWMValue(100), pwm_line_end=PWMValue(240), never_stop=False, ) @pytest.mark.parametrize("pwmfan_fixture", ["fan_speed", "pwmfan_norm"]) def test_get_speed(pwmfan_fixture, fan_speed, pwmfan_norm, fan_input_path): fan = locals()[pwmfan_fixture] fan_input_path.write_text("721\n") assert 721 == fan.get_speed() @pytest.mark.parametrize("pwmfan_fixture", ["pwm_write", "pwmfan_norm"]) @pytest.mark.parametrize("raises", [True, False]) def test_enter_exit( raises, pwmfan_fixture, pwm_write, pwmfan_norm, pwm_enable_path, pwm_path ): fan = locals()[pwmfan_fixture] class Exc(Exception): pass with ExitStack() as stack: if raises: stack.enter_context(pytest.raises(Exc)) stack.enter_context(fan) assert "1" == pwm_enable_path.read_text().strip() assert "255" == pwm_path.read_text() value = dict(pwm_write=100, pwmfan_norm=0.39)[pwmfan_fixture] # 100/255 ~= 0.39 fan.set(value) assert "1" == pwm_enable_path.read_text().strip() assert "100" == pwm_path.read_text() if raises: raise Exc() assert "0" == pwm_enable_path.read_text().strip() assert "100" == pwm_path.read_text() # `fancontrol` doesn't reset speed def test_get_set_pwmfan(pwm_read, pwm_write, pwm_path): pwm_write.set(142) assert "142" == pwm_path.read_text() pwm_path.write_text("132\n") assert 132 == pwm_read.get() pwm_write.set_full_speed() assert "255" == pwm_path.read_text() with pytest.raises(ValueError): pwm_write.set(256) with pytest.raises(ValueError): pwm_write.set(-1) def test_get_set_pwmfan_norm(pwmfan_norm, pwm_path): pwmfan_norm.set(0.42) assert "101" == pwm_path.read_text() pwm_path.write_text("132\n") assert pytest.approx(0.517, 0.01) == pwmfan_norm.get() pwmfan_norm.set_full_speed() assert "255" == pwm_path.read_text() assert 238 == pwmfan_norm.set(0.99) assert "238" == pwm_path.read_text() assert 255 == pwmfan_norm.set(1.0) assert "255" == pwm_path.read_text() assert 255 == pwmfan_norm.set(1.1) assert "255" == pwm_path.read_text() assert 0 == pwmfan_norm.set(-0.1) assert "0" == pwm_path.read_text()