blob: b4106e7e2d79f5b37d636a9df4272735cedefd06 [file] [log] [blame]
# Copyright 2020 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.
"""Server side Bluetooth audio tests."""
import logging
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros.bluetooth.bluetooth_audio_test_data import (
a2dp_test_data)
from autotest_lib.server.cros.bluetooth.bluetooth_adapter_tests import (
BluetoothAdapterTests, test_retry_and_log)
class BluetoothAdapterAudioTests(BluetoothAdapterTests):
"""Server side Bluetooth adapter audio test class."""
DEVICE_TYPE = 'BLUETOOTH_AUDIO'
FREQUENCY_TOLERANCE_RATIO = 0.01
def _get_pulseaudio_bluez_source(self, get_source_method, device):
"""Get the specified bluez device number in the pulseaudio source list.
@param get_source_method: the method to get distinct bluez source
@param device: the bluetooth peer device
@returns: True if the specified bluez source is derived
"""
sources = device.ListSources()
logging.debug('ListSources()\n%s', sources)
self.bluez_source = get_source_method()
result = bool(self.bluez_source)
if result:
logging.debug('bluez_source device number: %s', self.bluez_source)
else:
logging.debug('waiting for bluez_source ready in pulseaudio...')
return result
def _get_pulseaudio_bluez_source_a2dp(self, device):
"""Get the a2dp bluez source device number.
@param device: the bluetooth peer device
@returns: the a2dp bluez source device number
"""
return self._get_pulseaudio_bluez_source(
device.GetBluezSourceA2DPDevice, device)
def _get_pulseaudio_bluez_source_hfp(self, device):
"""Get the hfp bluez source device number.
@param device: the bluetooth peer device
@returns: the hfp bluez source device number
"""
return self._get_pulseaudio_bluez_source(
device.GetBluezSourceHFPDevice, device)
def _check_frequency(self, recorded_freq, expected_freq):
"""Check if the recorded frequency is within tolerance.
@param recorded_freq: the frequency of recorded audio
@param expected_freq: the expected frequency
@returns: True if the recoreded frequency falls within the tolerance of
the expected frequency
"""
tolerance = expected_freq * self.FREQUENCY_TOLERANCE_RATIO
return abs(expected_freq - recorded_freq) <= tolerance
def _check_primary_frequencies(self, audio_test_data):
"""Check if the recorded frequencies meet expectation.
@param audio_test_data: a dictionary about the audio test data
defined in client/cros/bluetooth/bluetooth_audio_test_data.py
@returns: True if the recorded frequencies of all channels fall within
the tolerance of expected frequencies
"""
recorded_frequencies = self.bluetooth_facade.get_primary_frequencies(
audio_test_data['recorded_file'])
expected_frequencies = audio_test_data['frequencies']
final_result = True
self.results = dict()
for channel, expected_freq in enumerate(expected_frequencies):
recorded_freq = recorded_frequencies[channel]
ret_val = self._check_frequency(recorded_freq, expected_freq)
pass_fail_str = 'pass' if ret_val else 'fail'
self.results['Channel %d' % channel] = (
'primary frequency %d (expected %d): %s' % (
recorded_freq, expected_freq, pass_fail_str))
if not ret_val:
final_result = False
logging.debug(str(self.results))
if not final_result:
logging.error('Failure at checking primary frequencies')
return final_result
def initialize_bluetooth_audio(self, device):
"""Initialize the Bluetooth audio task.
Note: pulseaudio is not stable. Need to restart it in the beginning.
@param device: the bluetooth peer device
"""
if not device.StartPulseaudio():
raise error.TestError('Failed to start pulseaudio.')
logging.debug('pulseaudio is started.')
def cleanup_bluetooth_audio(self, device):
"""Cleanup for Bluetooth audio.
@param device: the bluetooth peer device
"""
if not device.StopPulseaudio():
logging.warn('Failed to stop pulseaudio. Ignored.')
# ---------------------------------------------------------------
# Definitions of all bluetooth audio test cases
# ---------------------------------------------------------------
@test_retry_and_log(False)
def test_a2dp_sinewaves(self, device):
"""Test Case: a2dp sinewaves
@param device: the bluetooth peer device
@returns: True if the recorded primary frequency is within the
tolerance of the playback sine wave frequency.
"""
try:
utils.poll_for_condition(
condition=(lambda:
self._get_pulseaudio_bluez_source_a2dp(device)),
timeout=20,
sleep_interval=1,
desc='Waiting for bluez a2dp source in pulseaudio')
except Exception as e:
logging.error('pulseaudio bluez_source: %s', e)
raise error.TestError('Bluetooth peer failed to get bluez source.')
# Start recording audio on the peer Bluetooth audio device.
if not device.StartRecordingAudioSubprocess('a2dp'):
raise error.TestError(
'Failed to record on the peer Bluetooth audio device.')
# Play stereo audio on the DUT.
if not self.bluetooth_facade.play_audio(a2dp_test_data):
raise error.TestError('DUT failed to play audio.')
# Stop recording audio on the peer Bluetooth audio device.
if not device.StopRecordingingAudioSubprocess():
msg = 'Failed to stop recording on the peer Bluetooth audio device'
logging.error(msg)
# Copy the recorded audio file to the DUT for spectrum analysis.
recorded_file = a2dp_test_data['recorded_file']
device.ScpToDut(recorded_file, recorded_file, self.host.ip)
# Check if the primary frequencies of recorded file meet expectation.
check_freq_result = self._check_primary_frequencies(a2dp_test_data)
return check_freq_result