Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
62 lines
3.6 KiB
Python
62 lines
3.6 KiB
Python
import importlib.util,json,unittest
|
|
from pathlib import Path
|
|
spec=importlib.util.spec_from_file_location('audio',Path(__file__).resolve().parents[1]/'runtime/call-audio.py')
|
|
audio=importlib.util.module_from_spec(spec);spec.loader.exec_module(audio)
|
|
class AudioTests(unittest.TestCase):
|
|
def rig(self):
|
|
commands=[]
|
|
devices={'cards':[{'index':0,'owner_module':1,'name':'droid_card.primary','profiles':{'default':{},'voicecall':{}},'active_profile':'default'}],
|
|
'sinks':[{'owner_module':1,'name':'sink.primary_output','ports':[{'name':'output-earpiece'},{'name':'output-parking'},{'name':'output-speaker'}],'active_port':'output-speaker','mute':True}],
|
|
'sources':[{'name':'source.droid','mute':True}],
|
|
'sink-inputs':[{'index':9,'properties':{'media.role':'phone'}}]}
|
|
def command(*args):
|
|
commands.append(args)
|
|
return ''
|
|
route=audio.CallAudio(command)
|
|
route.devices=lambda kind:devices[kind]
|
|
return route,commands,devices
|
|
def test_actual_pulse14_listing_and_routing(self):
|
|
fixture=Path(__file__).parent/'fixtures/pulse14'
|
|
commands=[]
|
|
def command(*args):
|
|
commands.append(args)
|
|
if args[0]=='list':
|
|
return (fixture/(args[1]+'.txt')).read_text() if args[1]!='sink-inputs' else ''
|
|
return ''
|
|
route=audio.CallAudio(command)
|
|
self.assertEqual(len(route.devices('cards')),1)
|
|
self.assertEqual(route.devices('cards')[0]['owner_module'],1)
|
|
self.assertIn('output-earpiece',route.devices('sinks')[0]['ports'])
|
|
route.enter();self.assertTrue(route.active)
|
|
route.leave();self.assertIsNone(route.saved)
|
|
self.assertIn(('set-sink-port','sink.primary_output','output-earpiece'),commands)
|
|
|
|
def test_active_call_routes_earpiece_then_restores(self):
|
|
route,c,_=self.rig()
|
|
route.update([{'state':'active'}]);n=len(c)
|
|
route.update([{'state':'active'}]);self.assertEqual(len(c),n)
|
|
self.assertIn(('set-card-profile','droid_card.primary','voicecall'),c)
|
|
self.assertIn(('set-sink-port','sink.primary_output','output-earpiece'),c)
|
|
self.assertNotIn(('set-sink-port','sink.primary_output','output-speaker'),c)
|
|
self.assertIn(('set-source-mute','source.droid','0'),c)
|
|
route.update([])
|
|
self.assertIsNone(route.saved)
|
|
self.assertIn(('set-card-profile','droid_card.primary','default'),c)
|
|
self.assertIn(('set-source-mute','source.droid','1'),c)
|
|
def test_controls_route_speaker_mute_and_phone_stream(self):
|
|
route,c,_=self.rig();route.enter()
|
|
result=route.control({'operation':'set','speaker':True,'muted':True,'volume':0.8})
|
|
self.assertTrue(result['speaker']);self.assertTrue(result['muted'])
|
|
self.assertIn(('set-sink-port','sink.primary_output','output-speaker'),c)
|
|
self.assertIn(('set-source-mute','source.droid','1'),c)
|
|
self.assertIn(('set-sink-input-volume','9','80%'),c)
|
|
route.leave();self.assertFalse(route.muted);self.assertFalse(route.speaker)
|
|
with self.assertRaisesRegex(ValueError,'NOT_ACTIVE'):route.control({'operation':'set','muted':True})
|
|
|
|
def test_ringing_alone_does_not_open_microphone(self):
|
|
route,c,_=self.rig();route.update([{'state':'incoming'}]);self.assertEqual(c,[])
|
|
def test_missing_earpiece_never_falls_back_to_speaker(self):
|
|
route,c,d=self.rig();d['sinks'][0]['ports']=[{'name':'output-speaker'}]
|
|
with self.assertRaisesRegex(RuntimeError,'EARPIECE'):route.update([{'state':'active'}])
|
|
self.assertFalse(any(x[0].startswith('set-') for x in c))
|
|
if __name__=='__main__':unittest.main()
|