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 5 import os 6 import logging 7 import time 8 9 from autotest_lib.client.common_lib import error 10 from autotest_lib.client.common_lib.cros import chrome 11 from autotest_lib.client.cros.a11y import a11y_test_base 12 from autotest_lib.client.cros.audio import cras_utils 13 from autotest_lib.client.cros.audio import sox_utils 14 15 16 class 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, max_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 @param max_time: the maximum allowed sound length in seconds. 36 37 @raises: error.TestFail if the observed behavior doesn't match 38 expected: either no sound or sound of bad length. 39 40 """ 41 count = 0 42 counting = 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 break 55 if not counting: 56 raise error.TestFail('No audio for %s was found!' % name) 57 58 logging.info('Time taken - %s: %f', name, audio_length) 59 if audio_length < min_time: 60 raise error.TestFail( 61 '%s audio was only %f seconds long!' % (name, audio_length)) 62 elif audio_length > max_time: 63 raise error.TestFail( 64 '%s audio was too long: %f seconds!' % (name, audio_length)) 65 return 66 67 68 def _rms_of_next_audio_chunk(self): 69 """Finds the sox_stats values of the next chunk of audio.""" 70 cras_utils.loopback(self._loopback_file, channels=1, 71 duration=self._audio_chunk_size) 72 stat_output = sox_utils.get_stat(self._loopback_file) 73 logging.info(stat_output) 74 return vars(stat_output)['rms'] 75 76 77 def _check_chromevox_sound(self, cr): 78 """Test contents. 79 80 Enable ChromeVox, navigate to a new page, and open a new tab. Check 81 the audio output at each point. 82 83 @param cr: the chrome.Chrome() object 84 85 """ 86 chromevox_start_time = time.time() 87 self._toggle_chromevox() 88 self._confirm_chromevox_state(True) 89 90 # TODO: this sound doesn't play for Telemetry user. crbug.com/590403 91 # Welcome ding 92 # self._detect_audio('enable ChromeVox ding', 1, 2) 93 94 # "ChromeVox Spoken Feedback is ready!" 95 self._detect_audio('welcome message', 2, 6) 96 chromevox_open_time = time.time() - chromevox_start_time 97 logging.info('ChromeVox took %f seconds to start.') 98 99 # Page navigation sound. 100 cr.browser.tabs[0].Navigate('chrome://version') 101 self._detect_audio('page navigation sound', 2, 6) 102 103 # New tab sound 104 tab = cr.browser.tabs.New() 105 self._detect_audio('new tab ding', 2, 6) 106 107 108 def run_once(self): 109 """Entry point of this test.""" 110 self._loopback_file = os.path.join(self.bindir, 'cras_loopback.wav') 111 extension_path = self._get_extension_path() 112 113 with chrome.Chrome(extension_paths=[extension_path]) as cr: 114 self._extension = cr.get_extension(extension_path) 115 cr.browser.tabs[0].WaitForDocumentReadyStateToBeComplete() 116 self._confirm_chromevox_state(False) 117 self._check_chromevox_sound(cr) 118 119 120 def _child_test_cleanup(self): 121 try: 122 os.remove(self._loopback_file) 123 except OSError: 124 pass 125