Files
ProxMenux/oci/tests/test_usb_devices.py
T

95 lines
4.2 KiB
Python

"""USB devices are offered by name, as the Monitor shows them, with the node an LXC receives."""
import os
from pathlib import Path
import sys
import tempfile
import unittest
from unittest.mock import Mock, patch
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from proxmenux_oci import host
from proxmenux_oci.extra_devices import ask_extra_devices, choose_usb_device
LSUSB = """Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 002: ID 0463:ffff MGE UPS Systems UPS
Bus 003 Device 003: ID 0658:0200 Sigma Designs, Inc. Aeotec Z-Stick Gen5 (ZW090) - UZB
Bus 003 Device 004: ID 1cf1:0030 Dresden Elektronik ZigBee gateway [ConBee II]
Bus 003 Device 005: ID 0781:5581 SanDisk Corp. Ultra
"""
class UsbDevicesTests(unittest.TestCase):
def setUp(self):
tmp = tempfile.TemporaryDirectory()
self.addCleanup(tmp.cleanup)
self.root = Path(tmp.name)
pci = self.root / "sys/devices/pci0000:00/usb3"
self.device(pci, "usb3", "1d6b", "09", 1)
self.device(pci, "3-3", "0463", "00", 2, product="Ellipse ECO", interface="03")
self.device(pci, "3-8", "0658", "02", 3, tty="ttyACM1")
self.device(pci, "3-9", "1cf1", "02", 4, product="ConBee II", tty="ttyACM0")
self.device(pci, "3-10", "0781", "00", 5, product="Ultra", interface="08")
def device(self, parent, name, vendor, device_class, number, product="", interface="", tty=""):
path = parent / name
path.mkdir(parents=True)
for key, value in {"idVendor": vendor, "idProduct": "0001", "bDeviceClass": device_class,
"busnum": "3", "devnum": str(number)}.items():
(path / key).write_text(value + "\n")
if product:
(path / "product").write_text(product + "\n")
if interface:
(path / f"{name}:1.0").mkdir()
(path / f"{name}:1.0" / "bInterfaceClass").write_text(interface + "\n")
link = self.root / "sys/bus/usb/devices" / name
link.parent.mkdir(parents=True, exist_ok=True)
os.symlink(path, link)
if tty:
port = path / f"{name}:1.0" / "tty" / tty
port.mkdir(parents=True)
(self.root / "sys/class/tty").mkdir(parents=True, exist_ok=True)
(self.root / "sys/class/tty" / tty).mkdir()
os.symlink(path / f"{name}:1.0", self.root / "sys/class/tty" / tty / "device")
def test_devices_are_named_as_the_monitor_names_them(self):
rows = host.usb_devices(self.root, LSUSB)
self.assertEqual(rows, [
{"path": "/dev/bus/usb/003/002", "name": "Ellipse ECO", "kind": "UPS"},
{"path": "/dev/ttyACM1", "name": "Sigma Designs, Inc. Aeotec Z-Stick Gen5 (ZW090) - UZB",
"kind": "Communications"},
{"path": "/dev/ttyACM0", "name": "ConBee II", "kind": "Communications"},
])
def test_the_menu_lists_the_devices_and_leaves_attached_ones_out(self):
ui = Mock()
ui.choose.return_value = "/dev/ttyACM1"
with patch.object(host, "usb_devices", return_value=host.usb_devices(self.root, LSUSB)):
self.assertEqual(choose_usb_device(ui, "USB", attached={"/dev/ttyACM0"}), "/dev/ttyACM1")
tags = [tag for tag, _label in ui.choose.call_args.args[1]]
self.assertEqual(tags, ["/dev/bus/usb/003/002", "/dev/ttyACM1", "manual"])
ui.ask.assert_not_called()
def test_without_devices_the_node_is_typed(self):
ui = Mock()
ui.ask.return_value = "/dev/ttyUSB0"
with patch.object(host, "usb_devices", return_value=[]):
self.assertEqual(choose_usb_device(ui, "USB"), "/dev/ttyUSB0")
ui.message.assert_called_once()
ui.choose.assert_not_called()
def test_extra_usb_device_comes_from_the_list(self):
ui = Mock()
ui.confirm.side_effect = [True, False]
ui.choose.side_effect = ["usb", "/dev/ttyACM0"]
with patch.object(host, "usb_devices", return_value=host.usb_devices(self.root, LSUSB)):
devices = ask_extra_devices(ui, [], True)
self.assertEqual([(d["host_path"], d["container_path"]) for d in devices],
[("/dev/ttyACM0", "/dev/ttyACM0")])
if __name__ == "__main__":
unittest.main()