VulpesOS/tundra/tests/test_usb_transport.py
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

64 lines
2.9 KiB
Python

# SPDX-License-Identifier: MPL-2.0
import ctypes
from pathlib import Path
import sys
import tempfile
from types import SimpleNamespace
import unittest
from unittest.mock import Mock, patch
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'tools'))
import usb_protocol as usb
# Captured through sysfs from the actual Sargo diagnostic, high-speed config 1.
SARGO = bytes.fromhex('12011002000000406b1d04010904010203010902200001010080fa0904000002ff0000040705810200020007050102000200')
class USBTransportTests(unittest.TestCase):
def test_pixel_remapped_endpoint(self):
self.assertEqual(usb.parse_endpoints(SARGO,1),{'interface':0,'in':0x81,'out':0x01})
def test_original_endpoint_and_different_interface(self):
descriptors=bytearray(SARGO)
descriptors[-5]=2
descriptors[29]=4
self.assertEqual(usb.parse_endpoints(descriptors,1),{'interface':4,'in':0x81,'out':0x02})
def test_invalid_and_ambiguous_descriptors_rejected(self):
both_in=bytearray(SARGO);both_in[-5]=0x82
interrupt=bytearray(SARGO);interrupt[-4]=3
for data in (b'',b'\0\2',SARGO[:-1],both_in,interrupt,SARGO+SARGO[27:]):
with self.subTest(data=data):
with self.assertRaises(ValueError):
usb.parse_endpoints(data,1)
with self.assertRaises(ValueError):
usb.parse_endpoints(SARGO,2)
def test_transfer_uses_discovered_addresses_and_claims_interface(self):
layout={'interface':4,'out':1,'in':0x81}
endpoints=[]
response=b'TUNDRA_USB_STATUS protocol=1 readOnly=true\nEND\n'
def ioctl(fd,code,pointer):
request=ctypes.cast(pointer,ctypes.POINTER(usb.Bulk)).contents
endpoints.append(request.ep)
if len(endpoints)==1:
self.assertEqual(request.ep,1)
self.assertEqual(ctypes.string_at(request.data,request.length),b'status\n')
return request.length
self.assertEqual(request.ep,0x81)
ctypes.memmove(request.data,response,len(response))
return len(response)
with tempfile.TemporaryFile() as stream, \
patch.object(usb,'device_layout',return_value=layout), \
patch.object(usb.Path,'open',return_value=stream), \
patch.object(usb.fcntl,'ioctl') as claim, \
patch.object(usb.ctypes,'CDLL',return_value=SimpleNamespace(ioctl=Mock(side_effect=ioctl))):
self.assertEqual(usb.exchange('/dev/bus/usb/003/011',('status',)),{'status':response.decode()})
self.assertEqual(claim.call_args.args[2],usb.struct.pack('I',4))
self.assertEqual(endpoints,[1,0x81])
def test_invalid_command_rejected_before_device_access(self):
with patch.object(usb,'device_layout') as layout:
with self.assertRaises(ValueError):
usb.exchange('/dev/bus/usb/003/011',('status\nversion',))
layout.assert_not_called()