994 lines
44 KiB
Python
994 lines
44 KiB
Python
import pytest
|
|
from _testutils import MockHidapiDevice, Report
|
|
|
|
from liquidctl.driver.hwmon import HwmonDevice
|
|
from liquidctl.driver.aquacomputer import Aquacomputer
|
|
from liquidctl.error import NotSupportedByDriver, NotSupportedByDevice
|
|
|
|
D5NEXT_SAMPLE_STATUS_REPORT = bytes.fromhex(
|
|
"00030DCB597C00010000006403FF00000051000004DC14000001E0007A98AF000"
|
|
"00000FFFF000041A803C169000001481ACAA3465CB804B401F40000005213887F"
|
|
"FF7FFF7FFF7FFF7FFF7FFF7FFF000000000000000009D27FFF00007FFF01F404B"
|
|
"400200026016D006300000004B200D7010207B80000000000098D083A098A083A"
|
|
"00060001000000000000000000000000011A24015E27101D4CFFBF"
|
|
)
|
|
|
|
D5NEXT_SAMPLE_CONTROL_REPORT = bytes.fromhex(
|
|
"00031E00000000000AC0007FFF0000000002020E100BB8000000000A0001000A0"
|
|
"006000A000C000A0000000000000101F42710271007D000000027102710138802"
|
|
"07D200000C8001F4012C00000064001E00010AF00A8C0AFD0B4C0B9D0BE90C460"
|
|
"C9F0CF30D3C0DA20DE50E420E8A0EE60F350F7000000000000002D604D606D609"
|
|
"810A010DAC1202162D17AD19D81EAE222E232E0212D300000D4801F4012C00000"
|
|
"064001E00010AF00A8C0AFA0B4C0BA40C000C4F0CA30D110D510DA60DFD0E560E"
|
|
"9E0EEE0F2010820000008C0000000000000000000001000180035407810A810B0"
|
|
"10C810DD70EAC03E8FF000000000F030000FFFF0F19000003E80164000003E801"
|
|
"FF0032006400000000000000000000000000000000000000000000FFFF0000FFF"
|
|
"F0000FFFF0000FFFF0000FFFF0000FFFF000F0F080000FFFF0F19000003E80164"
|
|
"000003E801FF00190028001400000000000000000000000000000000000000000"
|
|
"00F03E7FFFF00FEFFFF0000FFFF0000FFFF0000FFFF001E0F0B0000FFFF0F1900"
|
|
"0003E80164000003E801FF001E002800010006005000000000000000000000000"
|
|
"0000002FF02FF01FBFFFF0525FFFF00C5FFFF03F5FFFF05F3FFFF002D0F040006"
|
|
"FFFF0F19000003E80164000003E801FF002800050000000000000000000000000"
|
|
"0000000000000000000000F0000FFFF01FDFFFF03FFFFFF00FAFFFF01CE10FF00"
|
|
"3C0F040006FFFF0F19000003E80164000003E801FF00280005000000000000000"
|
|
"00000000000000000000000000000000F00FAFFFF05DCFFFF01C2FFFF0000FFFF"
|
|
"07D010FF004B0F040006FFFF0F19000003E80164000003E801FF0028000500000"
|
|
"000000000000000000000000000000000000000000F03E8FFFF01C2FFFF0000FF"
|
|
"FF0064FFFF032010FF010006030000FFFF0F19000003E80164000003E801FF001"
|
|
"E006400000000000000000000000000000000000000000000FFFF0000FFFF0000"
|
|
"FFFF0000FFFF0000FFFF0000FFFF010006000000FFFF0F19000003E8016400000"
|
|
"3E80164001E006400000000000000000000000000000000000000000000FFFF00"
|
|
"00FFFF0000FFFF0000FFFF0000FFFF0000FFFFC00401C20FA00110FB"
|
|
)
|
|
|
|
FARBWERK360_SAMPLE_STATUS_REPORT = bytes.fromhex(
|
|
"000141BBDE9203E80000006403FE000000110000001A150000005F0008AE3E000"
|
|
"00023BFC8C01AA20EFFD6A0E8A3915AEC0A3C0A470A6F09F814507FFF7FFF7FFF"
|
|
"7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF00000000000000000"
|
|
"00000000000000001F901FA0006000000030000004300000000000A0324000000"
|
|
"00000000002710271027102710271003E8000003E8000003E8000003E80000000"
|
|
"0000000000000000000010002000101040006"
|
|
)
|
|
|
|
OCTO_SAMPLE_STATUS_REPORT = bytes.fromhex(
|
|
"00023A92C9EA03E80001006503FB000000010000010DB4000000C5003C3EA4010"
|
|
"00200000000000000000000000000059EDCFFDCFFDDFFDDA7A65BF80AC60ACF0B"
|
|
"150D600EC87FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FF"
|
|
"F7FFF0300000000000000000000000000000004B9000300030000055D04B90001"
|
|
"00010000000008138804B9015E006702400000000000000000000000000000000"
|
|
"00000000000000000000000000000000000000000000000000000000000000000"
|
|
"0000000000000000000000000000000000000000213B04B900020002000000000"
|
|
"80000000003E8055D0000000003E800000000000003E800000000000003E80000"
|
|
"0000000003E800000000000003E800000000000003E800000000000003E8213B0"
|
|
"000000003E827100000000003E827100000000000000000120412862710271098"
|
|
"20"
|
|
)
|
|
|
|
OCTO_SAMPLE_CONTROL_REPORT = bytes.fromhex(
|
|
"000228000000A90000051402BC000000000001F42710271007D00201F42710271"
|
|
"007D00201F42710271007D00201F42710271007D00201F42710271007D00201F4"
|
|
"2710271007D00201F42710271007D00001F42710271007D000055DFFFF0DAC057"
|
|
"804B000000028001400010AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D480DA2"
|
|
"0DF20E4C0E9C0EF50F460FA00000008C011801F4032004B0069008D40B680E4C1"
|
|
"194152C19281D7422102710000000FFFF0DAC057804B000000028001400010AF0"
|
|
"0A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4C0E9C0EF50F460FA00"
|
|
"000008C011801F4032004B0069008D40B680E4C1194152C19281D742210271000"
|
|
"0000FFFF0DAC057804B000000028001400010AF00A8C0AFA0B4A0BA40BF40C4E0"
|
|
"C9D0CF80D480DA20DF20E4C0E9C0EF50F460FA00000008C011801F4032004B006"
|
|
"9008D40B680E4C1194152C19281D7422102710000000FFFF0DAC057804B000000"
|
|
"028001400010AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4C0E"
|
|
"9C0EF50F460FA00000008C011801F4032004B0069008D40B680E4C1194152C192"
|
|
"81D7422102710000000FFFF0DAC057804B000000028001400010AF00A8C0AFA0B"
|
|
"4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4C0E9C0EF50F460FA00000008C011"
|
|
"801F4032004B0069008D40B680E4C1194152C19281D7422102710000000FFFF0D"
|
|
"AC057804B000000028001400010AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D4"
|
|
"80DA20DF20E4C0E9C0EF50F460FA00000008C011801F4032004B0069008D40B68"
|
|
"0E4C1194152C19281D7422102710000000FFFF0DAC057804B0000000280014000"
|
|
"10AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4C0E9C0EF50F46"
|
|
"0FA00000008C011801F4032004B0069008D40B680E4C1194152C19281D7422102"
|
|
"71000213BFFFF0DAC057804B000000028001400010AF00A8C0AFA0B4A0BA40BF4"
|
|
"0C4E0C9D0CF80D480DA20DF20E4C0E9C0EF50F460FA00000008C011801F403200"
|
|
"4B0069008D40B680E4C1194152C19281D74221027100000FF000000000F030000"
|
|
"FFFF0F19000003E80164000003E801FF003200640000000000000000000000000"
|
|
"0000000000000000000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF00"
|
|
"0F0F080000FFFF0F19000003E80164000003E801FF00190028001400000000000"
|
|
"00000000000000000000000000000000F03E7FFFF00FEFFFF0000FFFF0000FFFF"
|
|
"0000FFFF001E0F0B0000FFFF0F19000003E80164000003E801FF001E002800010"
|
|
"0060050000000000000000000000000000002FF02FF01FBFFFF0525FFFF00C5FF"
|
|
"FF03F5FFFF05F3FFFF002D0F130000FFFF0F19000003E80164000003E801FF001"
|
|
"9000A0005000500190000000000000000000000000000000000FF0200FF780000"
|
|
"FFFF0000FFFF0000FFFF0000FFFF003C0F040006FFFF0F19000003E8016400000"
|
|
"3E801FF0028000500000000000000000000000000000000000000000000000F00"
|
|
"00FFFF01FDFFFF03FFFFFF00FAFFFF01CE10FF004B0F0F0000FFFF0F19000003E"
|
|
"80164000003E801FF00280004001E001E00000000000000000000000000000000"
|
|
"00000000007800780000FFFF0000FFFF0000FFFF0000FFFF01000F030000FFFF0"
|
|
"F19000003E80164000003E801FF00320064000000000000000000000000000000"
|
|
"00000000000000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF010F0F0"
|
|
"80000FFFF0F19000003E80164000003E801FF0019002800140000000000000000"
|
|
"000000000000000000000000000F03E7FFFF00FEFFFF0000FFFF0000FFFF0000F"
|
|
"FFF011E0F0B0000FFFF0F19000003E80164000003E801FF001E00280001000600"
|
|
"50000000000000000000000000000002FF02FF01FBFFFF0525FFFF00C5FFFF03F"
|
|
"5FFFF05F3FFFF012D0F130000FFFF0F19000003E80164000003E801FF0019000A"
|
|
"0005000500190000000000000000000000000000000000FF0200FF780000FFFF0"
|
|
"000FFFF0000FFFF0000FFFF013C0F040006FFFF0F19000003E80164000003E801"
|
|
"FF0028000500000000000000000000000000000000000000000000000F0000FFF"
|
|
"F01FDFFFF03FFFFFF00FAFFFF01CE10FF014B0F0F0000FFFF0F19000003E80164"
|
|
"000003E801FF00280004001E001E0000000000000000000000000000000000000"
|
|
"000007800780000FFFF0000FFFF0000FFFF0000FFFF0100001388138813881388"
|
|
"015E01AB59"
|
|
)
|
|
|
|
QUADRO_SAMPLE_STATUS_REPORT = bytes.fromhex(
|
|
"00035B72FF4000010000006504080000000100000013C5000000910032CBB0000"
|
|
"0000000000000FFD5FFD69B54FFD8A6FD5B977FFF7FFF06517FFF09597FFF7FFF"
|
|
"7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF7FFF13887FFF7FFF7FFF0300000000000"
|
|
"000000000000300000004B9000000000000000000000000000000271004B90000"
|
|
"0000000000000805BB04B900000000016400000015E004B900000000000000000"
|
|
"80000000003E800000000000003E827100000000003E805BB0000000003E815E0"
|
|
"0000000003E82710000A0000000E000000002710FF000001"
|
|
)
|
|
|
|
QUADRO_SAMPLE_CONTROL_REPORT = bytes.fromhex(
|
|
"00031C000000A9000002580514FAEC05DC0001F42710271007D00001F42710271"
|
|
"007D00001F42710271007D00001F42710271007D0000000FFFF0DAC057804B000"
|
|
"000028001400010AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4"
|
|
"C0E9C0EF50F460FA00000008C011801F4032004B0069008D40B680E4C1194152C"
|
|
"19281D7422102710004CD0FFFF0DAC057804B000000028001400010AF00A8C0AF"
|
|
"A0B4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4C0E9C0EF50F460FA00000008C"
|
|
"011801F4032004B0069008D40B680E4C1194152C19281D74221027100005BB000"
|
|
"30DAC057804B000000028001400010AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF8"
|
|
"0D480DA20DF20E4C0E9C0EF50F460FA00000008C011801F4032004B0069008D40"
|
|
"B680E4C1194152C19281D74221027100015E0FFFF0DAC057804B0000000280014"
|
|
"00010AF00A8C0AFA0B4A0BA40BF40C4E0C9D0CF80D480DA20DF20E4C0E9C0EF50"
|
|
"F460FA00000008C011801F4032004B0069008D40B680E4C1194152C19281D7422"
|
|
"102710FF000200000F030000FFFF0F19000003E80164000003E801FF003200640"
|
|
"0000000000000000000000000000000000000000000FFFF0000FFFF0000FFFF00"
|
|
"00FFFF0000FFFF0000FFFF000F0F080000FFFF0F19000003E80164000003E801F"
|
|
"F0019002800140000000000000000000000000000000000000000000F03E7FFFF"
|
|
"00FEFFFF0000FFFF0000FFFF0000FFFF001E0F0B0000FFFF0F19000003E801640"
|
|
"00003E801FF001E0028000100060050000000000000000000000000000002FF02"
|
|
"FF01FBFFFF0525FFFF00C5FFFF03F5FFFF05F3FFFF002D0F040006FFFF0F19000"
|
|
"003E80164000003E801FF00280005000000000000000000000000000000000000"
|
|
"00000000000F0000FFFF01FDFFFF03FFFFFF00FAFFFF01CE10FF003C0F040006F"
|
|
"FFF0F19000003E80164000003E801FF0028000200000000000000000000000000"
|
|
"000000000000000000000F03FFFFFF07D0FFFF0000FFFF0000FFFF0000FFFF004"
|
|
"B0F040006FFFF0F19000003E80164000003E801FF002800020000000000000000"
|
|
"0000000000000000000000000000000F01CEFFFF03FFFFFF0000FFFF0000FFFF0"
|
|
"000FFFF002D0F000006FFFF0F19000003E80164000003E8016400280002000000"
|
|
"00000000000000000000000000000000000000000F00FAFFFF01CE10FF0000FFF"
|
|
"F0000FFFF0000FFFF002D0F000006FFFF0F19000003E80164000003E801640028"
|
|
"000500000000000000000000000000000000000000000000000F0000FFFF01FDF"
|
|
"FFF03FFFFFF00FAFFFF01CE10FF0100E0A8"
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mockD5NextDevice():
|
|
device = _MockD5NextDevice()
|
|
dev = Aquacomputer(
|
|
device,
|
|
"Mock Aquacomputer D5 Next",
|
|
device_info=Aquacomputer._DEVICE_INFO[Aquacomputer._DEVICE_D5NEXT],
|
|
)
|
|
|
|
dev.connect()
|
|
return dev
|
|
|
|
|
|
class _MockD5NextDevice(MockHidapiDevice):
|
|
def __init__(self):
|
|
super().__init__(vendor_id=0x0C70, product_id=0xF00E)
|
|
|
|
self.preload_read(Report(1, D5NEXT_SAMPLE_STATUS_REPORT))
|
|
self.preload_read(Report(3, D5NEXT_SAMPLE_CONTROL_REPORT))
|
|
self.preload_read(Report(3, D5NEXT_SAMPLE_CONTROL_REPORT))
|
|
|
|
def read(self, length):
|
|
pre = super().read(length)
|
|
if pre:
|
|
return pre
|
|
|
|
return Report(1, D5NEXT_SAMPLE_STATUS_REPORT)
|
|
|
|
|
|
def test_d5next_connect(mockD5NextDevice):
|
|
def mock_open():
|
|
nonlocal opened
|
|
opened = True
|
|
|
|
mockD5NextDevice.device.open = mock_open
|
|
opened = False
|
|
|
|
with mockD5NextDevice.connect() as cm:
|
|
assert cm == mockD5NextDevice
|
|
assert opened
|
|
|
|
|
|
def test_d5next_initialize(mockD5NextDevice):
|
|
init_result = mockD5NextDevice.initialize()
|
|
|
|
# Verify firmware version
|
|
assert init_result[0][1] == 1023
|
|
|
|
# Verify serial number
|
|
assert init_result[1][1] == "03531-22908"
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_d5next_get_status_directly(mockD5NextDevice, has_hwmon, direct_access):
|
|
if has_hwmon:
|
|
mockD5NextDevice._hwmon = HwmonDevice(None, None)
|
|
|
|
got = mockD5NextDevice.get_status(direct_access=direct_access)
|
|
|
|
expected = [
|
|
("Liquid temperature", pytest.approx(25.1, 0.1), "°C"),
|
|
("Soft. Sensor 1", pytest.approx(50, 0.1), "°C"),
|
|
("Pump speed", 1976, "rpm"),
|
|
("Pump power", pytest.approx(2.58, 0.1), "W"),
|
|
("Pump voltage", pytest.approx(12.02, 0.1), "V"),
|
|
("Pump current", pytest.approx(0.21, 0.1), "A"),
|
|
("Fan speed", 365, "rpm"),
|
|
("Fan power", pytest.approx(0.38, 0.1), "W"),
|
|
("Fan voltage", pytest.approx(12.04, 0.1), "V"),
|
|
("Fan current", pytest.approx(0.03, 0.1), "A"),
|
|
("+5V voltage", pytest.approx(5.00, 0.1), "V"),
|
|
("+12V voltage", pytest.approx(12.04, 0.1), "V"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
def test_d5next_get_status_from_hwmon(mockD5NextDevice, tmp_path):
|
|
mockD5NextDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "temp1_input").write_text("25100\n") # Liquid temperature
|
|
(tmp_path / "fan1_input").write_text("1976\n") # Pump speed
|
|
(tmp_path / "power1_input").write_text("2580000\n") # Pump power
|
|
(tmp_path / "in0_input").write_text("12020\n") # Pump voltage
|
|
(tmp_path / "curr1_input").write_text("215\n") # Pump current
|
|
(tmp_path / "fan2_input").write_text("365\n") # Fan speed
|
|
(tmp_path / "power2_input").write_text("380000\n") # Fan power
|
|
(tmp_path / "in1_input").write_text("12040\n") # Fan voltage
|
|
(tmp_path / "curr2_input").write_text("31\n") # Fan current
|
|
(tmp_path / "in2_input").write_text("4990\n") # +5V voltage
|
|
(tmp_path / "in3_input").write_text("12040\n") # +12V voltage
|
|
(tmp_path / "temp2_input").write_text("50000\n") # Soft. Sensor 1 temperature
|
|
(tmp_path / "temp3_input").write_text("60000\n") # Soft. Sensor 2 temperature
|
|
(tmp_path / "temp4_input").write_text("50000\n") # Soft. Sensor 3 temperature
|
|
(tmp_path / "temp5_input").write_text("50000\n") # Soft. Sensor 4 temperature
|
|
(tmp_path / "temp6_input").write_text("50000\n") # Soft. Sensor 5 temperature
|
|
(tmp_path / "temp7_input").write_text("50000\n") # Soft. Sensor 6 temperature
|
|
(tmp_path / "temp8_input").write_text("50000\n") # Soft. Sensor 7 temperature
|
|
(tmp_path / "temp9_input").write_text("50000\n") # Soft. Sensor 8 temperature
|
|
|
|
got = mockD5NextDevice.get_status()
|
|
|
|
expected = [
|
|
("Liquid temperature", pytest.approx(25.1, 0.1), "°C"),
|
|
("Soft. Sensor 1", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 2", pytest.approx(60, 0.1), "°C"),
|
|
("Soft. Sensor 3", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 4", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 5", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 6", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 7", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 8", pytest.approx(50, 0.1), "°C"),
|
|
("Pump speed", 1976, "rpm"),
|
|
("Pump power", pytest.approx(2.58, 0.1), "W"),
|
|
("Pump voltage", pytest.approx(12.02, 0.1), "V"),
|
|
("Pump current", pytest.approx(0.21, 0.1), "A"),
|
|
("Fan speed", 365, "rpm"),
|
|
("Fan power", pytest.approx(0.38, 0.1), "W"),
|
|
("Fan voltage", pytest.approx(12.04, 0.1), "V"),
|
|
("Fan current", pytest.approx(0.03, 0.1), "A"),
|
|
("+5V voltage", pytest.approx(5.00, 0.1), "V"),
|
|
("+12V voltage", pytest.approx(12.04, 0.1), "V"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_d5next_set_fixed_speeds_directly(mockD5NextDevice, has_hwmon, direct_access, tmp_path):
|
|
"""For both test cases only direct access should be used"""
|
|
|
|
if has_hwmon:
|
|
mockD5NextDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "pwm1").write_text("0")
|
|
(tmp_path / "pwm1_enable").write_text("0")
|
|
(tmp_path / "pwm2").write_text("0")
|
|
(tmp_path / "pwm2_enable").write_text("0")
|
|
|
|
mockD5NextDevice.set_fixed_speed("pump", 84, direct_access=direct_access)
|
|
mockD5NextDevice.set_fixed_speed("fan", 50, direct_access=direct_access)
|
|
|
|
pump_report, fan_report = mockD5NextDevice.device.sent
|
|
|
|
assert pump_report.number == 3
|
|
assert pump_report.data[0x95:0x98] == [0, 32, 208] # 0, <8400>
|
|
assert fan_report.number == 3
|
|
assert fan_report.data[0x40:0x43] == [0, 19, 136] # 0, <5000>
|
|
|
|
# Assert that hwmon wasn't touched
|
|
if has_hwmon:
|
|
assert (tmp_path / "pwm1_enable").read_text() == "0"
|
|
assert (tmp_path / "pwm1").read_text() == "0"
|
|
assert (tmp_path / "pwm2_enable").read_text() == "0"
|
|
assert (tmp_path / "pwm2").read_text() == "0"
|
|
|
|
|
|
@pytest.mark.parametrize("has_support", [False, True])
|
|
def test_d5next_set_fixed_speeds_hwmon(mockD5NextDevice, has_support, tmp_path):
|
|
mockD5NextDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
|
|
if has_support:
|
|
(tmp_path / "pwm1").write_text("0\n")
|
|
(tmp_path / "pwm1_enable").write_text("0\n")
|
|
(tmp_path / "pwm2").write_text("0\n")
|
|
(tmp_path / "pwm2_enable").write_text("0\n")
|
|
|
|
mockD5NextDevice.set_fixed_speed("pump", 84)
|
|
mockD5NextDevice.set_fixed_speed("fan", 50)
|
|
|
|
if has_support:
|
|
assert (tmp_path / "pwm1_enable").read_text() == "1"
|
|
assert (tmp_path / "pwm1").read_text() == "214"
|
|
assert (tmp_path / "pwm2_enable").read_text() == "1"
|
|
assert (tmp_path / "pwm2").read_text() == "127"
|
|
else:
|
|
# Assert fallback to direct access
|
|
pump_report, fan_report = mockD5NextDevice.device.sent
|
|
|
|
assert pump_report.number == 3
|
|
assert pump_report.data[0x95:0x98] == [0, 32, 208] # 0, <8400>
|
|
assert fan_report.number == 3
|
|
assert fan_report.data[0x40:0x43] == [0, 19, 136] # 0, <5000>
|
|
|
|
|
|
def test_d5next_speed_profiles_not_supported(mockD5NextDevice):
|
|
with pytest.raises(NotSupportedByDriver):
|
|
mockD5NextDevice.set_speed_profile("fan", None)
|
|
|
|
with pytest.raises(NotSupportedByDriver):
|
|
mockD5NextDevice.set_speed_profile("pump", None)
|
|
|
|
|
|
@pytest.fixture
|
|
def mockFarbwerk360Device():
|
|
device = _MockFarbwerk360Device()
|
|
dev = Aquacomputer(
|
|
device,
|
|
"Mock Aquacomputer Farbwerk 360",
|
|
device_info=Aquacomputer._DEVICE_INFO[Aquacomputer._DEVICE_FARBWERK360],
|
|
)
|
|
|
|
dev.connect()
|
|
return dev
|
|
|
|
|
|
class _MockFarbwerk360Device(MockHidapiDevice):
|
|
def __init__(self):
|
|
super().__init__(vendor_id=0x0C70, product_id=0xF010)
|
|
|
|
self.preload_read(Report(1, FARBWERK360_SAMPLE_STATUS_REPORT))
|
|
|
|
def read(self, length):
|
|
pre = super().read(length)
|
|
if pre:
|
|
return pre
|
|
|
|
return Report(1, FARBWERK360_SAMPLE_STATUS_REPORT)
|
|
|
|
|
|
def test_farbwerk360_connect(mockFarbwerk360Device):
|
|
def mock_open():
|
|
nonlocal opened
|
|
opened = True
|
|
|
|
mockFarbwerk360Device.device.open = mock_open
|
|
opened = False
|
|
|
|
with mockFarbwerk360Device.connect() as cm:
|
|
assert cm == mockFarbwerk360Device
|
|
assert opened
|
|
|
|
|
|
def test_farbwerk360_initialize(mockFarbwerk360Device):
|
|
init_result = mockFarbwerk360Device.initialize()
|
|
|
|
# Verify firmware version
|
|
assert init_result[0][1] == 1022
|
|
|
|
# Verify serial number
|
|
assert init_result[1][1] == "16827-56978"
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_farbwerk360_get_status_directly(mockFarbwerk360Device, has_hwmon, direct_access):
|
|
if has_hwmon:
|
|
mockFarbwerk360Device._hwmon = HwmonDevice(None, None)
|
|
|
|
got = mockFarbwerk360Device.get_status(direct_access=direct_access)
|
|
|
|
expected = [
|
|
("Sensor 1", pytest.approx(26.2, 0.1), "°C"),
|
|
("Sensor 2", pytest.approx(26.3, 0.1), "°C"),
|
|
("Sensor 3", pytest.approx(26.7, 0.1), "°C"),
|
|
("Sensor 4", pytest.approx(25.5, 0.1), "°C"),
|
|
("Soft. Sensor 1", pytest.approx(52.00, 0.1), "°C"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
def test_farbwerk360_get_status_from_hwmon(mockFarbwerk360Device, tmp_path):
|
|
mockFarbwerk360Device._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "temp1_input").write_text("26200\n")
|
|
(tmp_path / "temp2_input").write_text("26310\n")
|
|
(tmp_path / "temp3_input").write_text("26710\n")
|
|
(tmp_path / "temp4_input").write_text("25520\n")
|
|
(tmp_path / "temp5_input").write_text("50000\n") # Soft. Sensor 1 temperature
|
|
(tmp_path / "temp6_input").write_text("60000\n") # Soft. Sensor 2 temperature
|
|
(tmp_path / "temp7_input").write_text("50000\n") # Soft. Sensor 3 temperature
|
|
(tmp_path / "temp8_input").write_text("50000\n") # Soft. Sensor 4 temperature
|
|
(tmp_path / "temp9_input").write_text("50000\n") # Soft. Sensor 5 temperature
|
|
(tmp_path / "temp10_input").write_text("50000\n") # Soft. Sensor 6 temperature
|
|
(tmp_path / "temp11_input").write_text("50000\n") # Soft. Sensor 7 temperature
|
|
(tmp_path / "temp12_input").write_text("50000\n") # Soft. Sensor 8 temperature
|
|
(tmp_path / "temp13_input").write_text("50000\n") # Soft. Sensor 9 temperature
|
|
(tmp_path / "temp14_input").write_text("50000\n") # Soft. Sensor 10 temperature
|
|
(tmp_path / "temp15_input").write_text("50000\n") # Soft. Sensor 11 temperature
|
|
(tmp_path / "temp16_input").write_text("50000\n") # Soft. Sensor 12 temperature
|
|
(tmp_path / "temp17_input").write_text("50000\n") # Soft. Sensor 13 temperature
|
|
(tmp_path / "temp18_input").write_text("50000\n") # Soft. Sensor 14 temperature
|
|
(tmp_path / "temp19_input").write_text("50000\n") # Soft. Sensor 15 temperature
|
|
(tmp_path / "temp20_input").write_text("50000\n") # Soft. Sensor 16 temperature
|
|
|
|
got = mockFarbwerk360Device.get_status()
|
|
|
|
expected = [
|
|
("Sensor 1", pytest.approx(26.2, 0.1), "°C"),
|
|
("Sensor 2", pytest.approx(26.3, 0.1), "°C"),
|
|
("Sensor 3", pytest.approx(26.7, 0.1), "°C"),
|
|
("Sensor 4", pytest.approx(25.5, 0.1), "°C"),
|
|
("Soft. Sensor 1", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 2", pytest.approx(60, 0.1), "°C"),
|
|
("Soft. Sensor 3", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 4", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 5", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 6", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 7", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 8", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 9", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 10", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 11", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 12", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 13", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 14", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 15", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 16", pytest.approx(50, 0.1), "°C"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
def test_farbwerk360_set_fixed_speeds_not_supported(mockFarbwerk360Device):
|
|
with pytest.raises(NotSupportedByDevice):
|
|
mockFarbwerk360Device.set_fixed_speed("fan", 42)
|
|
|
|
|
|
def test_farbwerk360_speed_profiles_not_supported(mockFarbwerk360Device):
|
|
with pytest.raises(NotSupportedByDevice):
|
|
mockFarbwerk360Device.set_speed_profile("fan", None)
|
|
|
|
|
|
@pytest.fixture
|
|
def mockOctoDevice():
|
|
device = _MockOctoDevice()
|
|
dev = Aquacomputer(
|
|
device,
|
|
"Mock Aquacomputer Octo",
|
|
device_info=Aquacomputer._DEVICE_INFO[Aquacomputer._DEVICE_OCTO],
|
|
)
|
|
|
|
dev.connect()
|
|
return dev
|
|
|
|
|
|
class _MockOctoDevice(MockHidapiDevice):
|
|
def __init__(self):
|
|
super().__init__(vendor_id=0x0C70, product_id=0xF011)
|
|
|
|
self.preload_read(Report(1, OCTO_SAMPLE_STATUS_REPORT))
|
|
self.preload_read(Report(3, OCTO_SAMPLE_CONTROL_REPORT))
|
|
self.preload_read(Report(3, OCTO_SAMPLE_CONTROL_REPORT))
|
|
|
|
def read(self, length):
|
|
pre = super().read(length)
|
|
if pre:
|
|
return pre
|
|
|
|
return Report(1, OCTO_SAMPLE_STATUS_REPORT)
|
|
|
|
|
|
def test_octo_connect(mockOctoDevice):
|
|
def mock_open():
|
|
nonlocal opened
|
|
opened = True
|
|
|
|
mockOctoDevice.device.open = mock_open
|
|
opened = False
|
|
|
|
with mockOctoDevice.connect() as cm:
|
|
assert cm == mockOctoDevice
|
|
assert opened
|
|
|
|
|
|
def test_octo_initialize(mockOctoDevice):
|
|
init_result = mockOctoDevice.initialize()
|
|
|
|
# Verify firmware version
|
|
assert init_result[0][1] == 1019
|
|
|
|
# Verify serial number
|
|
assert init_result[1][1] == "14994-51690"
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_octo_get_status_directly(mockOctoDevice, has_hwmon, direct_access):
|
|
if has_hwmon:
|
|
mockOctoDevice._hwmon = HwmonDevice(None, None)
|
|
|
|
got = mockOctoDevice.get_status(direct_access=direct_access)
|
|
|
|
expected = [
|
|
("Sensor 1", pytest.approx(27.5, 0.1), "°C"),
|
|
("Sensor 2", pytest.approx(27.7, 0.1), "°C"),
|
|
("Sensor 3", pytest.approx(28.4, 0.1), "°C"),
|
|
("Sensor 4", pytest.approx(34.2, 0.1), "°C"),
|
|
("Soft. Sensor 1", pytest.approx(37.84, 0.1), "°C"),
|
|
("Fan 1 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 1 power", pytest.approx(0.01, 0.1), "W"),
|
|
("Fan 1 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 1 current", pytest.approx(0.001, 0.1), "A"),
|
|
("Fan 2 speed", pytest.approx(576, 0.1), "rpm"),
|
|
("Fan 2 power", pytest.approx(1.03, 0.1), "W"),
|
|
("Fan 2 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 2 current", pytest.approx(0.35, 0.1), "A"),
|
|
("Fan 3 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 3 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 3 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 3 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 4 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 4 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 4 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 4 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 5 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 5 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 5 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 5 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 6 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 6 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 6 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 6 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 7 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 7 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 7 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 7 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 8 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 8 power", pytest.approx(0.02, 0.1), "W"),
|
|
("Fan 8 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 8 current", pytest.approx(0.002, 0.1), "A"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
def test_octo_get_status_from_hwmon(mockOctoDevice, tmp_path):
|
|
mockOctoDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "temp1_input").write_text("27580\n")
|
|
(tmp_path / "temp2_input").write_text("27670\n")
|
|
(tmp_path / "temp3_input").write_text("28370\n")
|
|
(tmp_path / "temp4_input").write_text("34240\n")
|
|
(tmp_path / "fan1_input").write_text("0\n")
|
|
(tmp_path / "power1_input").write_text("10000\n")
|
|
(tmp_path / "in0_input").write_text("12090\n")
|
|
(tmp_path / "curr1_input").write_text("1\n")
|
|
(tmp_path / "fan2_input").write_text("576\n")
|
|
(tmp_path / "power2_input").write_text("1030000\n")
|
|
(tmp_path / "in1_input").write_text("12090\n")
|
|
(tmp_path / "curr2_input").write_text("350\n")
|
|
(tmp_path / "fan3_input").write_text("0\n")
|
|
(tmp_path / "power3_input").write_text("0\n")
|
|
(tmp_path / "in2_input").write_text("0\n")
|
|
(tmp_path / "curr3_input").write_text("0\n")
|
|
(tmp_path / "fan4_input").write_text("0\n")
|
|
(tmp_path / "power4_input").write_text("0\n")
|
|
(tmp_path / "in3_input").write_text("0\n")
|
|
(tmp_path / "curr4_input").write_text("0\n")
|
|
(tmp_path / "fan5_input").write_text("0\n")
|
|
(tmp_path / "power5_input").write_text("0\n")
|
|
(tmp_path / "in4_input").write_text("0\n")
|
|
(tmp_path / "curr5_input").write_text("0\n")
|
|
(tmp_path / "fan6_input").write_text("0\n")
|
|
(tmp_path / "power6_input").write_text("0\n")
|
|
(tmp_path / "in5_input").write_text("0\n")
|
|
(tmp_path / "curr6_input").write_text("0\n")
|
|
(tmp_path / "fan7_input").write_text("0\n")
|
|
(tmp_path / "power7_input").write_text("0\n")
|
|
(tmp_path / "in6_input").write_text("0\n")
|
|
(tmp_path / "curr7_input").write_text("0\n")
|
|
(tmp_path / "fan8_input").write_text("0\n")
|
|
(tmp_path / "power8_input").write_text("20000\n")
|
|
(tmp_path / "in7_input").write_text("12090\n")
|
|
(tmp_path / "curr8_input").write_text("2\n")
|
|
(tmp_path / "temp5_input").write_text("50000\n") # Soft. Sensor 1 temperature
|
|
(tmp_path / "temp6_input").write_text("60000\n") # Soft. Sensor 2 temperature
|
|
(tmp_path / "temp7_input").write_text("50000\n") # Soft. Sensor 3 temperature
|
|
(tmp_path / "temp8_input").write_text("50000\n") # Soft. Sensor 4 temperature
|
|
(tmp_path / "temp9_input").write_text("50000\n") # Soft. Sensor 5 temperature
|
|
(tmp_path / "temp10_input").write_text("50000\n") # Soft. Sensor 6 temperature
|
|
(tmp_path / "temp11_input").write_text("50000\n") # Soft. Sensor 7 temperature
|
|
(tmp_path / "temp12_input").write_text("50000\n") # Soft. Sensor 8 temperature
|
|
(tmp_path / "temp13_input").write_text("50000\n") # Soft. Sensor 9 temperature
|
|
(tmp_path / "temp14_input").write_text("50000\n") # Soft. Sensor 10 temperature
|
|
(tmp_path / "temp15_input").write_text("50000\n") # Soft. Sensor 11 temperature
|
|
(tmp_path / "temp16_input").write_text("50000\n") # Soft. Sensor 12 temperature
|
|
(tmp_path / "temp17_input").write_text("50000\n") # Soft. Sensor 13 temperature
|
|
(tmp_path / "temp18_input").write_text("50000\n") # Soft. Sensor 14 temperature
|
|
(tmp_path / "temp19_input").write_text("50000\n") # Soft. Sensor 15 temperature
|
|
(tmp_path / "temp20_input").write_text("50000\n") # Soft. Sensor 16 temperature
|
|
|
|
got = mockOctoDevice.get_status()
|
|
|
|
expected = [
|
|
("Sensor 1", pytest.approx(27.5, 0.1), "°C"),
|
|
("Sensor 2", pytest.approx(27.7, 0.1), "°C"),
|
|
("Sensor 3", pytest.approx(28.4, 0.1), "°C"),
|
|
("Sensor 4", pytest.approx(34.2, 0.1), "°C"),
|
|
("Fan 1 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 1 power", pytest.approx(0.01, 0.1), "W"),
|
|
("Fan 1 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 1 current", pytest.approx(0.001, 0.1), "A"),
|
|
("Fan 2 speed", pytest.approx(576, 0.1), "rpm"),
|
|
("Fan 2 power", pytest.approx(1.03, 0.1), "W"),
|
|
("Fan 2 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 2 current", pytest.approx(0.35, 0.1), "A"),
|
|
("Fan 3 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 3 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 3 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 3 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 4 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 4 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 4 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 4 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 5 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 5 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 5 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 5 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 6 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 6 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 6 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 6 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 7 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 7 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 7 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 7 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 8 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 8 power", pytest.approx(0.02, 0.1), "W"),
|
|
("Fan 8 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 8 current", pytest.approx(0.002, 0.1), "A"),
|
|
("Soft. Sensor 1", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 2", pytest.approx(60, 0.1), "°C"),
|
|
("Soft. Sensor 3", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 4", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 5", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 6", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 7", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 8", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 9", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 10", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 11", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 12", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 13", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 14", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 15", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 16", pytest.approx(50, 0.1), "°C"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_octo_set_fixed_speeds_directly(mockOctoDevice, has_hwmon, direct_access, tmp_path):
|
|
"""For both test cases only direct access should be used"""
|
|
|
|
if has_hwmon:
|
|
mockOctoDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "pwm1").write_text("0")
|
|
(tmp_path / "pwm1_enable").write_text("0")
|
|
(tmp_path / "pwm2").write_text("0")
|
|
(tmp_path / "pwm2_enable").write_text("0")
|
|
|
|
mockOctoDevice.set_fixed_speed("fan1", 84, direct_access=direct_access)
|
|
mockOctoDevice.set_fixed_speed("fan2", 50, direct_access=direct_access)
|
|
|
|
pump_report, fan_report = mockOctoDevice.device.sent
|
|
|
|
assert pump_report.number == 3
|
|
assert pump_report.data[0x59:0x5C] == [0, 32, 208] # 0, <8400>
|
|
assert fan_report.number == 3
|
|
assert fan_report.data[0xAE:0xB1] == [0, 19, 136] # 0, <5000>
|
|
|
|
# Assert that hwmon wasn't touched
|
|
if has_hwmon:
|
|
assert (tmp_path / "pwm1_enable").read_text() == "0"
|
|
assert (tmp_path / "pwm1").read_text() == "0"
|
|
assert (tmp_path / "pwm2_enable").read_text() == "0"
|
|
assert (tmp_path / "pwm2").read_text() == "0"
|
|
|
|
|
|
@pytest.mark.parametrize("has_support", [False, True])
|
|
def test_octo_set_fixed_speeds_hwmon(mockOctoDevice, has_support, tmp_path):
|
|
mockOctoDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
|
|
if has_support:
|
|
(tmp_path / "pwm1").write_text("0\n")
|
|
(tmp_path / "pwm1_enable").write_text("0\n")
|
|
(tmp_path / "pwm2").write_text("0\n")
|
|
(tmp_path / "pwm2_enable").write_text("0\n")
|
|
|
|
mockOctoDevice.set_fixed_speed("fan1", 84)
|
|
mockOctoDevice.set_fixed_speed("fan2", 50)
|
|
|
|
if has_support:
|
|
assert (tmp_path / "pwm1_enable").read_text() == "1"
|
|
assert (tmp_path / "pwm1").read_text() == "214"
|
|
assert (tmp_path / "pwm2_enable").read_text() == "1"
|
|
assert (tmp_path / "pwm2").read_text() == "127"
|
|
else:
|
|
# Assert fallback to direct access
|
|
pump_report, fan_report = mockOctoDevice.device.sent
|
|
|
|
assert pump_report.number == 3
|
|
assert pump_report.data[0x59:0x5C] == [0, 32, 208] # 0, <8400>
|
|
assert fan_report.number == 3
|
|
assert fan_report.data[0xAE:0xB1] == [0, 19, 136] # 0, <5000>
|
|
|
|
|
|
def test_octo_speed_profiles_not_supported(mockOctoDevice):
|
|
with pytest.raises(NotSupportedByDriver):
|
|
mockOctoDevice.set_speed_profile("fan", None)
|
|
|
|
|
|
@pytest.fixture
|
|
def mockQuadroDevice():
|
|
device = _MockQuadroDevice()
|
|
dev = Aquacomputer(
|
|
device,
|
|
"Mock Aquacomputer Quadro",
|
|
device_info=Aquacomputer._DEVICE_INFO[Aquacomputer._DEVICE_QUADRO],
|
|
)
|
|
|
|
dev.connect()
|
|
return dev
|
|
|
|
|
|
class _MockQuadroDevice(MockHidapiDevice):
|
|
def __init__(self):
|
|
super().__init__(vendor_id=0x0C70, product_id=0xF00D)
|
|
|
|
self.preload_read(Report(1, QUADRO_SAMPLE_STATUS_REPORT))
|
|
self.preload_read(Report(3, QUADRO_SAMPLE_CONTROL_REPORT))
|
|
self.preload_read(Report(3, QUADRO_SAMPLE_CONTROL_REPORT))
|
|
|
|
def read(self, length):
|
|
pre = super().read(length)
|
|
if pre:
|
|
return pre
|
|
|
|
return Report(1, QUADRO_SAMPLE_STATUS_REPORT)
|
|
|
|
|
|
def test_quadro_connect(mockQuadroDevice):
|
|
def mock_open():
|
|
nonlocal opened
|
|
opened = True
|
|
|
|
mockQuadroDevice.device.open = mock_open
|
|
opened = False
|
|
|
|
with mockQuadroDevice.connect() as cm:
|
|
assert cm == mockQuadroDevice
|
|
assert opened
|
|
|
|
|
|
def test_quadro_initialize(mockQuadroDevice):
|
|
init_result = mockQuadroDevice.initialize()
|
|
|
|
# Verify firmware version
|
|
assert init_result[0][1] == 1032
|
|
|
|
# Verify serial number
|
|
assert init_result[1][1] == "23410-65344"
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_quadro_get_status_directly(mockQuadroDevice, has_hwmon, direct_access):
|
|
if has_hwmon:
|
|
mockQuadroDevice._hwmon = HwmonDevice(None, None)
|
|
|
|
got = mockQuadroDevice.get_status(direct_access=direct_access)
|
|
|
|
expected = [
|
|
("Sensor 3", pytest.approx(16.17, 0.1), "°C"),
|
|
("Soft. Sensor 1", pytest.approx(23.9, 0.1), "°C"),
|
|
("Soft. Sensor 13", pytest.approx(50.0, 0.1), "°C"),
|
|
("Fan 1 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 1 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 1 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 1 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 2 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 2 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 2 voltage", pytest.approx(12.07, 0.1), "V"),
|
|
("Fan 2 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 3 speed", pytest.approx(356, 0.1), "rpm"),
|
|
("Fan 3 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 3 voltage", pytest.approx(12.07, 0.1), "V"),
|
|
("Fan 3 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 4 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 4 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 4 voltage", pytest.approx(12.07, 0.1), "V"),
|
|
("Fan 4 current", pytest.approx(0, 0.1), "A"),
|
|
("Flow sensor", pytest.approx(0, 0.1), "dL/h"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
def test_quadro_get_status_from_hwmon(mockQuadroDevice, tmp_path):
|
|
mockQuadroDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "temp1_input").write_text("27580\n")
|
|
(tmp_path / "temp2_input").write_text("27670\n")
|
|
(tmp_path / "temp3_input").write_text("28370\n")
|
|
(tmp_path / "temp4_input").write_text("34240\n")
|
|
(tmp_path / "fan1_input").write_text("0\n")
|
|
(tmp_path / "power1_input").write_text("10000\n")
|
|
(tmp_path / "in0_input").write_text("12090\n")
|
|
(tmp_path / "curr1_input").write_text("1\n")
|
|
(tmp_path / "fan2_input").write_text("576\n")
|
|
(tmp_path / "power2_input").write_text("1030000\n")
|
|
(tmp_path / "in1_input").write_text("12090\n")
|
|
(tmp_path / "curr2_input").write_text("350\n")
|
|
(tmp_path / "fan3_input").write_text("0\n")
|
|
(tmp_path / "power3_input").write_text("0\n")
|
|
(tmp_path / "in2_input").write_text("0\n")
|
|
(tmp_path / "curr3_input").write_text("0\n")
|
|
(tmp_path / "fan4_input").write_text("0\n")
|
|
(tmp_path / "power4_input").write_text("0\n")
|
|
(tmp_path / "in3_input").write_text("0\n")
|
|
(tmp_path / "curr4_input").write_text("0\n")
|
|
(tmp_path / "fan5_input").write_text("603\n")
|
|
(tmp_path / "temp5_input").write_text("50000\n") # Soft. Sensor 1 temperature
|
|
(tmp_path / "temp6_input").write_text("60000\n") # Soft. Sensor 2 temperature
|
|
(tmp_path / "temp7_input").write_text("50000\n") # Soft. Sensor 3 temperature
|
|
(tmp_path / "temp8_input").write_text("50000\n") # Soft. Sensor 4 temperature
|
|
(tmp_path / "temp9_input").write_text("50000\n") # Soft. Sensor 5 temperature
|
|
(tmp_path / "temp10_input").write_text("50000\n") # Soft. Sensor 6 temperature
|
|
(tmp_path / "temp11_input").write_text("50000\n") # Soft. Sensor 7 temperature
|
|
(tmp_path / "temp12_input").write_text("50000\n") # Soft. Sensor 8 temperature
|
|
(tmp_path / "temp13_input").write_text("50000\n") # Soft. Sensor 9 temperature
|
|
(tmp_path / "temp14_input").write_text("50000\n") # Soft. Sensor 10 temperature
|
|
(tmp_path / "temp15_input").write_text("50000\n") # Soft. Sensor 11 temperature
|
|
(tmp_path / "temp16_input").write_text("50000\n") # Soft. Sensor 12 temperature
|
|
(tmp_path / "temp17_input").write_text("50000\n") # Soft. Sensor 13 temperature
|
|
(tmp_path / "temp18_input").write_text("50000\n") # Soft. Sensor 14 temperature
|
|
(tmp_path / "temp19_input").write_text("50000\n") # Soft. Sensor 15 temperature
|
|
(tmp_path / "temp20_input").write_text("50000\n") # Soft. Sensor 16 temperature
|
|
|
|
got = mockQuadroDevice.get_status()
|
|
|
|
expected = [
|
|
("Sensor 1", pytest.approx(27.5, 0.1), "°C"),
|
|
("Sensor 2", pytest.approx(27.7, 0.1), "°C"),
|
|
("Sensor 3", pytest.approx(28.4, 0.1), "°C"),
|
|
("Sensor 4", pytest.approx(34.2, 0.1), "°C"),
|
|
("Fan 1 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 1 power", pytest.approx(0.01, 0.1), "W"),
|
|
("Fan 1 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 1 current", pytest.approx(0.001, 0.1), "A"),
|
|
("Fan 2 speed", pytest.approx(576, 0.1), "rpm"),
|
|
("Fan 2 power", pytest.approx(1.03, 0.1), "W"),
|
|
("Fan 2 voltage", pytest.approx(12.09, 0.1), "V"),
|
|
("Fan 2 current", pytest.approx(0.35, 0.1), "A"),
|
|
("Fan 3 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 3 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 3 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 3 current", pytest.approx(0, 0.1), "A"),
|
|
("Fan 4 speed", pytest.approx(0, 0.1), "rpm"),
|
|
("Fan 4 power", pytest.approx(0, 0.1), "W"),
|
|
("Fan 4 voltage", pytest.approx(0, 0.1), "V"),
|
|
("Fan 4 current", pytest.approx(0, 0.1), "A"),
|
|
("Flow sensor", pytest.approx(603, 0.1), "dL/h"),
|
|
("Soft. Sensor 1", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 2", pytest.approx(60, 0.1), "°C"),
|
|
("Soft. Sensor 3", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 4", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 5", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 6", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 7", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 8", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 9", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 10", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 11", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 12", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 13", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 14", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 15", pytest.approx(50, 0.1), "°C"),
|
|
("Soft. Sensor 16", pytest.approx(50, 0.1), "°C"),
|
|
]
|
|
|
|
assert sorted(got) == sorted(expected)
|
|
|
|
|
|
@pytest.mark.parametrize("has_hwmon,direct_access", [(False, False), (True, True)])
|
|
def test_quadro_set_fixed_speeds_directly(mockQuadroDevice, has_hwmon, direct_access, tmp_path):
|
|
"""For both test cases only direct access should be used"""
|
|
|
|
if has_hwmon:
|
|
mockQuadroDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
(tmp_path / "pwm1").write_text("0")
|
|
(tmp_path / "pwm1_enable").write_text("0")
|
|
(tmp_path / "pwm2").write_text("0")
|
|
(tmp_path / "pwm2_enable").write_text("0")
|
|
|
|
mockQuadroDevice.set_fixed_speed("fan1", 84, direct_access=direct_access)
|
|
mockQuadroDevice.set_fixed_speed("fan2", 50, direct_access=direct_access)
|
|
|
|
pump_report, fan_report = mockQuadroDevice.device.sent
|
|
|
|
assert pump_report.number == 3
|
|
assert pump_report.data[0x35:0x38] == [0, 32, 208] # 0, <8400>
|
|
assert fan_report.number == 3
|
|
assert fan_report.data[0x8A:0x8D] == [0, 19, 136] # 0, <5000>
|
|
|
|
# Assert that hwmon wasn't touched
|
|
if has_hwmon:
|
|
assert (tmp_path / "pwm1_enable").read_text() == "0"
|
|
assert (tmp_path / "pwm1").read_text() == "0"
|
|
assert (tmp_path / "pwm2_enable").read_text() == "0"
|
|
assert (tmp_path / "pwm2").read_text() == "0"
|
|
|
|
|
|
@pytest.mark.parametrize("has_support", [False, True])
|
|
def test_quadro_set_fixed_speeds_hwmon(mockQuadroDevice, has_support, tmp_path):
|
|
mockQuadroDevice._hwmon = HwmonDevice("mock_module", tmp_path)
|
|
|
|
if has_support:
|
|
(tmp_path / "pwm1").write_text("0\n")
|
|
(tmp_path / "pwm1_enable").write_text("0\n")
|
|
(tmp_path / "pwm2").write_text("0\n")
|
|
(tmp_path / "pwm2_enable").write_text("0\n")
|
|
|
|
mockQuadroDevice.set_fixed_speed("fan1", 84)
|
|
mockQuadroDevice.set_fixed_speed("fan2", 50)
|
|
|
|
if has_support:
|
|
assert (tmp_path / "pwm1_enable").read_text() == "1"
|
|
assert (tmp_path / "pwm1").read_text() == "214"
|
|
assert (tmp_path / "pwm2_enable").read_text() == "1"
|
|
assert (tmp_path / "pwm2").read_text() == "127"
|
|
else:
|
|
# Assert fallback to direct access
|
|
pump_report, fan_report = mockQuadroDevice.device.sent
|
|
|
|
assert pump_report.number == 3
|
|
assert pump_report.data[0x35:0x38] == [0, 32, 208] # 0, <8400>
|
|
assert fan_report.number == 3
|
|
assert fan_report.data[0x8A:0x8D] == [0, 19, 136] # 0, <5000>
|
|
|
|
|
|
def test_quadro_speed_profiles_not_supported(mockQuadroDevice):
|
|
with pytest.raises(NotSupportedByDriver):
|
|
mockQuadroDevice.set_speed_profile("fan", None)
|