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