xref: /aosp_15_r20/external/autotest/client/site_tests/audio_Aplay/audio_Aplay.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import os
7import logging
8import time
9
10from autotest_lib.client.bin import test, utils
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.cros import upstart
13from autotest_lib.client.cros.audio import alsa_utils
14from autotest_lib.client.cros.audio import audio_helper
15from autotest_lib.client.cros.audio import audio_spec
16from autotest_lib.client.cros.audio import cras_utils
17
18APLAY_FILE = '/dev/zero'  # raw data
19
20# Expected results of 'aplay -v' commands.
21APLAY_EXPECTED = set([('stream', 'PLAYBACK')])
22
23
24def _play_audio(device_name, duration=1, channel_count=2):
25    """Play a tone and try to ensure it played properly.
26
27    Sample output from aplay -v:
28
29    Playing raw data '/dev/zero' : Signed 16 bit Little Endian, Rate 44100 Hz,
30    Stereo
31    Hardware PCM card 0 'HDA Intel PCH' device 0 subdevice 0
32    Its setup is:
33      stream       : PLAYBACK
34      access       : RW_INTERLEAVED  format       : S16_LE
35      subformat    : STD
36      channels     : 2
37      rate         : 44100
38      exact rate   : 44100 (44100/1)
39      msbits       : 16
40      buffer_size  : 16384
41      period_size  : 4096
42      period_time  : 92879
43      tstamp_mode  : NONE
44      period_step  : 1
45      avail_min    : 4096
46      period_event : 0
47      start_threshold  : 16384
48      stop_threshold   : 16384
49      silence_threshold: 0
50      silence_size : 0
51      boundary     : 4611686018427387904
52      appl_ptr     : 0
53      hw_ptr       : 0
54
55    @param device_name: The output device for aplay.
56    @param duration: Duration supplied to aplay.
57    @return String output from the command (may be empty).
58    @raises CmdError when cmd returns <> 0.
59    """
60    cmd = [
61            'aplay',
62            '-v',  # show verbose details
63            '-D %s' % device_name,
64            '-d %d' % duration,
65            '-c %d' % channel_count,
66            '-r 44100',
67            '-f S16_LE',
68            APLAY_FILE,
69            '2>&1'
70    ]  # verbose details
71    return utils.system_output(' '.join(cmd)).strip()
72
73
74def _check_play(device_name, duration, channel_count, expected):
75    """Runs aplay command and checks the output against an expected result.
76
77    The expected results are compared as sets of tuples.
78
79    @param device_name: The output device for aplay.
80    @param duration: Duration supplied to aplay.
81    @param channel_count: Channel count supplied to aplay
82    @param expected: The set of expected tuples.
83    @raises error.TestError for invalid output or invalidly matching expected.
84    """
85    error_msg = 'invalid response from aplay'
86    results = _play_audio(device_name, duration, channel_count)
87    if not results.startswith("Playing raw data '%s' :" % APLAY_FILE):
88        raise error.TestError('%s: %s' % (error_msg, results))
89    result_set = utils.set_from_keyval_output(results, '[\s]*:[\s]*')
90    if set(expected) <= result_set:
91        return
92    raise error.TestError('%s: expected=%s.' %
93                          (error_msg, sorted(set(expected) - result_set)))
94
95
96class audio_Aplay(test.test):
97    """Checks that simple aplay functions correctly."""
98    version = 1
99
100    def initialize(self):
101        """Stop ui while running the test."""
102        upstart.stop_job('ui')
103
104    def cleanup(self):
105        """Start ui back after the test."""
106        upstart.restart_job('ui')
107
108    def run_once(self, duration=1, test_headphone=False):
109        """Run aplay and verify its output is as expected.
110
111        @param duration: The duration to run aplay in seconds.
112        @param test_headphone: If the value is true, test a headphone. If false,
113                               test an internal speaker.
114        """
115
116        # Check CRAS server is alive. If not, restart it and wait a second to
117        # get server ready.
118        if utils.get_service_pid('cras') == 0:
119            logging.debug("CRAS server is down. Restart it.")
120            utils.start_service('cras', ignore_status=True)
121            time.sleep(1)
122
123        # Skip test if there is no internal speaker on the board.
124        if not test_headphone:
125            board_type = utils.get_board_type()
126            board_name = utils.get_board()
127            if not audio_spec.has_internal_speaker(board_type, board_name):
128                logging.debug("No internal speaker. Skipping the test.")
129                return
130
131        if test_headphone:
132            output_node = audio_spec.get_headphone_node(utils.get_board())
133            channel_count = 2
134        else:
135            output_node = "INTERNAL_SPEAKER"
136            channel_count = audio_spec.get_internal_speaker_channel_count(
137                    utils.get_board_type(), utils.get_board(),
138                    utils.get_platform(), utils.get_sku())
139        logging.debug("Test output device %s", output_node)
140
141        cras_utils.set_single_selected_output_node(output_node)
142
143        cras_device_type = cras_utils.get_selected_output_device_type()
144        logging.debug("Selected output device type=%s", cras_device_type)
145        if cras_device_type != output_node:
146            audio_helper.dump_audio_diagnostics(
147                    os.path.join(self.resultsdir, "audio_diagnostics.txt"))
148            raise error.TestFail("Fail to select output device.")
149
150        cras_device_name = cras_utils.get_selected_output_device_name()
151        logging.debug("Selected output device name=%s", cras_device_name)
152        if cras_device_name is None:
153            audio_helper.dump_audio_diagnostics(
154                    os.path.join(self.resultsdir, "audio_diagnostics.txt"))
155            raise error.TestFail("Fail to get selected output device.")
156
157        alsa_device_name = alsa_utils.convert_device_name(cras_device_name)
158
159        # Stop CRAS to make sure the audio device won't be occupied.
160        utils.stop_service('cras', ignore_status=True)
161        try:
162            _check_play(alsa_device_name, duration, channel_count,
163                        APLAY_EXPECTED)
164        finally:
165            #Restart CRAS
166            utils.start_service('cras', ignore_status=True)
167