Home | History | Annotate | Download | only in audio_AudioQualityAfterSuspend
      1 # Copyright 2015 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 """This is a server side audio test using the Chameleon board."""
      6 
      7 import logging
      8 import os
      9 import tempfile
     10 import time
     11 import threading
     12 
     13 from autotest_lib.client.common_lib import error, file_utils
     14 from autotest_lib.client.cros.chameleon import audio_test_utils
     15 from autotest_lib.client.cros.chameleon import chameleon_audio_helper
     16 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
     17 from autotest_lib.server.cros.audio import audio_test
     18 from autotest_lib.server.cros.multimedia import remote_facade_factory
     19 
     20 
     21 class audio_AudioQualityAfterSuspend(audio_test.AudioTest):
     22     """Server side audio test.
     23 
     24     This test talks to a Chameleon board and a Cros device to verify
     25     audio function of the Cros device after suspend/resume.
     26 
     27     """
     28     version = 1
     29     RECORD_SECONDS = 10
     30     RESUME_TIMEOUT_SECS = 60
     31     SHORT_WAIT = 4
     32     SUSPEND_SECONDS = 30
     33 
     34 
     35     def action_suspend(self, suspend_time=SUSPEND_SECONDS):
     36         """Calls the host method suspend.
     37 
     38         @param suspend_time: time to suspend the device for.
     39 
     40         """
     41         self.host.suspend(suspend_time=suspend_time)
     42 
     43 
     44     def suspend_resume(self):
     45         """Performs suspend/resume."""
     46 
     47         # Suspend
     48         boot_id = self.host.get_boot_id()
     49         thread = threading.Thread(target=self.action_suspend)
     50         thread.start()
     51 
     52         logging.info('Suspend start....')
     53         self.host.test_wait_for_sleep(self.SUSPEND_SECONDS / 3)
     54         logging.info('Waiting for resume....')
     55         self.host.test_wait_for_resume(boot_id, self.RESUME_TIMEOUT_SECS)
     56         logging.info('Resume complete....')
     57 
     58 
     59     def check_correct_audio_node_selected(self):
     60         """Checks the node selected by Cras is correct."""
     61         audio_test_utils.check_audio_nodes(self.audio_facade, self.audio_nodes)
     62 
     63 
     64     def play_and_record(self, recorder_widget):
     65         """Plays and records audio
     66 
     67         @param recorder_widget: widget to do the recording
     68 
     69         """
     70         audio_test_utils.dump_cros_audio_logs(
     71                 self.host, self.audio_facade, self.resultsdir,
     72                 'before_playback')
     73 
     74         self.check_correct_audio_node_selected()
     75 
     76         browser_facade = self.factory.create_browser_facade()
     77 
     78         host_file = os.path.join('/tmp',
     79                 os.path.basename(self.test_playback_file))
     80         with tempfile.NamedTemporaryFile() as tmpfile:
     81             file_utils.download_file(self.test_playback_file, tmpfile.name)
     82             os.chmod(tmpfile.name, 0444)
     83             self.host.send_file(tmpfile.name, host_file)
     84             logging.debug('Copied the file on the DUT at %s', host_file)
     85 
     86         # Play, wait for some time, and then start recording.
     87         # This is to avoid artifact caused by codec initialization.
     88         browser_facade.new_tab('file://' + host_file)
     89         logging.info('Start playing %s on Cros device', host_file)
     90 
     91         time.sleep(self.SHORT_WAIT)
     92         logging.debug('Suspend.')
     93         self.suspend_resume()
     94         logging.debug('Resume.')
     95         time.sleep(self.SHORT_WAIT)
     96         logging.debug('Start recording.')
     97         recorder_widget.start_recording()
     98 
     99         time.sleep(self.RECORD_SECONDS)
    100 
    101         recorder_widget.stop_recording()
    102         logging.debug('Stopped recording.')
    103 
    104         audio_test_utils.dump_cros_audio_logs(
    105                 self.host, self.audio_facade, self.resultsdir,
    106                 'after_recording')
    107 
    108         recorder_widget.read_recorded_binary()
    109 
    110 
    111     def save_and_check_data(self, recorder_widget):
    112         """Saves and checks the data from the recorder
    113 
    114         @param recorder_widget: recorder widget to save data from
    115 
    116         """
    117         recorded_file = os.path.join(self.resultsdir, 'recorded.raw')
    118         logging.debug('Saving recorded data to %s', recorded_file)
    119         recorder_widget.save_file(recorded_file)
    120 
    121         # Removes the beginning of recorded data. This is to avoid artifact
    122         # caused by codec initialization in the beginning of
    123         # recording.
    124         recorder_widget.remove_head(2.0)
    125 
    126         # Removes noise by a lowpass filter.
    127         recorder_widget.lowpass_filter(self.lowpass_freq)
    128         recorded_file = os.path.join(self.resultsdir, 'recorded_filtered.raw')
    129         logging.debug('Saving filtered data to %s', recorded_file)
    130         recorder_widget.save_file(recorded_file)
    131 
    132         # Compares data by frequency and returns the result.
    133         audio_test_utils.check_recorded_frequency(
    134                 self.audio_test_data, recorder_widget,
    135                 second_peak_ratio=self.second_peak_ratio,
    136                 ignore_frequencies=self.ignore_frequencies,
    137                 check_anomaly=True)
    138 
    139 
    140     def run_once(self, host, audio_nodes, audio_test_data, test_playback_file,
    141                  lowpass_freq=None,
    142                  bind_from=None, bind_to=None,
    143                  source=None, recorder=None,
    144                  tag=None):
    145         """Runs the test main workflow
    146 
    147         @param host: A host object representing the DUT.
    148         @param audio_nodes: audio nodes supposed to be selected.
    149         @param audio_test_data: audio test frequency defined in audio_test_data
    150         @param test_playback_file: audio media file(wav, mp3,...) to be used
    151             for testing
    152         @param lowpass_freq: frequency noise filter.
    153         @param bind_from: audio originating entity to be binded
    154             should be defined in chameleon_audio_ids
    155         @param bind_to: audio directed_to entity to be binded
    156             should be defined in chameleon_audio_ids
    157         @param source: source widget entity
    158             should be defined in chameleon_audio_ids
    159         @param recorder: recorder widget entity
    160             should be defined in chameleon_audio_ids
    161 
    162         """
    163         self.host = host
    164         self.audio_nodes = audio_nodes
    165 
    166         if (not audio_test_utils.has_internal_speaker(host) and
    167                 tag == "internal_speaker"):
    168             return
    169 
    170         self.second_peak_ratio = audio_test_utils.get_second_peak_ratio(
    171                 source_id=source,
    172                 recorder_id=recorder)
    173 
    174         self.ignore_frequencies = None
    175         if source == chameleon_audio_ids.CrosIds.SPEAKER:
    176             self.ignore_frequencies = [50, 60]
    177 
    178         self.audio_test_data = audio_test_data
    179         self.lowpass_freq = lowpass_freq
    180         self.test_playback_file = test_playback_file
    181         chameleon_board = self.host.chameleon
    182         self.factory = remote_facade_factory.RemoteFacadeFactory(
    183                 self.host, results_dir=self.resultsdir)
    184         self.audio_facade = self.factory.create_audio_facade()
    185         chameleon_board.setup_and_reset(self.outputdir)
    186         widget_factory = chameleon_audio_helper.AudioWidgetFactory(
    187                 self.factory, host)
    188 
    189         # Two widgets are binded in the factory if necessary.
    190         binder_widget = None
    191         source_widget = None
    192         recorder_widget = None
    193         if bind_from != None and bind_to != None:
    194             source_widget = widget_factory.create_widget(bind_from)
    195             recorder_widget = widget_factory.create_widget(bind_to)
    196             binder_widget = widget_factory.create_binder(source_widget,
    197                                                          recorder_widget)
    198         elif source != None and recorder != None:
    199             source_widget = widget_factory.create_widget(source)
    200             recorder_widget = widget_factory.create_widget(recorder)
    201         else:
    202             raise error.TestError('Test configuration or setup problem.')
    203 
    204         self.audio_board = chameleon_board.get_audio_board()
    205 
    206         if binder_widget:
    207             # Headphone test which requires Chameleon LINEIN and DUT headphones
    208             # binding.
    209             with chameleon_audio_helper.bind_widgets(binder_widget):
    210                 self.play_and_record(recorder_widget)
    211         else:
    212             # Internal speakers test.
    213             self.play_and_record(recorder_widget)
    214 
    215         self.save_and_check_data(recorder_widget)
    216