Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Over The Air device config feature #48

Merged
merged 29 commits into from
Nov 6, 2024
Merged
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
95e628c
Added remote update function and minimally working
t-sasatani Oct 23, 2024
e88e208
Formatting
t-sasatani Oct 24, 2024
ada51cb
ROI/gain/LED value remote update prototype working
t-sasatani Oct 27, 2024
e705b69
allow multi-word value transfer with headers
t-sasatani Oct 27, 2024
7ad77ad
device ID-based over-the-air update
t-sasatani Oct 27, 2024
afdbc97
Add device commands (mainly for restart)
t-sasatani Nov 1, 2024
a560a48
Isolate models in a separate file
t-sasatani Nov 1, 2024
7ac453e
add devupdate model tests
t-sasatani Nov 1, 2024
c1ea61a
Fix model tests
t-sasatani Nov 1, 2024
8782cd2
update format
t-sasatani Nov 1, 2024
9fff132
moved cmd constants to model file
t-sasatani Nov 1, 2024
3fe1108
fix definitions
t-sasatani Nov 1, 2024
3bbed78
Fix remote reboot command
t-sasatani Nov 2, 2024
41b50fe
minor fix for model and modeltest
t-sasatani Nov 2, 2024
d4212a2
bundle ota update with yaml
t-sasatani Nov 2, 2024
70d806a
Add tests for device update
t-sasatani Nov 2, 2024
212df47
Add controller docs (mostly just placeholder)
t-sasatani Nov 2, 2024
3721008
update docs index, add update device tests
t-sasatani Nov 2, 2024
a5bceb0
remove duplicate functions
t-sasatani Nov 2, 2024
b913814
add pyserial to intersphinx mapping
sneakers-the-rat Nov 6, 2024
1ed6e46
Specify return type of FTDI devices
t-sasatani Nov 6, 2024
d9543f8
Update command key validation
t-sasatani Nov 6, 2024
a0116b6
Update update target not found error message
t-sasatani Nov 6, 2024
8214b60
separate device command, specify update device as key, snake case
t-sasatani Nov 6, 2024
fa1abfb
move FTDI constants to module, change UpdateTarget type
t-sasatani Nov 6, 2024
4db38cf
change update config to batch (name)
t-sasatani Nov 6, 2024
ef6d1db
add experimental feature note
t-sasatani Nov 6, 2024
e3bba77
make update test with parameterized fixtures
t-sasatani Nov 6, 2024
7e0a836
Merge branch 'main' into feature_ir_update
t-sasatani Nov 6, 2024
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
Prev Previous commit
Next Next commit
make update test with parameterized fixtures
t-sasatani committed Nov 6, 2024
commit e3bba77fa57fd9d43b713686176118bb732bb380
103 changes: 56 additions & 47 deletions tests/test_device_update.py
Original file line number Diff line number Diff line change
@@ -5,18 +5,24 @@
from miniscope_io.models.devupdate import UpdateCommandDefinitions, UpdateTarget
from miniscope_io.device_update import device_update, find_ftdi_device


@patch("miniscope_io.device_update.serial.tools.list_ports.comports")
@patch("miniscope_io.device_update.serial.Serial")
def test_devupdate_with_device_connected(mock_serial, mock_comports):
@pytest.fixture
def mock_serial_fixture(request):
device_list = request.param
with patch('serial.Serial') as mock_serial, patch('serial.tools.list_ports.comports') as mock_comports:
mock_serial_instance = mock_serial.return_value
mock_comports.return_value = [MagicMock(vid=device['vid'], pid=device['pid'], device=device['device'])
for device in device_list]
yield mock_serial, mock_comports, mock_serial_instance

@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM3'},
{'vid': 0x0111, 'pid': 0x6111, 'device': 'COM2'}],
], indirect=True)
def test_devupdate_with_device_connected(mock_serial_fixture):
"""
Test device_update function with a device connected.
"""
mock_serial_instance = mock_serial.return_value
mock_comports.return_value = [
MagicMock(vid=0x0403, pid=0x6001, device="COM3"),
MagicMock(vid=0x1234, pid=0x5678, device="COM4"),
]
mock_serial, mock_comports, mock_serial_instance = mock_serial_fixture
target = "LED"
value = 2
device_id = 1
@@ -47,43 +53,42 @@ def test_devupdate_with_device_connected(mock_serial, mock_comports):
assert mock_serial_instance.write.call_count == len(expected_calls)
mock_serial_instance.write.assert_has_calls(expected_calls, any_order=False)

@patch("miniscope_io.device_update.serial.tools.list_ports.comports")
def test_devupdate_without_device_connected(mock_comports):

@pytest.mark.parametrize('mock_serial_fixture', [
[],
], indirect=True)
def test_devupdate_without_device_connected(mock_serial_fixture):
"""
Test device_update function without a device connected.
"""
mock_comports.return_value = [
]

