# Copyrigh The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Get speaker/microphone status from cras_client_test, /proc/asound and atrus.log. """ from __future__ import print_function import logging import re NUM_AUDIO_STREAM_IN_MEETING = 3 def get_soundcard_by_name(dut, name): """ Returns the soundcard number of specified soundcard by name. @param dut: The handle of the device under test. @param name: The name of Speaker For example: 'Hangouts Meet speakermic' @returns the soundcard, if no device found returns None. """ soundcard = None cmd = "cat /proc/asound/cards | grep \"{}\" | grep USB".format(name) logging.info('---cmd: %s', cmd) try: soundcard = dut.run(cmd, ignore_status=True).stdout.strip().split()[0] except Exception as e: soundcard = None logging.exception('Fail to execute %s.', cmd) if soundcard: soundcard = "card{}".format(soundcard) logging.info('---audio card %s', soundcard) else: logging.exception('Fail to get sound card, cli=%s.', cmd) return soundcard def check_soundcard_by_name(dut, name): """ check soundcard by name exists @param dut: The handle of the device under test. @param name: The name of Speaker For example: 'Hangouts Meet speakermic' @returns: True, None if test passes, False, errMsg if test fails """ if get_soundcard_by_name(dut, name): return True, None else: return False, 'Soundcard is not found under /proc/asound/cards.' def check_audio_stream(dut, is_in_meeting): """ Verify speaker is streaming or not streaming as expected. @param dut: The handle of the device under test. @is_in_meeting: True if CfM is in meeting, False, if not @returns: True, None if test passes, False, errMsg if test fails """ number_stream = get_number_of_active_streams(dut) if is_in_meeting: if number_stream >= NUM_AUDIO_STREAM_IN_MEETING: return True, None else: return False, 'Number of Audio streams is not expected.' else: if number_stream <= NUM_AUDIO_STREAM_IN_MEETING: return True, None else: return False, 'Number of Audio streams is not expected.' def get_audio_stream_state(dut, soundcard): """ Returns the state of stream0 for specified soundcard. @param dut: The handle of the device under test. Should be initialized in autotest. @param soundcard: soundcard For example: 'card0' @returns the list of state of steam0, "Running" or "Stop" """ stream_state = [] try: cmd = ("cat /proc/asound/%s/stream0 | grep Status | " "awk -v N=2 '{print $N}'" % soundcard) stream_state = dut.run(cmd, ignore_status=True).stdout.split() except Exception as e: logging.exception('Fail to run cli: %s.', cmd) return stream_state def check_default_speaker_volume(dut, cfm_facade): """Check volume of speaker is the same as expected. @param dut: The handle of the device under test. @param cfm_facade: the handle of cfm facade @returns True, None if default speakers have same volume as one read from hotrod, False, errMsg, otherwise """ try: expected_volume = int(cfm_facade.get_speaker_volume()) except Exception as e: errmsg = 'Fail to run telemetry api to get speaker volume.' logging.exception(errmsg) return False, errmsg if expected_volume < 1: return False, 'Fail to get speaker volume from Hotrod.' nodes = get_nodes_for_default_speakers_cras(dut) if not nodes: logging.info('---Fail to get node for default speaker.') return False, 'Fail to get node for default speaker.' for node in nodes: cras_volume = get_speaker_volume_cras(dut, node) logging.info('---Volume for default speaker are sync for ' 'node %s? cfm: %d, cras: %d.' 'format(node, expected_volume, cras_volume)') if not expected_volume == cras_volume: logging.info('---Volume Check Fail for default speaker: ' 'expected_volume:%d, actual_volume:%d.' 'format(expected_volume, cras_volume)') return False, ('Volume Check fails for default speaker: ' 'expected_volume:%d, actual_volume:%d', '% expected_volume, cras_volume') logging.info('---Expected volume: %d, actual: %d', expected_volume, cras_volume) return True, None def get_number_of_active_streams(dut): """ Returns the number of active stream. @param dut: The handle of the device under test. Should be initialized in autotest. @returns the number of active streams. """ cmd = ("cras_test_client --dump_server_info " "| grep 'Num active streams:' " "| awk -v N=4 '{print $N}'") try: number_of_streams = int(dut.run(cmd, ignore_status=True).stdout.strip()) except Exception as e: logging.exception('Fail to execute cli to get number of streams: %s.', cmd) return None logging.info('---number of audio streaming: %d', number_of_streams) return number_of_streams def get_nodes_for_default_speakers_cras(dut): """get node for default speakers from cras_test_client. @param dut: The handle of the device under test. Should be initialized in autotest. @returns the list of nodes for default speakers. If device not found, returns []. """ nodes = [] cmd = ("cras_test_client --dump_server_info | awk '/Output Nodes:/," "/Input Devices:/'") try: lines = dut.run(cmd, ignore_status=True).stdout.splitlines() except Exception as e: logging.exception('Fail to execute cli to get nodes for default' 'speaker: %s', cmd) return nodes for _line in lines: match = re.findall(r"(\d+):\d+.*USB\s+\*.*", _line) if match: nodes.append(match[0]) logging.info('---found nodes for default speaker %s', nodes) return nodes def get_speaker_for_node_cras(dut, node): """get node for default speakers from cras_test_client. @param dut: The handle of the device under test. Should be initialized in autotest. @returns the list of nodes for default speakers. If device not found, returns []. """ cmd = ("cras_test_client --dump_server_info | awk '/Output Devices:/," "/Output Nodes:/' | grep '%s'" % node) try: line = dut.run(cmd, ignore_status=True).stdout.stripe() speaker = re.findall(r"^[0-9]+\s*(.*):\s+USB\s+Audio:", line)[0] except Exception as e: logging.exception('Fail to execute cli to get nodes for default' 'speaker: %s.', cmd) logging.info('---speaker for %s is %s', node, speaker) return speaker def get_nodes_for_default_microphone_cras(dut): """get node for default microphones from cras_test_client. @param dut: The handle of the device under test. Should be initialized in autotest. @returns the list of nodes for default microphone. If device not found, returns []. """ nodes = None cmd = ("cras_test_client --dump_server_info | awk '/Input Nodes:/," "/Attached clients:/'") try: lines = dut.run(cmd, ignore_status=True).stdout.splitlines() for _line in lines: nodes.append(re.findall(r"(\d+):\d+.*USB\s+\*.*", _line)[0]) except Exception as e: logging.exception('Fail to execute cli to get nodes for default' ' speaker: %s.', cmd) return nodes def get_microphone_for_node_cras(dut, node): """get node for default speakers from cras_test_client. @param dut: The handle of the device under test. Should be initialized in autotest. @returns the list of nodes for default speakers. If device not found, returns []. """ cmd = ("cras_test_client --dump_server_info | awk '/Input Devices:/," "/Input Nodes:/' | grep '%s' " % node) try: line = dut.run(cmd, ignore_status=True).stdout microphone = re.findall(r"10\t(.*):\s+USB\s+Audio:", line)[0] except Exception as e: logging.exception('Fail to execute cli to get nodes for default' ' speaker: %s.', cmd) logging.info('---mic for %s is %s', node, microphone) return microphone def get_speaker_volume_cras(dut, node): """get volume for speaker from cras_test_client based on node @param dut: The handle of the device under test. Should be initialized in autotest. @param node: The node of Speaker Example cli: cras_test_client --dump_server_info | awk '/Output Nodes:/,/Input Devices:/' | grep 9:0 | awk -v N=3 '{print $N}' @returns the volume of speaker. If device not found, returns None. """ cmd = ("cras_test_client --dump_server_info | awk '/Output Nodes:/," "/Input Devices:/' | grep -E 'USB' | grep '%s':0 " "| awk -v N=3 '{print $N}'" % node) try: volume = int(dut.run(cmd, ignore_status=True).stdout.strip()) except Exception as e: logging.exception('Fail to execute cli %s to get volume for node %d', cmd, node) return None return volume def check_cras_mic_mute(dut, cfm_facade): """ check microphone is muted or unmuted as expected/. @param dut: The handle of the device under test. @param cfm_facade: facade of CfM @param is_mic_muted: True if muted, False otherwise @returns True, none if test passes False, errMsg if test fails """ try: is_mic_muted = cfm_facade.is_mic_muted() except Exception as e: errmsg = 'Fail to run telemetry api to check mute state for mic.' logging.exception(errmsg) return False, errmsg actual_muted = get_mic_muted_cras(dut) if is_mic_muted == actual_muted: return True, None else: if is_mic_muted: logging.info('Hotrod setting: microphone is muted') else: logging.info('Hotrod setting: microphone is not muted') if actual_muted: logging.info('cras setting: microphone is muted') else: logging.info('cras setting: microphone is not muted') return False, 'Microphone is not muted/unmuted as shown in Hotrod.' def check_is_preferred_speaker(dut, name): """ check preferred speaker is speaker to be tested. @param dut: The handle of the device under test. @param cfm_facade: facade of CfM @param name: name of speaker @returns True, none if test passes False, errMsg if test fails """ node = None cmd = ("cras_test_client --dump_server_info | awk " "'/Output Devices:/,/Output Nodes:/' " "| grep '%s' " % name) try: output = dut.run(cmd, ignore_status=True).stdout.strip() except Exception as e: logging.exception('Fail to run cli %s to find %s in cras_test_client.', cmd, node) return False, 'Fail to run cli {}:, reason: {}'.format(cmd, str(e)) logging.info('---output = %s', output) if output: node = output.split()[0] logging.info('---found node for %s is %s', name, node) else: return False, 'Fail in get node for speaker in cras.' default_nodes = get_nodes_for_default_speakers_cras(dut) logging.info('---default speaker node is %s', default_nodes) if node in default_nodes: return True, None return False, '{} is not set to preferred speaker.'.format(name) def check_is_preferred_mic(dut, name): """check preferred mic is set to speaker to be tested.""" cmd = ("cras_test_client --dump_server_info | " "awk '/Input Devices/,/Input Nodes/' | grep '%s' | " "awk -v N=1 '{print $N}'" % name) logging.info('---cmd = %s',cmd) try: mic_node = dut.run(cmd, ignore_status=True).stdout.strip() logging.info('---mic_node : %s', mic_node) except Exception as e: logging.exception('Fail to execute: %s to check preferred mic.', cmd) return False, 'Fail to run cli.' try: cmd = ("cras_test_client --dump_server_info | awk '/Input Nodes:/," "/Attached clients:/' | grep default " "| awk -v N=2 '{print $N}'") mic_node_default = dut.run(cmd, ignore_status=True).stdout.strip() if not mic_node_default: cmd = ("cras_test_client --dump_server_info | awk '/Input Nodes:/," "/Attached clients:/' | grep '%s' " "| awk -v N=2 '{print $N}'" %name) mic_node_default = dut.run(cmd,ignore_status=True).stdout.strip() logging.info('---%s',cmd) logging.info('---%s', mic_node_default) except Exception as e: msg = 'Fail to execute: {} to check preferred mic'.format(cmd) logging.exception(msg) return False, msg logging.info('---mic node:%s, default node:%s', mic_node, mic_node_default) if mic_node == mic_node_default.split(':')[0]: return True, None return False, '{} is not preferred microphone.'.format(name) def get_mic_muted_cras(dut): """ Get the status of mute or unmute for microphone @param dut: the handle of CfM under test @returns True if mic is muted False if mic not not muted """ cmd = 'cras_test_client --dump_server_info | grep "Capture Gain"' try: microphone_muted = dut.run(cmd, ignore_status=True).stdout.strip() except Exception as e: logging.exception('Fail to execute: %s.', cmd) return False, 'Fail to execute: {}.'.format(cmd) logging.info('---%s', microphone_muted) if "Muted" in microphone_muted: return True else: return False def check_speaker_exist_cras(dut, name): """ Check speaker exists in cras. @param dut: The handle of the device under test. @param name: name of speaker @returns: True, None if test passes, False, errMsg if test fails """ cmd = ("cras_test_client --dump_server_info | awk " "'/Output Devices:/, /Output Nodes:/' " "| grep '%s'" % name) try: speaker = dut.run(cmd, ignore_status=True).stdout.splitlines()[0] except Exception as e: logging.exception('Fail to find %s in cras_test_client running %s.', name, cmd) speaker = None logging.info('---cmd: %s\n---output = %s', cmd, speaker) if speaker: return True, None return False, 'Fail to execute cli {}: Reason: {}'.format(cmd, str(e)) def check_microphone_exist_cras(dut, name): """ Check microphone exists in cras. @param dut: The handle of the device under test. @param name: name of speaker @returns: True, None if test passes, False, errMsg if test fails """ microphone = None cmd = ("cras_test_client --dump_server_info | awk " "'/Input Devices:/, /Input Nodes:/' " "| grep '%s'" % name ) try: microphone = dut.run(cmd, ignore_status=True).stdout.splitlines()[0] except Exception as e: logging.exception('Fail to execute cli %s.', cmd) logging.info('---cmd: %s\n---output = %s', cmd, microphone) if microphone: return True, None return False, 'Fail to find microphone {}.'.format(name) def check_audio_stream(dut, is_in_meet): """ Verify speaker is streaming or not streaming as expected. @param dut: The handle of the device under test. @is_in_meeting: True if CfM is in meeting, False, if not @returns: True, None if test passes, False, errMsg if test fails """ number_stream = get_number_of_active_streams(dut) if is_in_meet: if number_stream >= NUM_AUDIO_STREAM_IN_MEETING: return True, None else: return False, 'Number of Audio streams is not expected.' else: if number_stream <= NUM_AUDIO_STREAM_IN_MEETING: return True, None else: return False, 'Number of Audio streams is not expected.'