Files
orisys_arm/station/test/test_tactile_flow.py
T

61 lines
3.4 KiB
Python

import tempfile
from pathlib import Path
from types import SimpleNamespace
from unittest import TestCase
import numpy as np
from arm_application.tactile_flow import tactile_devices,select_source,flow_preview,read_sample
class TactileFlowTest(TestCase):
def test_discovery_filters_realsense_and_duplicate_interfaces(self):
with tempfile.TemporaryDirectory() as folder:
root=Path(folder);sys=root/'class';sys.mkdir()
for name,vendor,index in [('video0','8086','0'),('video1','1bcf','0'),('video2','1bcf','1'),('video3','0bda','0')]:
entry=sys/name;entry.mkdir();(entry/'index').write_text(index)
usb=root/('usb-'+name);usb.mkdir();(usb/'idVendor').write_text(vendor)
(entry/'device').symlink_to(usb,target_is_directory=True)
found=tactile_devices(sys,Path('/dev'))
self.assertEqual(found,['/dev/video1'])
self.assertEqual(select_source('left',{},found),'/dev/video1')
self.assertIsNone(select_source('right',{},found))
self.assertEqual(select_source('right',{'right_video':'/dev/v4l/by-path/sensor'},found),'/dev/v4l/by-path/sensor')
def test_actual_vector_field_changes_preview_and_rejects_bad_shapes(self):
flow=np.zeros((64,64,2),np.float32)
rest=flow_preview(flow);flow[...,0]=12
motion=flow_preview(flow)
self.assertEqual(motion.shape,(360,360,3));self.assertFalse(np.array_equal(rest,motion))
with self.assertRaises(ValueError):flow_preview(np.zeros((8,8)))
def test_uses_raw_force_channels_and_reads_flow_only_once(self):
info=SimpleNamespace(FPS=1,FNORMAL=2,FSHEARX=3,FSHEARY=4,FNORMAL_RAW=5,FSHEARX_RAW=6,FSHEARY_RAW=7,VRAW=8)
calls=[]
def read(*keys):
calls.append(keys);return 30.,200.,3.,-4.,np.zeros((8,8,2))
row=read_sample(SimpleNamespace(info=info,read_info=read))
self.assertEqual(calls,[(1,5,6,7,8)])
self.assertEqual(row['fnormal'],200.);self.assertEqual(row['fsheary'],-4.)
def test_preview_polling_is_independent_of_slow_state_poll(self):
import base64,threading,time
from unittest.mock import patch
from station.runtime_client import RuntimeClient
client=RuntimeClient.__new__(RuntimeClient)
client._lock=threading.RLock();client._stop=threading.Event()
from collections import deque
client._force_history={'left':deque(maxlen=1800),'right':deque(maxlen=1800)}
client._force_cursors={};client._force_epochs={}
client._snapshot={'view':{'on':True}};client._frames={};client.socket=Path('/unused')
calls=[]
def rpc_call(*args,**kwargs):
calls.append(args[1]);return {'frames':{'left_shear':base64.b64encode(b'jpeg').decode()},
'samples':{'left':[{'seq':1,'t':time.time(),'fnormal':.5,'fshearx':.2,'fsheary':-.3}]},'epochs':{'left':0}}
with patch('station.runtime_client.rpc.call',side_effect=rpc_call):
thread=threading.Thread(target=client._poll_frames);thread.start()
time.sleep(.16);client._stop.set();thread.join(1)
self.assertGreaterEqual(len(calls),3)
self.assertEqual(set(calls),{'tactile_preview'})
self.assertEqual(client._frames['left_shear'],b'jpeg')
self.assertEqual(len(client._force_history['left']),1)
self.assertEqual(client._force_cursors['left'],1)