1# Copyright (c) 2014 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 os
6import logging
7import time
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib.cros import chrome
11from autotest_lib.client.cros.a11y import a11y_test_base
12from autotest_lib.client.cros.audio import cras_utils
13from autotest_lib.client.cros.audio import sox_utils
14
15
16class accessibility_ChromeVoxSound(a11y_test_base.a11y_test_base):
17    """Check whether ChromeVox makes noise on real hardware."""
18    version = 1
19
20    _audio_chunk_size = 1 # Length of chunk size in seconds.
21    _detect_time = 40 # Max length of time to spend detecting audio in seconds.
22
23
24    def _detect_audio(self, name, min_time):
25        """Detects whether audio was heard and returns the approximate time.
26
27        Runs for at most self._detect_time, checking each chunk for sound.
28        After first detecting a chunk that has audio, counts the subsequent
29        chunks that also do.
30
31        Finally, check whether the found audio matches the expected length.
32
33        @param name: a string representing which sound is expected.
34        @param min_time: the minimum allowed sound length in seconds.
35
36        @raises: error.TestFail if the observed behavior doesn't match
37                 expected: either no sound or sound of bad length.
38
39        """
40        count = 0
41        counting = False
42        saw_sound_end = False
43
44        for i in xrange(self._detect_time / self._audio_chunk_size):
45            rms = self._rms_of_next_audio_chunk()
46            if rms > 0:
47                logging.info('Found passing chunk: %d.', i)
48                if not counting:
49                    start_time = time.time()
50                    counting = True
51                count += 1
52            elif counting:
53                audio_length = time.time() - start_time
54                saw_sound_end = True
55                break
56        if not counting:
57            raise error.TestFail('No audio for %s was found!' % name)
58        if not saw_sound_end:
59            raise error.TestFail('Audio for %s was more than % seconds!' % (
60                    name, self._detect_time))
61
62        logging.info('Time taken - %s: %f', name, audio_length)
63        if audio_length < min_time:
64            raise error.TestFail(
65                    '%s audio was only %f seconds long!' % (name, audio_length))
66        return
67
68
69    def _rms_of_next_audio_chunk(self):
70        """Finds the sox_stats values of the next chunk of audio."""
71        cras_utils.loopback(self._loopback_file, channels=1,
72                            duration=self._audio_chunk_size)
73        stat_output = sox_utils.get_stat(self._loopback_file)
74        logging.info(stat_output)
75        return vars(stat_output)['rms']
76
77
78    def _check_chromevox_sound(self, cr):
79        """Test contents.
80
81        Enable ChromeVox, navigate to a new page, and open a new tab.  Check
82        the audio output at each point.
83
84        @param cr: the chrome.Chrome() object
85
86        """
87        chromevox_start_time = time.time()
88        self._toggle_chromevox()
89        self._confirm_chromevox_state(True)
90
91        # TODO: this sound doesn't play for Telemetry user.  crbug.com/590403
92        # Welcome ding
93        # self._detect_audio('enable ChromeVox ding', 1)
94
95        # "ChromeVox Spoken Feedback is ready!"
96        self._detect_audio('welcome message', 2)
97        chromevox_open_time = time.time() - chromevox_start_time
98        logging.info('ChromeVox took %f seconds to start.')
99
100        # New tab sound.
101        tab = cr.browser.tabs.New()
102        self._detect_audio('new tab ding', 2)
103
104        # Page navigation sound.
105        tab.Navigate('chrome://version')
106        self._detect_audio('page navigation sound', 2)
107
108
109    def run_once(self):
110        """Entry point of this test."""
111        self._loopback_file = os.path.join(self.bindir, 'cras_loopback.wav')
112        extension_path = self._get_extension_path()
113
114        with chrome.Chrome(extension_paths=[extension_path]) as cr:
115            self._extension = cr.get_extension(extension_path)
116            cr.browser.tabs[0].WaitForDocumentReadyStateToBeComplete()
117            self._confirm_chromevox_state(False)
118            self._check_chromevox_sound(cr)
119
120
121    def _child_test_cleanup(self):
122        try:
123            os.remove(self._loopback_file)
124        except OSError:
125            pass
126