• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import os
7import random
8import re
9import subprocess
10import time
11
12from autotest_lib.client.bin import test
13from autotest_lib.client.common_lib import error
14from autotest_lib.client.cros.audio import audio_helper
15
16_STREAM_TYPE_INPUT = 0
17_STREAM_TYPE_OUTPUT = 1
18
19class audio_CrasStress(test.test):
20    """Checks if output buffer will drift to super high level."""
21    version = 2
22    _MAX_STREAMS = 3
23    _LOOP_COUNT = 300
24    _INPUT_BUFFER_LEVEL = '.*?READ_AUDIO.*?hw_level.*?(\d+).*?'
25    _OUTPUT_BUFFER_LEVEL = '.*?FILL_AUDIO.*?hw_level.*?(\d+).*?'
26    _CHECK_PERIOD_TIME_SECS = 1 # Check buffer level every second.
27
28    """
29    We only run 1024 and 512 block size streams in this test. So buffer level
30    of input device should stay between 0 and 1024. Buffer level of output
31    device should between 1024 to 2048. Sometimes it will be a little more.
32    Therefore, we set input buffer criteria to 2 * 1024 and output buffer
33    criteria to 3 * 1024.
34    """
35    _RATES = ['48000', '44100']
36    _BLOCK_SIZES = ['512', '1024']
37    _INPUT_BUFFER_DRIFT_CRITERIA = 2 * 1024
38    _OUTPUT_BUFFER_DRIFT_CRITERIA = 3 * 1024
39
40    def _new_stream(self, stream_type):
41        """Runs new stream by cras_test_client."""
42        if stream_type == _STREAM_TYPE_INPUT:
43            cmd = ['cras_test_client', '--capture_file', '/dev/null']
44        else:
45            cmd = ['cras_test_client', '--playback_file', '/dev/zero']
46
47        cmd += ['--rate', self._RATES[random.randint(0, 1)],
48                '--block_size', self._BLOCK_SIZES[random.randint(0, 1)]]
49
50        return subprocess.Popen(cmd)
51
52    def _dump_audio(self):
53        log_file = os.path.join(self.resultsdir, "audio_diagnostics.txt")
54        with open(log_file, 'w') as f:
55            f.write(audio_helper.get_audio_diagnostics())
56
57    def _check_buffer_level(self, stream_type):
58
59        buffer_level = self._get_buffer_level(stream_type)
60
61        if stream_type == _STREAM_TYPE_INPUT:
62            logging.debug("Max input buffer level: %d", buffer_level)
63            if buffer_level > self._INPUT_BUFFER_DRIFT_CRITERIA:
64                self._dump_audio()
65                raise error.TestFail('Input buffer level %d drift too high' %
66                                     buffer_level)
67
68        if stream_type == _STREAM_TYPE_OUTPUT:
69            logging.debug("Max output buffer level: %d", buffer_level)
70            if buffer_level > self._OUTPUT_BUFFER_DRIFT_CRITERIA:
71                self._dump_audio()
72                raise error.TestFail('Output buffer level %d drift too high' %
73                                     buffer_level)
74
75    def cleanup(self):
76        """Clean up all streams."""
77        while len(self._streams) > 0:
78            self._streams[0].kill()
79            self._streams.remove(self._streams[0])
80
81    def run_once(self, input_stream=True, output_stream=True):
82        """
83        Repeatedly add output streams of random configurations and
84        remove them to verify if output buffer level would drift.
85
86        @params input_stream: If true, run input stream in the test.
87        @params output_stream: If true, run output stream in the test.
88        """
89
90        if not input_stream and not output_stream:
91            raise error.TestError('Not supported mode.')
92
93        self._streams = []
94
95        loop_count = 0
96        past_time = time.time()
97        while loop_count < self._LOOP_COUNT:
98
99            # 1 for adding stream, 0 for removing stream.
100            add = random.randint(0, 1)
101            if not self._streams:
102                add = 1
103            elif len(self._streams) == self._MAX_STREAMS:
104                add = 0
105
106            if add == 1:
107                # 0 for input stream, 1 for output stream.
108                stream_type = random.randint(0, 1)
109                if not input_stream:
110                    stream_type = _STREAM_TYPE_OUTPUT
111                elif not output_stream:
112                    stream_type = _STREAM_TYPE_INPUT
113
114                self._streams.append(self._new_stream(stream_type))
115            else:
116                self._streams[0].kill()
117                self._streams.remove(self._streams[0])
118                time.sleep(0.1)
119
120            now = time.time()
121
122            # Check buffer level.
123            if now - past_time > self._CHECK_PERIOD_TIME_SECS:
124                past_time = now
125                if input_stream:
126                    self._check_buffer_level(_STREAM_TYPE_INPUT)
127                if output_stream:
128                    self._check_buffer_level(_STREAM_TYPE_OUTPUT)
129
130            loop_count += 1
131
132    def _get_buffer_level(self, stream_type):
133        """Gets a rough number about current buffer level.
134
135        @returns: The current buffer level.
136
137        """
138        if stream_type == _STREAM_TYPE_INPUT:
139            match_str = self._INPUT_BUFFER_LEVEL
140        else:
141            match_str = self._OUTPUT_BUFFER_LEVEL
142
143        proc = subprocess.Popen(['cras_test_client', '--dump_a'],
144                                stdout=subprocess.PIPE)
145        output, err = proc.communicate()
146        buffer_level = 0
147        for line in output.split('\n'):
148            search = re.match(match_str, line)
149            if search:
150                tmp = int(search.group(1))
151                if tmp > buffer_level:
152                    buffer_level = tmp
153        return buffer_level
154