target = "GAIN"
value = 2
device_id = 0

with pytest.raises(ValueError, match="No FTDI devices found."):
device_update(target, value, device_id)

@patch("miniscope_io.device_update.serial.tools.list_ports.comports")
def test_find_ftdi_device(mock_comports):
@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM3'},
{'vid': 0x0111, 'pid': 0x6111, 'device': 'COM2'}],
], indirect=True)
def test_find_ftdi_device(mock_serial_fixture):
"""
Test find_ftdi_device function.
"""
mock_comports.return_value = [
MagicMock(vid=0x0403, pid=0x6001, device="COM3"),
MagicMock(vid=0x1234, pid=0x5678, device="COM4"),
]

result = find_ftdi_device()

assert result == ["COM3"]

@patch("miniscope_io.models.devupdate.serial.tools.list_ports.comports")
def test_invalid_target_raises_error(mock_comports):
@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM3'},
{'vid': 0x0111, 'pid': 0x6111, 'device': 'COM2'}],
], indirect=True)
def test_invalid_target_raises_error(mock_serial_fixture):
"""
Test that an invalid target raises an error.
"""
mock_comports.return_value = [
MagicMock(vid=0x0403, pid=0x6001, device="COM3"),
MagicMock(vid=0x1234, pid=0x5678, device="COM4"),
]

target = "RANDOM_STRING"
value = 50
@@ -93,15 +98,16 @@ def test_invalid_target_raises_error(mock_comports):
with pytest.raises(ValidationError, match="Target RANDOM_STRING not found"):
device_update(target, value, device_id, port)

@patch("miniscope_io.models.devupdate.serial.tools.list_ports.comports")
def test_invalid_led_value_raises_error(mock_comports):
@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM3'},
{'vid': 0x0111, 'pid': 0x6111, 'device': 'COM2'}],
], indirect=True)
def test_invalid_led_value_raises_error(mock_serial_fixture):
"""
Test that an invalid LED value raises an error.
"""
mock_comports.return_value = [
MagicMock(vid=0x0403, pid=0x6001, device="COM3"),
MagicMock(vid=0x1234, pid=0x5678, device="COM4"),
]
mock_serial, mock_comports, mock_serial_instance = mock_serial_fixture

target = "LED"
value = 150 # LED value should be between 0 and 100
device_id = 1
@@ -110,16 +116,14 @@ def test_invalid_led_value_raises_error(mock_comports):
with pytest.raises(ValidationError, match="For LED, value must be between 0 and 100"):
device_update(target, value, device_id, port)


@patch("miniscope_io.device_update.serial.tools.list_ports.comports")
def test_devupdate_with_multiple_ftdi_devices(mock_comports):
@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM3'},
{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM2'}],
], indirect=True)
def test_devupdate_with_multiple_ftdi_devices(mock_serial_fixture):
"""
Test that multiple FTDI devices raise an error.
"""
mock_comports.return_value = [
MagicMock(vid=0x0403, pid=0x6001, device="COM1"),
MagicMock(vid=0x0403, pid=0x6001, device="COM2"),
]

target = "GAIN"
value = 5
@@ -128,29 +132,34 @@ def test_devupdate_with_multiple_ftdi_devices(mock_comports):
with pytest.raises(ValueError, match="Multiple FTDI devices found. Please specify the port."):
device_update(target, value, device_id)

@patch("miniscope_io.device_update.serial.Serial")
def test_devupdate_serial_exception_handling(mock_serial):
@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0403, 'pid': 0x6001, 'device': 'COM3'}],
], indirect=True)
def test_devupdate_serial_exception_handling(mock_serial_fixture):
"""
Test exception handling when serial port cannot be opened.
Might be too obvious so it might be better to re-think this test.
"""
mock_serial, mock_comports, mock_serial_instance = mock_serial_fixture

mock_serial.side_effect = serial.SerialException("Serial port error")

target = "LED"
value = 50
device_id = 1
port = "COM3"

with pytest.raises(ValidationError):
with pytest.raises(serial.SerialException):
device_update(target, value, device_id, port)

@patch("miniscope_io.device_update.serial.tools.list_ports.comports")
def test_specified_port_not_ftdi_device(mock_comports):
@pytest.mark.parametrize('mock_serial_fixture', [
[{'vid': 0x0413, 'pid': 0x6111, 'device': 'COM2'}],
], indirect=True)
def test_specified_port_not_ftdi_device(mock_serial_fixture):
"""
Test with a specified port not corresponding to an FTDI device.
"""
mock_comports.return_value = [
MagicMock(vid=None, pid=None, device="COM3")
]
mock_serial, mock_comports, mock_serial_instance = mock_serial_fixture

target = "GAIN"
value = 10