[autotest] Refine audio essential tests

- Extract common setups and checks to AudioTest
- Refine tests to explicitly select the target node
- Refine audio_InternalCardNodes
- Reformat some codes and fix pylint warnings

BUG=b:145369399, b:145368865, b:145369119, b:145368916, b:145367773
TEST=test_that run all tests belong to audio_essential

Change-Id: I67ec547fc65960a7522d08e85ca14f22900986d6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1944096
Reviewed-by: Yu-Hsuan Hsu <yuhsuan@chromium.org>
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
Tested-by: En-Shuo Hsu <enshuo@chromium.org>
Commit-Queue: Kalin Stoyanov <kalin@chromium.org>
diff --git a/client/cros/chameleon/audio_board.py b/client/cros/chameleon/audio_board.py
index 5b2cd1c..a98f5fd 100644
--- a/client/cros/chameleon/audio_board.py
+++ b/client/cros/chameleon/audio_board.py
@@ -1,7 +1,6 @@
 # Copyright 2015 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This module provides the audio board interface."""
 
 import logging
@@ -17,27 +16,23 @@
     A ChameleonConnection object is passed to the construction.
 
     """
+
     def __init__(self, chameleon_connection):
         """Constructs an AudioBoard.
 
         @param chameleon_connection: A ChameleonConnection object.
 
+        @returns: An AudioBoard object.
+
         """
         self._audio_buses = {
                 1: AudioBus(1, chameleon_connection),
-                2: AudioBus(2, chameleon_connection)}
-
+                2: AudioBus(2, chameleon_connection)
+        }
+        self._chameleon_connection = chameleon_connection
         self._jack_plugger = None
-        try:
-            self._jack_plugger = AudioJackPlugger(chameleon_connection)
-        except AudioJackPluggerException:
-            logging.warning(
-                    'There is no jack plugger on this audio board.')
-            self._jack_plugger = None
-
         self._bluetooth_controller = BluetoothController(chameleon_connection)
 
-
     def get_audio_bus(self, bus_index):
         """Gets an audio bus on this audio board.
 
@@ -48,17 +43,29 @@
         """
         return self._audio_buses[bus_index]
 
-
     def get_jack_plugger(self):
         """Gets an AudioJackPlugger on this audio board.
 
         @returns: An AudioJackPlugger object if there is an audio jack plugger.
                   None if there is no audio jack plugger.
+        @raises:
+            AudioJackPluggerException if there is no jack plugger on this audio
+            board.
 
         """
+        if self._jack_plugger is None:
+            try:
+                self._jack_plugger = AudioJackPlugger(
+                        self._chameleon_connection)
+            except AudioJackPluggerException as e:
+                logging.error(
+                        'There is no jack plugger on this audio board. Please '
+                        'check the jack plugger if all labels are correctly '
+                        'configured.')
+                self._jack_plugger = None
+                raise e
         return self._jack_plugger
 
-
     def get_bluetooth_controller(self):
         """Gets an BluetoothController on this audio board.
 
@@ -88,14 +95,13 @@
             ids.CrosIds.EXTERNAL_MIC: 'Cros device external microphone',
             ids.PeripheralIds.SPEAKER: 'Peripheral speaker',
             ids.PeripheralIds.MIC: 'Peripheral microphone',
-            ids.PeripheralIds.BLUETOOTH_DATA_RX:
-                    'Bluetooth module output',
-            ids.PeripheralIds.BLUETOOTH_DATA_TX:
-                    'Bluetooth module input'}
-
+            ids.PeripheralIds.BLUETOOTH_DATA_RX: 'Bluetooth module output',
+            ids.PeripheralIds.BLUETOOTH_DATA_TX: 'Bluetooth module input'
+    }
 
     class AudioBusSnapshot(object):
         """Abstracts the snapshot of AudioBus for user to restore it later."""
+
         def __init__(self, endpoints):
             """Initializes an AudioBusSnapshot.
 
@@ -104,7 +110,6 @@
             """
             self._endpoints = endpoints.copy()
 
-
     def __init__(self, bus_index, chameleon_connection):
         """Constructs an AudioBus.
 
@@ -116,7 +121,6 @@
         self._chameleond_proxy = chameleon_connection.chameleond_proxy
         self._connected_endpoints = set()
 
-
     def _get_endpoint_name(self, port_id):
         """Gets the endpoint name used in audio bus API.
 
@@ -128,33 +132,28 @@
         """
         return self._PORT_ID_AUDIO_BUS_ENDPOINT_MAP[port_id]
 
-
     def _connect_endpoint(self, endpoint):
         """Connects an endpoint to audio bus.
 
         @param endpoint: An endpoint name in _PORT_ID_AUDIO_BUS_ENDPOINT_MAP.
 
         """
-        logging.debug(
-                'Audio bus %s is connecting endpoint %s',
-                self.bus_index, endpoint)
+        logging.debug('Audio bus %s is connecting endpoint %s', self.bus_index,
+                      endpoint)
         self._chameleond_proxy.AudioBoardConnect(self.bus_index, endpoint)
         self._connected_endpoints.add(endpoint)
 
-
     def _disconnect_endpoint(self, endpoint):
         """Disconnects an endpoint from audio bus.
 
         @param endpoint: An endpoint name in _PORT_ID_AUDIO_BUS_ENDPOINT_MAP.
 
         """
-        logging.debug(
-                'Audio bus %s is disconnecting endpoint %s',
-                self.bus_index, endpoint)
+        logging.debug('Audio bus %s is disconnecting endpoint %s',
+                      self.bus_index, endpoint)
         self._chameleond_proxy.AudioBoardDisconnect(self.bus_index, endpoint)
         self._connected_endpoints.remove(endpoint)
 
-
     def connect(self, port_id):
         """Connects an audio port to this audio bus.
 
@@ -165,7 +164,6 @@
         endpoint = self._get_endpoint_name(port_id)
         self._connect_endpoint(endpoint)
 
-
     def disconnect(self, port_id):
         """Disconnects an audio port from this audio bus.
 
@@ -176,18 +174,15 @@
         endpoint = self._get_endpoint_name(port_id)
         self._disconnect_endpoint(endpoint)
 
-
     def clear(self):
         """Disconnects all audio port from this audio bus."""
         self._disconnect_all_endpoints()
 
-
     def _disconnect_all_endpoints(self):
         """Disconnects all endpoints from this audio bus."""
         for endpoint in self._connected_endpoints.copy():
             self._disconnect_endpoint(endpoint)
 
-
     def get_snapshot(self):
         """Gets the snapshot of AudioBus so user can restore it later.
 
@@ -196,7 +191,6 @@
         """
         return self.AudioBusSnapshot(self._connected_endpoints)
 
-
     def restore_snapshot(self, snapshot):
         """Restore the snapshot.
 
@@ -224,6 +218,7 @@
     A ChameleonConnection object is passed to the construction.
 
     """
+
     def __init__(self, chameleon_connection):
         """Constructs an AudioJackPlugger.
 
@@ -237,20 +232,18 @@
         self._chameleond_proxy = chameleon_connection.chameleond_proxy
         if not self._chameleond_proxy.AudioBoardHasJackPlugger():
             raise AudioJackPluggerException(
-                'There is no jack plugger on audio board. '
-                'Perhaps the audio board is not connected to audio box.')
-
+                    'There is no jack plugger on audio board. '
+                    'Perhaps the audio board is not connected to audio box.')
 
     def plug(self):
         """Plugs the audio cable into audio jack of Cros device."""
         self._chameleond_proxy.AudioBoardAudioJackPlug()
-        logging.info('Plugged 3.5mm audio cable to Cros device')
-
+        logging.info('Plugged 3.5mm audio cable to Cros device.')
 
     def unplug(self):
         """Unplugs the audio cable from audio jack of Cros device."""
         self._chameleond_proxy.AudioBoardAudioJackUnplug()
-        logging.info('Unplugged 3.5mm audio cable from Cros device')
+        logging.info('Unplugged 3.5mm audio cable from Cros device.')
 
 
 class BluetoothController(object):
@@ -260,6 +253,7 @@
     API provided by chameleon proxy.
 
     """
+
     def __init__(self, chameleon_connection):
         """Constructs an BluetoothController.
 
@@ -268,19 +262,16 @@
         """
         self._chameleond_proxy = chameleon_connection.chameleond_proxy
 
-
     def reset(self):
         """Resets the bluetooth module."""
         self._chameleond_proxy.AudioBoardResetBluetooth()
         logging.info('Resets bluetooth module on audio board.')
 
-
     def disable(self):
         """Disables the bluetooth module."""
         self._chameleond_proxy.AudioBoardDisableBluetooth()
         logging.info('Disables bluetooth module on audio board.')
 
-
     def is_enabled(self):
         """Checks if the bluetooth module is enabled.
 
diff --git a/client/cros/chameleon/audio_test_utils.py b/client/cros/chameleon/audio_test_utils.py
index 3687ebb..79f02d9 100644
--- a/client/cros/chameleon/audio_test_utils.py
+++ b/client/cros/chameleon/audio_test_utils.py
@@ -1,7 +1,6 @@
 # Copyright 2015 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This module provides the test utilities for audio tests using chameleon."""
 
 # TODO (cychiang) Move test utilities from chameleon_audio_helpers
@@ -24,15 +23,15 @@
 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
 
 CHAMELEON_AUDIO_IDS_TO_CRAS_NODE_TYPES = {
-       chameleon_audio_ids.CrosIds.HDMI: 'HDMI',
-       chameleon_audio_ids.CrosIds.HEADPHONE: 'HEADPHONE',
-       chameleon_audio_ids.CrosIds.EXTERNAL_MIC: 'MIC',
-       chameleon_audio_ids.CrosIds.SPEAKER: 'INTERNAL_SPEAKER',
-       chameleon_audio_ids.CrosIds.INTERNAL_MIC: 'INTERNAL_MIC',
-       chameleon_audio_ids.CrosIds.BLUETOOTH_HEADPHONE: 'BLUETOOTH',
-       chameleon_audio_ids.CrosIds.BLUETOOTH_MIC: 'BLUETOOTH',
-       chameleon_audio_ids.CrosIds.USBIN: 'USB',
-       chameleon_audio_ids.CrosIds.USBOUT: 'USB',
+        chameleon_audio_ids.CrosIds.HDMI: 'HDMI',
+        chameleon_audio_ids.CrosIds.HEADPHONE: 'HEADPHONE',
+        chameleon_audio_ids.CrosIds.EXTERNAL_MIC: 'MIC',
+        chameleon_audio_ids.CrosIds.SPEAKER: 'INTERNAL_SPEAKER',
+        chameleon_audio_ids.CrosIds.INTERNAL_MIC: 'INTERNAL_MIC',
+        chameleon_audio_ids.CrosIds.BLUETOOTH_HEADPHONE: 'BLUETOOTH',
+        chameleon_audio_ids.CrosIds.BLUETOOTH_MIC: 'BLUETOOTH',
+        chameleon_audio_ids.CrosIds.USBIN: 'USB',
+        chameleon_audio_ids.CrosIds.USBOUT: 'USB',
 }
 
 
@@ -81,9 +80,10 @@
     """
     curr_out_nodes, curr_in_nodes = audio_facade.get_selected_node_types()
     out_audio_nodes, in_audio_nodes = audio_nodes
-    if (in_audio_nodes != None and
-        sorted(curr_in_nodes) != sorted(in_audio_nodes)):
-        raise error.TestFail('Wrong input node(s) selected: %s '
+    if (in_audio_nodes != None
+                and sorted(curr_in_nodes) != sorted(in_audio_nodes)):
+        raise error.TestFail(
+                'Wrong input node(s) selected: %s '
                 'expected: %s' % (str(curr_in_nodes), str(in_audio_nodes)))
 
     # Treat line-out node as headphone node in Chameleon test since some
@@ -92,9 +92,10 @@
     if (out_audio_nodes == ['HEADPHONE'] and curr_out_nodes == ['LINEOUT']):
         return
 
-    if (out_audio_nodes != None and
-        sorted(curr_out_nodes) != sorted(out_audio_nodes)):
-        raise error.TestFail('Wrong output node(s) selected %s '
+    if (out_audio_nodes != None
+                and sorted(curr_out_nodes) != sorted(out_audio_nodes)):
+        raise error.TestFail(
+                'Wrong output node(s) selected %s '
                 'expected: %s' % (str(curr_out_nodes), str(out_audio_nodes)))
 
 
@@ -116,12 +117,13 @@
         for node in in_audio_nodes:
             if node not in curr_in_nodes:
                 raise error.TestFail('Wrong input node(s) plugged: %s '
-                        'expected %s to be plugged!' % (str(curr_in_nodes),
-                                                        str(in_audio_nodes)))
+                                     'expected %s to be plugged!' %
+                                     (str(curr_in_nodes), str(in_audio_nodes)))
     if out_audio_nodes != None:
         for node in out_audio_nodes:
             if node not in curr_out_nodes:
-                raise error.TestFail('Wrong output node(s) plugged: %s '
+                raise error.TestFail(
+                        'Wrong output node(s) plugged: %s '
                         'expected %s to be plugged!' % (str(curr_out_nodes),
                                                         str(out_audio_nodes)))
 
@@ -140,14 +142,16 @@
     """
     curr_out_nodes, curr_in_nodes = audio_facade.get_plugged_node_types()
     out_audio_nodes, in_audio_nodes = audio_nodes
-    if (in_audio_nodes != None and
-        sorted(curr_in_nodes) != sorted(in_audio_nodes)):
+    if (in_audio_nodes != None
+                and sorted(curr_in_nodes) != sorted(in_audio_nodes)):
         raise error.TestFail('Wrong input node(s) plugged: %s '
-                'expected: %s!' % (str(curr_in_nodes), str(in_audio_nodes)))
-    if (out_audio_nodes != None and
-        sorted(curr_out_nodes) != sorted(out_audio_nodes)):
+                             'expected: %s!' % (str(sorted(curr_in_nodes)),
+                                                str(sorted(in_audio_nodes))))
+    if (out_audio_nodes != None
+                and sorted(curr_out_nodes) != sorted(out_audio_nodes)):
         raise error.TestFail('Wrong output node(s) plugged: %s '
-                'expected: %s!' % (str(curr_out_nodes), str(out_audio_nodes)))
+                             'expected: %s!' % (str(sorted(curr_out_nodes)),
+                                                str(sorted(out_audio_nodes))))
 
 
 def bluetooth_nodes_plugged(audio_facade):
@@ -241,6 +245,7 @@
     @resume_network_timeout_secs: Time in seconds to let Cros device resume and
                                   obtain network.
     """
+
     def action_suspend():
         """Calls the host method suspend."""
         host.suspend(suspend_time=suspend_time_secs)
@@ -252,12 +257,15 @@
     proc.start()
     host.test_wait_for_sleep(suspend_time_secs)
     logging.info("DUT suspended! Waiting to resume...")
-    host.test_wait_for_resume(
-            boot_id, suspend_time_secs + resume_network_timeout_secs)
+    host.test_wait_for_resume(boot_id,
+                              suspend_time_secs + resume_network_timeout_secs)
     logging.info("DUT resumed!")
 
 
-def dump_cros_audio_logs(host, audio_facade, directory, suffix='',
+def dump_cros_audio_logs(host,
+                         audio_facade,
+                         directory,
+                         suffix='',
                          fail_if_warnings=False):
     """Dumps logs for audio debugging from Cros device.
 
@@ -267,6 +275,7 @@
     @directory: The directory to dump logs.
 
     """
+
     def get_file_path(name):
         """Gets file path to dump logs.
 
@@ -288,8 +297,8 @@
 
     # Raising error if any warning messages in the audio diagnostics
     if fail_if_warnings:
-        audio_logs = examine_audio_diagnostics(get_file_path(
-                'audio_diagnostics.txt'))
+        audio_logs = examine_audio_diagnostics(
+                get_file_path('audio_diagnostics.txt'))
         if audio_logs != '':
             raise error.TestFail(audio_logs)
 
@@ -315,9 +324,8 @@
             if search_result:
                 num_underruns = int(search_result.group(1))
                 if num_underruns != 0:
-                    warning_msgs.append(
-                            'Found %d underrun at line %d: %s' % (
-                                    num_underruns, line_number, line))
+                    warning_msgs.append('Found %d underrun at line %d: %s' %
+                                        (num_underruns, line_number, line))
 
             # TODO(cychiang) add other check like maximum client reply delay.
             line_number = line_number + 1
@@ -387,6 +395,7 @@
 _DC_FREQ_THRESHOLD = 0.001
 _DC_COEFF_THRESHOLD = 0.01
 
+
 def get_second_peak_ratio(source_id, recorder_id, is_hsp=False):
     """Gets the second peak ratio suitable for use case.
 
@@ -410,12 +419,17 @@
 # The deviation of estimated dominant frequency from golden frequency.
 DEFAULT_FREQUENCY_DIFF_THRESHOLD = 5
 
+
 def check_recorded_frequency(
-        golden_file, recorder,
+        golden_file,
+        recorder,
         second_peak_ratio=_DEFAULT_SECOND_PEAK_RATIO,
         frequency_diff_threshold=DEFAULT_FREQUENCY_DIFF_THRESHOLD,
-        ignore_frequencies=None, check_anomaly=False, check_artifacts=False,
-        mute_durations=None, volume_changes=None,
+        ignore_frequencies=None,
+        check_anomaly=False,
+        check_artifacts=False,
+        mute_durations=None,
+        volume_changes=None,
         tolerant_noise_level=DEFAULT_TOLERANT_NOISE_LEVEL):
     """Checks if the recorded data contains sine tone of golden frequency.
 
@@ -479,27 +493,27 @@
         normalized_signal = audio_analysis.normalize_signal(
                 signal, saturate_value)
         logging.debug('saturate_value: %f', saturate_value)
-        logging.debug('max signal after normalized: %f', max(normalized_signal))
-        spectral = audio_analysis.spectral_analysis(
-                normalized_signal, data_format['rate'])
+        logging.debug('max signal after normalized: %f',
+                      max(normalized_signal))
+        spectral = audio_analysis.spectral_analysis(normalized_signal,
+                                                    data_format['rate'])
         logging.debug('spectral: %s', spectral)
 
         if not spectral:
-            errors.append(
-                    'Channel %d: Can not find dominant frequency.' %
-                            test_channel)
+            errors.append('Channel %d: Can not find dominant frequency.' %
+                          test_channel)
 
         golden_frequency = golden_file.frequencies[golden_channel]
         logging.debug('Checking channel %s spectral %s against frequency %s',
-                test_channel, spectral, golden_frequency)
+                      test_channel, spectral, golden_frequency)
 
         dominant_frequency = spectral[0][0]
 
         if (abs(dominant_frequency - golden_frequency) >
-            frequency_diff_threshold):
+                    frequency_diff_threshold):
             errors.append(
-                    'Channel %d: Dominant frequency %s is away from golden %s' %
-                    (test_channel, dominant_frequency, golden_frequency))
+                    'Channel %d: Dominant frequency %s is away from golden %s'
+                    % (test_channel, dominant_frequency, golden_frequency))
 
         if check_anomaly:
             detected_anomaly = audio_analysis.anomaly_detection(
@@ -507,9 +521,8 @@
                     rate=data_format['rate'],
                     freq=golden_frequency)
             if detected_anomaly:
-                errors.append(
-                        'Channel %d: Detect anomaly near these time: %s' %
-                        (test_channel, detected_anomaly))
+                errors.append('Channel %d: Detect anomaly near these time: %s'
+                              % (test_channel, detected_anomaly))
             else:
                 logging.info(
                         'Channel %d: Quality is good as there is no anomaly',
@@ -517,24 +530,26 @@
 
         if check_artifacts or mute_durations or volume_changes:
             result = audio_quality_measurement.quality_measurement(
-                                        normalized_signal,
-                                        data_format['rate'],
-                                        dominant_frequency=dominant_frequency)
-            logging.debug('Quality measurement result:\n%s', pprint.pformat(result))
+                    normalized_signal,
+                    data_format['rate'],
+                    dominant_frequency=dominant_frequency)
+            logging.debug('Quality measurement result:\n%s',
+                          pprint.pformat(result))
             if check_artifacts:
                 if len(result['artifacts']['noise_before_playback']) > 0:
                     errors.append(
-                        'Channel %d: Detects artifacts before playing near'
-                        ' these time and duration: %s' %
-                        (test_channel,
-                         str(result['artifacts']['noise_before_playback'])))
+                            'Channel %d: Detects artifacts before playing near'
+                            ' these time and duration: %s' %
+                            (test_channel,
+                             str(result['artifacts']['noise_before_playback']))
+                    )
 
                 if len(result['artifacts']['noise_after_playback']) > 0:
                     errors.append(
-                        'Channel %d: Detects artifacts after playing near'
-                        ' these time and duration: %s' %
-                        (test_channel,
-                         str(result['artifacts']['noise_after_playback'])))
+                            'Channel %d: Detects artifacts after playing near'
+                            ' these time and duration: %s' %
+                            (test_channel,
+                             str(result['artifacts']['noise_after_playback'])))
 
             if mute_durations:
                 delays = result['artifacts']['delay_during_playback']
@@ -542,45 +557,46 @@
                 for x in delays:
                     delay_durations.append(x[1])
                 mute_matched, delay_matched = longest_common_subsequence(
-                        mute_durations,
-                        delay_durations,
+                        mute_durations, delay_durations,
                         DEFAULT_EQUIVALENT_THRESHOLD)
 
                 # updated delay list
-                new_delays = [delays[i]
-                                for i in delay_matched if not delay_matched[i]]
+                new_delays = [
+                        delays[i] for i in delay_matched
+                        if not delay_matched[i]
+                ]
 
                 result['artifacts']['delay_during_playback'] = new_delays
 
-                unmatched_mutes = [mute_durations[i]
-                                for i in mute_matched if not mute_matched[i]]
+                unmatched_mutes = [
+                        mute_durations[i] for i in mute_matched
+                        if not mute_matched[i]
+                ]
 
                 if len(unmatched_mutes) > 0:
-                    errors.append(
-                        'Channel %d: Unmatched mute duration: %s' %
-                        (test_channel, unmatched_mutes))
+                    errors.append('Channel %d: Unmatched mute duration: %s' %
+                                  (test_channel, unmatched_mutes))
 
             if check_artifacts:
                 if len(result['artifacts']['delay_during_playback']) > 0:
                     errors.append(
-                        'Channel %d: Detects delay during playing near'
-                        ' these time and duration: %s' %
-                        (test_channel,
-                         result['artifacts']['delay_during_playback']))
+                            'Channel %d: Detects delay during playing near'
+                            ' these time and duration: %s' %
+                            (test_channel,
+                             result['artifacts']['delay_during_playback']))
 
                 if len(result['artifacts']['burst_during_playback']) > 0:
                     errors.append(
-                        'Channel %d: Detects burst/pop near these time: %s' %
-                        (test_channel,
-                         result['artifacts']['burst_during_playback']))
+                            'Channel %d: Detects burst/pop near these time: %s'
+                            % (test_channel,
+                               result['artifacts']['burst_during_playback']))
 
                 if result['equivalent_noise_level'] > tolerant_noise_level:
                     errors.append(
-                        'Channel %d: noise level is higher than tolerant'
-                        ' noise level: %f > %f' %
-                        (test_channel,
-                         result['equivalent_noise_level'],
-                         tolerant_noise_level))
+                            'Channel %d: noise level is higher than tolerant'
+                            ' noise level: %f > %f' %
+                            (test_channel, result['equivalent_noise_level'],
+                             tolerant_noise_level))
 
             if volume_changes:
                 matched = True
@@ -594,12 +610,10 @@
                             break
                 if not matched:
                     errors.append(
-                        'Channel %d: volume changing is not as expected, '
-                        'found changing time and events are: %s while '
-                        'expected changing events are %s'%
-                        (test_channel,
-                         volume_changing,
-                         volume_changes))
+                            'Channel %d: volume changing is not as expected, '
+                            'found changing time and events are: %s while '
+                            'expected changing events are %s' %
+                            (test_channel, volume_changing, volume_changes))
 
         # Filter out the harmonics resulted from imperfect sin wave.
         # This list is different for different channels.
@@ -617,23 +631,23 @@
             @returns: True if the frequency should be ignored. False otherwise.
 
             """
-            for ignore_frequency in (ignore_frequencies_harmonics + harmonics
-                                     + [0.0]):
+            for ignore_frequency in (
+                    ignore_frequencies_harmonics + harmonics + [0.0]):
                 if (abs(frequency - ignore_frequency) <
-                    frequency_diff_threshold):
+                            frequency_diff_threshold):
                     logging.debug('Ignore frequency: %s', frequency)
                     return True
 
         # Checks DC is small enough.
         for freq, coeff in spectral:
             if freq < _DC_FREQ_THRESHOLD and coeff > _DC_COEFF_THRESHOLD:
-                errors.append(
-                        'Channel %d: Found large DC coefficient: '
-                        '(%f Hz, %f)' % (test_channel, freq, coeff))
+                errors.append('Channel %d: Found large DC coefficient: '
+                              '(%f Hz, %f)' % (test_channel, freq, coeff))
 
         # Filter out the frequencies to be ignored.
         spectral_post_ignore = [
-                x for x in spectral if not should_be_ignored(x[0])]
+                x for x in spectral if not should_be_ignored(x[0])
+        ]
 
         if len(spectral_post_ignore) > 1:
             first_coeff = spectral_post_ignore[0][1]
@@ -647,8 +661,8 @@
             errors.append(
                     'Channel %d: No frequency left after removing unwanted '
                     'frequencies. Spectral: %s; After removing unwanted '
-                    'frequencies: %s' %
-                    (test_channel, spectral, spectral_post_ignore))
+                    'frequencies: %s' % (test_channel, spectral,
+                                         spectral_post_ignore))
 
         else:
             dominant_spectrals.append(spectral_post_ignore[0])
@@ -724,7 +738,9 @@
     audio_facade.set_chrome_active_node_type(None, 'BLUETOOTH')
     check_audio_nodes(audio_facade, (None, ['BLUETOOTH']))
     audio_facade.start_recording(
-            dict(file_type='raw', sample_format='S16_LE', channel=2,
+            dict(file_type='raw',
+                 sample_format='S16_LE',
+                 channel=2,
                  rate=48000))
 
 
@@ -747,31 +763,30 @@
             parameters)
 
 
-def check_and_set_chrome_active_node_types(audio_facade, output_type=None,
+def check_and_set_chrome_active_node_types(audio_facade,
+                                           output_type=None,
                                            input_type=None):
-   """Check the target types are available, and set them to be active nodes.
+    """Check the target types are available, and set them to be active nodes.
 
-   @param audio_facade: An AudioFacadeNative or AudioFacadeAdapter object.
-   @output_type: An output node type defined in cras_utils.CRAS_NODE_TYPES.
+    @param audio_facade: An AudioFacadeNative or AudioFacadeAdapter object.
+    @output_type: An output node type defined in cras_utils.CRAS_NODE_TYPES.
                  None to skip.
-   @input_type: An input node type defined in cras_utils.CRAS_NODE_TYPES.
+    @input_type: An input node type defined in cras_utils.CRAS_NODE_TYPES.
                  None to skip.
 
-   @raises: error.TestError if the expected node type is missing. We use
-            error.TestError here because usually this step is not the main
-            purpose of the test, but a setup step.
+    @raises: error.TestError if the expected node type is missing. We use
+             error.TestError here because usually this step is not the main
+             purpose of the test, but a setup step.
 
-   """
-   output_types, input_types = audio_facade.get_plugged_node_types()
-   logging.debug('Plugged types: output: %r, input: %r',
-                 output_types, input_types)
-   if output_type and output_type not in output_types:
-       raise error.TestError(
-               'Target output type %s not present' % output_type)
-   if input_type and input_type not in input_types:
-       raise error.TestError(
-               'Target input type %s not present' % input_type)
-   audio_facade.set_chrome_active_node_type(output_type, input_type)
+    """
+    output_types, input_types = audio_facade.get_plugged_node_types()
+    if output_type and output_type not in output_types:
+        raise error.TestError('Target output type %s not present in %r' %
+                              (output_type, output_types))
+    if input_type and input_type not in input_types:
+        raise error.TestError('Target input type %s not present in %r' %
+                              (input_type, input_types))
+    audio_facade.set_chrome_active_node_type(output_type, input_type)
 
 
 def check_hp_or_lineout_plugged(audio_facade):
@@ -793,7 +808,9 @@
         return 'LINEOUT'
     if 'HEADPHONE' in output_nodes:
         return 'HEADPHONE'
-    raise error.TestFail('Can not detect line-out or headphone')
+    raise error.TestFail(
+            'No line-out or headphone in plugged nodes:%r.'
+            'Please check the audio cable or jack plugger.' % output_nodes)
 
 
 def get_internal_mic_node(host):
diff --git a/server/cros/audio/audio_test.py b/server/cros/audio/audio_test.py
index ee21d6b..9d72a58 100644
--- a/server/cros/audio/audio_test.py
+++ b/server/cros/audio/audio_test.py
@@ -6,26 +6,46 @@
 
 from autotest_lib.client.bin import utils
 from autotest_lib.client.common_lib import error
+from autotest_lib.client.cros.chameleon import chameleon_audio_helper
 from autotest_lib.server import site_utils
 from autotest_lib.server import test
+from autotest_lib.server.cros.multimedia import remote_facade_factory
 from autotest_lib.site_utils import lxc
 
 
 class AudioTest(test.test):
     """Base class for audio tests.
 
-    AudioTest provides a common warmup() function for the collection
-    of audio tests.
-    It is not mandatory to use this base class for audio tests, it is for
-    convenience only.
-
+    AudioTest provides basic initialization, setup and warmup check for the
+    collection of server side audio tests. It is assumed to be run with a
+    Chameleon audio boards. It is recommended to use this base class for server
+    side Chameleon audio tests to take advantage of the initialize, setup and
+    sanity check.
     """
 
+    def initialize(self, host):
+        """Initialize audio test needed components and do some sanity checks"""
+        if host.chameleon is None:
+            raise error.TestError("host.chameleon is None."
+                                  "Please check the chameleon of this DUT.")
+
+        self.host = host
+        self.factory = remote_facade_factory.RemoteFacadeFactory(
+            host, results_dir=self.resultsdir)
+        self.widget_factory = chameleon_audio_helper.AudioWidgetFactory(
+            self.factory, self.host)
+        self.facade = self.factory.create_audio_facade()
+        host.chameleon.setup_and_reset(self.resultsdir)
+
+    def setup(self):
+        """Setup needed audio test requirement before executing main logic."""
+        super(AudioTest, self).setup()
+        audio_test_requirement()
+
     def warmup(self):
         """Warmup for the test before executing main logic of the test."""
         # test.test is an old-style class.
         test.test.warmup(self)
-        audio_test_requirement()
 
 
 def audio_test_requirement():
diff --git a/server/site_tests/audio_AudioBasicExternalMicrophone/audio_AudioBasicExternalMicrophone.py b/server/site_tests/audio_AudioBasicExternalMicrophone/audio_AudioBasicExternalMicrophone.py
index ac2e9d4..98fd3e6 100644
--- a/server/site_tests/audio_AudioBasicExternalMicrophone/audio_AudioBasicExternalMicrophone.py
+++ b/server/site_tests/audio_AudioBasicExternalMicrophone/audio_AudioBasicExternalMicrophone.py
@@ -1,20 +1,17 @@
 # Copyright 2015 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This is a server side external microphone test using the Chameleon board."""
 
 import logging
 import os
 import time
 
-from autotest_lib.client.common_lib import error
 from autotest_lib.client.cros.audio import audio_test_data
 from autotest_lib.client.cros.chameleon import audio_test_utils
 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
 from autotest_lib.client.cros.chameleon import chameleon_audio_helper
 from autotest_lib.server.cros.audio import audio_test
-from autotest_lib.server.cros.multimedia import remote_facade_factory
 
 
 class audio_AudioBasicExternalMicrophone(audio_test.AudioTest):
@@ -29,43 +26,30 @@
     RECORD_SECONDS = 9
     DELAY_AFTER_BINDING = 0.5
 
-    def run_once(self, host, check_quality=False):
+    def run_once(self, check_quality=False):
         """Running basic headphone audio tests.
 
-        @param host: device under test host
         @param check_quality: flag to check audio quality.
 
         """
         golden_file = audio_test_data.SIMPLE_FREQUENCY_TEST_1330_FILE
 
-        chameleon_board = host.chameleon
-        factory = remote_facade_factory.RemoteFacadeFactory(
-                host, results_dir=self.resultsdir)
-
-        chameleon_board.setup_and_reset(self.outputdir)
-
-        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
-                factory, host)
-
-        source = widget_factory.create_widget(
-            chameleon_audio_ids.ChameleonIds.LINEOUT)
-        recorder = widget_factory.create_widget(
-            chameleon_audio_ids.CrosIds.EXTERNAL_MIC)
-        binder = widget_factory.create_binder(source, recorder)
+        source = self.widget_factory.create_widget(
+                chameleon_audio_ids.ChameleonIds.LINEOUT)
+        recorder = self.widget_factory.create_widget(
+                chameleon_audio_ids.CrosIds.EXTERNAL_MIC)
+        binder = self.widget_factory.create_binder(source, recorder)
 
         with chameleon_audio_helper.bind_widgets(binder):
             # Checks the node selected by cras is correct.
             time.sleep(self.DELAY_AFTER_BINDING)
-            audio_facade = factory.create_audio_facade()
 
             audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir, 'after_binding')
+                    self.host, self.facade, self.resultsdir, 'after_binding')
 
-            _, input_nodes = audio_facade.get_selected_node_types()
-            if input_nodes != ['MIC']:
-                raise error.TestFail(
-                        '%s rather than external mic is selected on Cros '
-                        'device' % input_nodes)
+            # Selects and checks the node selected by cras is correct.
+            audio_test_utils.check_and_set_chrome_active_node_types(
+                    self.facade, None, 'MIC')
 
             logging.info('Setting playback data on Chameleon')
             source.set_playback_data(golden_file)
@@ -73,8 +57,7 @@
             # Starts playing, waits for some time, and then starts recording.
             # This is to avoid artifact caused by chameleon codec initialization
             # in the beginning of playback.
-            logging.info('Start playing %s from Chameleon',
-                         golden_file.path)
+            logging.info('Start playing %s from Chameleon', golden_file.path)
             source.start_playback()
 
             time.sleep(self.DELAY_BEFORE_RECORD_SECONDS)
@@ -87,7 +70,7 @@
             logging.info('Stopped recording from Cros device.')
 
             audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir, 'after_recording')
+                    self.host, self.facade, self.resultsdir, 'after_recording')
 
             recorder.read_recorded_binary()
             logging.info('Read recorded binary from Cros device.')
@@ -112,5 +95,7 @@
         # correlation, which is suitable for fully-digital audio path like USB
         # and HDMI.
         audio_test_utils.check_recorded_frequency(
-                golden_file, recorder, check_artifacts=check_quality,
+                golden_file,
+                recorder,
+                check_artifacts=check_quality,
                 ignore_frequencies=[50, 60])
diff --git a/server/site_tests/audio_AudioBasicHeadphone/audio_AudioBasicHeadphone.py b/server/site_tests/audio_AudioBasicHeadphone/audio_AudioBasicHeadphone.py
index c2ca4b0..ec6f1d5 100644
--- a/server/site_tests/audio_AudioBasicHeadphone/audio_AudioBasicHeadphone.py
+++ b/server/site_tests/audio_AudioBasicHeadphone/audio_AudioBasicHeadphone.py
@@ -1,20 +1,17 @@
 # Copyright 2014 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This is a server side headphone audio test using the Chameleon board."""
 
 import logging
 import os
 import time
 
-from autotest_lib.client.common_lib import error
 from autotest_lib.client.cros.audio import audio_test_data
 from autotest_lib.client.cros.chameleon import audio_test_utils
 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
 from autotest_lib.client.cros.chameleon import chameleon_audio_helper
 from autotest_lib.server.cros.audio import audio_test
-from autotest_lib.server.cros.multimedia import remote_facade_factory
 
 
 class audio_AudioBasicHeadphone(audio_test.AudioTest):
@@ -30,50 +27,38 @@
     DELAY_AFTER_BINDING = 0.5
     SILENCE_WAIT = 5
 
-    def run_once(self, host, check_quality=False):
+    def run_once(self, check_quality=False):
         """Running basic headphone audio tests.
 
-        @param host: device under test host
         @param check_quality: flag to check audio quality.
-
         """
-        if not audio_test_utils.has_headphone(host):
+        if not audio_test_utils.has_headphone(self.host):
             return
 
         golden_file = audio_test_data.FREQUENCY_TEST_FILE
 
-        chameleon_board = host.chameleon
-        factory = remote_facade_factory.RemoteFacadeFactory(
-                host, results_dir=self.resultsdir)
-
-        chameleon_board.setup_and_reset(self.outputdir)
-
-        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
-                factory, host)
-
-        source = widget_factory.create_widget(
-            chameleon_audio_ids.CrosIds.HEADPHONE)
-        recorder = widget_factory.create_widget(
-            chameleon_audio_ids.ChameleonIds.LINEIN)
-        binder = widget_factory.create_binder(source, recorder)
+        source = self.widget_factory.create_widget(
+                chameleon_audio_ids.CrosIds.HEADPHONE)
+        recorder = self.widget_factory.create_widget(
+                chameleon_audio_ids.ChameleonIds.LINEIN)
+        binder = self.widget_factory.create_binder(source, recorder)
 
         with chameleon_audio_helper.bind_widgets(binder):
-            # Checks the node selected by cras is correct.
             time.sleep(self.DELAY_AFTER_BINDING)
-            audio_facade = factory.create_audio_facade()
 
             audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir, 'after_binding')
+                    self.host, self.facade, self.resultsdir, 'after_binding')
 
-            audio_test_utils.check_audio_nodes(audio_facade, (['HEADPHONE'], None))
+            # Selects and checks the node selected by cras is correct.
+            audio_test_utils.check_and_set_chrome_active_node_types(
+                    self.facade, 'HEADPHONE', None)
 
             logging.info('Setting playback data on Cros device')
             source.set_playback_data(golden_file)
 
             # Starts playing, waits for some time, and then starts recording.
             # This is to avoid artifact caused by codec initialization.
-            logging.info('Start playing %s on Cros device',
-                         golden_file.path)
+            logging.info('Start playing %s on Cros device', golden_file.path)
             source.start_playback()
 
             time.sleep(self.DELAY_BEFORE_RECORD_SECONDS)
@@ -91,7 +76,7 @@
             logging.info('Stopped recording from Chameleon.')
 
             audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir, 'after_recording')
+                    self.host, self.facade, self.resultsdir, 'after_recording')
 
             recorder.read_recorded_binary()
             logging.info('Read recorded binary from Chameleon.')
diff --git a/server/site_tests/audio_AudioBasicInternalMicrophone/audio_AudioBasicInternalMicrophone.py b/server/site_tests/audio_AudioBasicInternalMicrophone/audio_AudioBasicInternalMicrophone.py
index 1f1db8b..65627db 100644
--- a/server/site_tests/audio_AudioBasicInternalMicrophone/audio_AudioBasicInternalMicrophone.py
+++ b/server/site_tests/audio_AudioBasicInternalMicrophone/audio_AudioBasicInternalMicrophone.py
@@ -1,7 +1,6 @@
 # Copyright 2015 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This is a server side internal microphone test using the Chameleon board."""
 
 import logging
@@ -10,10 +9,9 @@
 
 from autotest_lib.client.cros.audio import audio_test_data
 from autotest_lib.client.cros.chameleon import audio_test_utils
-from autotest_lib.client.cros.chameleon import chameleon_audio_helper
 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
+from autotest_lib.client.cros.chameleon import chameleon_audio_helper
 from autotest_lib.server.cros.audio import audio_test
-from autotest_lib.server.cros.multimedia import remote_facade_factory
 
 
 class audio_AudioBasicInternalMicrophone(audio_test.AudioTest):
@@ -28,47 +26,33 @@
     RECORD_SECONDS = 9
     DELAY_AFTER_BINDING = 0.5
 
-    def run_once(self, host):
-        """Runs Basic Audio Microphone test.
-
-        @param host: device under test CrosHost
-        """
-        if not audio_test_utils.has_internal_microphone(host):
+    def run_once(self):
+        """Runs Basic Audio Microphone test."""
+        if not audio_test_utils.has_internal_microphone(self.host):
             return
 
         golden_file = audio_test_data.SIMPLE_FREQUENCY_TEST_1330_FILE
 
-        chameleon_board = host.chameleon
-        factory = remote_facade_factory.RemoteFacadeFactory(
-                host, results_dir=self.resultsdir)
+        source = self.widget_factory.create_widget(
+                chameleon_audio_ids.ChameleonIds.LINEOUT)
+        sink = self.widget_factory.create_widget(
+                chameleon_audio_ids.PeripheralIds.SPEAKER)
+        binder = self.widget_factory.create_binder(source, sink)
 
-        chameleon_board.setup_and_reset(self.outputdir)
-
-        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
-                factory, host)
-
-        source = widget_factory.create_widget(
-            chameleon_audio_ids.ChameleonIds.LINEOUT)
-        sink = widget_factory.create_widget(
-            chameleon_audio_ids.PeripheralIds.SPEAKER)
-        binder = widget_factory.create_binder(source, sink)
-
-        recorder = widget_factory.create_widget(
-            chameleon_audio_ids.CrosIds.INTERNAL_MIC)
+        recorder = self.widget_factory.create_widget(
+                chameleon_audio_ids.CrosIds.INTERNAL_MIC)
 
         with chameleon_audio_helper.bind_widgets(binder):
             # Checks the node selected by cras is correct.
             time.sleep(self.DELAY_AFTER_BINDING)
-            audio_facade = factory.create_audio_facade()
 
             audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir, 'after_binding')
+                    self.host, self.facade, self.resultsdir, 'after_binding')
 
-            expected_internal_mic_node = audio_test_utils.get_internal_mic_node(
-                    host)
-
-            audio_test_utils.check_audio_nodes(
-                    audio_facade, (None, [expected_internal_mic_node]))
+            # Selects and checks the node selected by cras is correct.
+            audio_test_utils.check_and_set_chrome_active_node_types(
+                    self.facade, None,
+                    audio_test_utils.get_internal_mic_node(self.host))
 
             logging.info('Setting playback data on Chameleon')
             source.set_playback_data(golden_file)
@@ -76,8 +60,7 @@
             # Starts playing, waits for some time, and then starts recording.
             # This is to avoid artifact caused by chameleon codec initialization
             # in the beginning of playback.
-            logging.info('Start playing %s from Chameleon',
-                         golden_file.path)
+            logging.info('Start playing %s from Chameleon', golden_file.path)
             source.start_playback()
 
             time.sleep(self.DELAY_BEFORE_RECORD_SECONDS)
@@ -90,7 +73,7 @@
             logging.info('Stopped recording from Cros device.')
 
             audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir, 'after_recording')
+                    self.host, self.facade, self.resultsdir, 'after_recording')
 
             recorder.read_recorded_binary()
             logging.info('Read recorded binary from Cros device.')
@@ -123,5 +106,5 @@
         # Comparing data by frequency is more robust than comparing them by
         # correlation, which is suitable for fully-digital audio path like USB
         # and HDMI.
-        audio_test_utils.check_recorded_frequency(golden_file, recorder,
-                                                  second_peak_ratio=0.2)
+        audio_test_utils.check_recorded_frequency(
+                golden_file, recorder, second_peak_ratio=0.2)
diff --git a/server/site_tests/audio_AudioBasicInternalSpeaker/audio_AudioBasicInternalSpeaker.py b/server/site_tests/audio_AudioBasicInternalSpeaker/audio_AudioBasicInternalSpeaker.py
index ba12cd0..fc460d9 100644
--- a/server/site_tests/audio_AudioBasicInternalSpeaker/audio_AudioBasicInternalSpeaker.py
+++ b/server/site_tests/audio_AudioBasicInternalSpeaker/audio_AudioBasicInternalSpeaker.py
@@ -1,7 +1,6 @@
 # Copyright 2015 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This is a server side internal speaker test using the Chameleon board."""
 
 import logging
@@ -10,10 +9,8 @@
 
 from autotest_lib.client.cros.audio import audio_test_data
 from autotest_lib.client.cros.chameleon import audio_test_utils
-from autotest_lib.client.cros.chameleon import chameleon_audio_helper
 from autotest_lib.client.cros.chameleon import chameleon_audio_ids
 from autotest_lib.server.cros.audio import audio_test
-from autotest_lib.server.cros.multimedia import remote_facade_factory
 
 
 class audio_AudioBasicInternalSpeaker(audio_test.AudioTest):
@@ -27,49 +24,34 @@
     DELAY_BEFORE_RECORD_SECONDS = 0.5
     RECORD_SECONDS = 8
 
-    def run_once(self, host):
-        """Runs Basic Audio Speaker test.
-
-        @param host: device under test CrosHost
-        """
-        if not audio_test_utils.has_internal_speaker(host):
+    def run_once(self):
+        """Runs Basic Audio Speaker test."""
+        if not audio_test_utils.has_internal_speaker(self.host):
             return
 
         golden_file = audio_test_data.SIMPLE_FREQUENCY_SPEAKER_TEST_FILE
 
-        chameleon_board = host.chameleon
-        factory = remote_facade_factory.RemoteFacadeFactory(
-                host, results_dir=self.resultsdir)
+        source = self.widget_factory.create_widget(
+                chameleon_audio_ids.CrosIds.SPEAKER)
 
-        chameleon_board.setup_and_reset(self.outputdir)
+        recorder = self.widget_factory.create_widget(
+                chameleon_audio_ids.ChameleonIds.MIC)
 
-        widget_factory = chameleon_audio_helper.AudioWidgetFactory(
-                factory, host)
+        audio_test_utils.dump_cros_audio_logs(self.host, self.facade,
+                                              self.resultsdir, 'start')
 
-        source = widget_factory.create_widget(
-            chameleon_audio_ids.CrosIds.SPEAKER)
+        # Selects and checks the node selected by cras is correct.
+        audio_test_utils.check_and_set_chrome_active_node_types(
+                self.facade, 'INTERNAL_SPEAKER', None)
 
-        recorder = widget_factory.create_widget(
-            chameleon_audio_ids.ChameleonIds.MIC)
-
-        audio_facade = factory.create_audio_facade()
-
-        audio_test_utils.dump_cros_audio_logs(
-                host, audio_facade, self.resultsdir, 'start')
-
-        # Checks the node selected by cras is correct.
-        audio_test_utils.check_audio_nodes(audio_facade,
-                                           (['INTERNAL_SPEAKER'], None))
-
-        audio_facade.set_selected_output_volume(80)
+        self.facade.set_selected_output_volume(80)
 
         logging.info('Setting playback data on Cros device')
         source.set_playback_data(golden_file)
 
         # Starts playing, waits for some time, and then starts recording.
         # This is to avoid artifact caused by codec initialization.
-        logging.info('Start playing %s on Cros device',
-                     golden_file.path)
+        logging.info('Start playing %s on Cros device', golden_file.path)
         source.start_playback()
 
         time.sleep(self.DELAY_BEFORE_RECORD_SECONDS)
@@ -77,12 +59,12 @@
         recorder.start_recording()
 
         time.sleep(self.RECORD_SECONDS)
-        audio_facade.check_audio_stream_at_selected_device()
+        self.facade.check_audio_stream_at_selected_device()
         recorder.stop_recording()
         logging.info('Stopped recording from Chameleon.')
 
         audio_test_utils.dump_cros_audio_logs(
-                host, audio_facade, self.resultsdir, 'after_recording')
+                self.host, self.facade, self.resultsdir, 'after_recording')
 
         recorder.read_recorded_binary()
         logging.info('Read recorded binary from Chameleon.')
@@ -108,6 +90,8 @@
         # Comparing data by frequency is more robust than comparing by
         # correlation, which is suitable for fully-digital audio path like USB
         # and HDMI.
-        audio_test_utils.check_recorded_frequency(golden_file, recorder,
-                                                  second_peak_ratio=0.1,
-                                                  ignore_frequencies=[50, 60])
+        audio_test_utils.check_recorded_frequency(
+                golden_file,
+                recorder,
+                second_peak_ratio=0.1,
+                ignore_frequencies=[50, 60])
diff --git a/server/site_tests/audio_InternalCardNodes/audio_InternalCardNodes.py b/server/site_tests/audio_InternalCardNodes/audio_InternalCardNodes.py
index 591d466..e541ae3 100644
--- a/server/site_tests/audio_InternalCardNodes/audio_InternalCardNodes.py
+++ b/server/site_tests/audio_InternalCardNodes/audio_InternalCardNodes.py
@@ -1,16 +1,10 @@
 # Copyright 2015 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-
 """This is a server side test to check nodes created for internal card."""
 
-import time
-
-from autotest_lib.client.common_lib import error
 from autotest_lib.client.cros.chameleon import audio_test_utils
 from autotest_lib.server.cros.audio import audio_test
-from autotest_lib.client.cros.audio import audio_spec
-from autotest_lib.server.cros.multimedia import remote_facade_factory
 
 
 class audio_InternalCardNodes(audio_test.AudioTest):
@@ -21,96 +15,39 @@
 
     """
     version = 1
-    DELAY_AFTER_PLUGGING = 2
-    DELAY_AFTER_UNPLUGGING = 2
 
-    def run_once(self, host):
-        """Runs InternalCardNodes test.
+    def get_expected_nodes(self, plugged):
+        """Gets expected nodes should should be created for internal cards.
 
-        @param host: device under test CrosHost.
-
+        @param plugged: True for plugged state, false otherwise.
+        @returns:
+            a tuple (output, input) containing lists of expected input and
+            output nodes.
         """
-        chameleon_board = host.chameleon
-        factory = remote_facade_factory.RemoteFacadeFactory(
-                host, results_dir=self.resultsdir)
-        audio_facade = factory.create_audio_facade()
-
-        chameleon_board.setup_and_reset(self.outputdir)
-
-        jack_plugger = chameleon_board.get_audio_board().get_jack_plugger()
-
-        expected_plugged_nodes_without_audio_jack = (
-                [],
-                ['POST_DSP_LOOPBACK',
-                 'POST_MIX_LOOPBACK'])
-
-        # 'Headphone' or 'LINEOUT' will be added to expected list after jack
-        # is plugged.
-        expected_plugged_nodes_with_audio_jack = (
-                [],
-                ['MIC', 'POST_DSP_LOOPBACK',
-                 'POST_MIX_LOOPBACK'])
-
-        # Modify expected nodes for special boards.
-        board_name = host.get_board().split(':')[1]
-        model_name = host.get_platform()
-
-        if audio_test_utils.has_internal_speaker(host):
-            expected_plugged_nodes_without_audio_jack[0].append(
-                    'INTERNAL_SPEAKER')
-            expected_plugged_nodes_with_audio_jack[0].append(
-                    'INTERNAL_SPEAKER')
-
-        if audio_test_utils.has_internal_microphone(host):
-            expected_internal_mics = audio_test_utils.get_plugged_internal_mics(
-                    host)
-            expected_plugged_nodes_without_audio_jack[1].extend(
-                    expected_internal_mics)
-            expected_plugged_nodes_with_audio_jack[1].extend(
-                    expected_internal_mics)
-
-        if board_name == 'link':
-            expected_plugged_nodes_without_audio_jack[1].append('KEYBOARD_MIC')
-            expected_plugged_nodes_with_audio_jack[1].append('KEYBOARD_MIC')
-
-        if audio_spec.has_hotwording(board_name, model_name):
-            expected_plugged_nodes_without_audio_jack[1].append('HOTWORD')
-            expected_plugged_nodes_with_audio_jack[1].append('HOTWORD')
-
-        # If there is no jack plugger, check the nodes without plugging.
-        host_info = host.host_info_store.get()
-        if jack_plugger is None:
-            if 'audio_box' in host_info.labels:
-                raise error.TestError("Failed to detect jack plugger.")
-            hp_jack_node_type = audio_test_utils.check_hp_or_lineout_plugged(
-                    audio_facade)
-            expected_plugged_nodes_with_audio_jack[0].append(hp_jack_node_type)
-
-            audio_test_utils.check_plugged_nodes(
-                    audio_facade, expected_plugged_nodes_with_audio_jack)
-            return
-
-        audio_test_utils.check_plugged_nodes(
-                audio_facade, expected_plugged_nodes_without_audio_jack)
-
-        try:
-            jack_plugger.plug()
-            time.sleep(self.DELAY_AFTER_PLUGGING)
-
-            audio_test_utils.dump_cros_audio_logs(
-                    host, audio_facade, self.resultsdir)
-
+        nodes = ([], ['POST_DSP_LOOPBACK', 'POST_MIX_LOOPBACK'])
+        if plugged:
             # Checks whether line-out or headphone is detected.
             hp_jack_node_type = audio_test_utils.check_hp_or_lineout_plugged(
-                    audio_facade)
-            expected_plugged_nodes_with_audio_jack[0].append(hp_jack_node_type)
+                    self.facade)
+            nodes[0].append(hp_jack_node_type)
+            nodes[1].append('MIC')
+        if audio_test_utils.has_internal_speaker(self.host):
+            nodes[0].append('INTERNAL_SPEAKER')
+        if audio_test_utils.has_internal_microphone(self.host):
+            nodes[1].extend(
+                    audio_test_utils.get_plugged_internal_mics(self.host))
+        if audio_test_utils.has_hotwording(self.host):
+            nodes[1].append('HOTWORD')
+        return nodes
 
-            audio_test_utils.check_plugged_nodes(
-                    audio_facade, expected_plugged_nodes_with_audio_jack)
+    def run_once(self):
+        """Runs InternalCardNodes test."""
+        jack_plugger = self.host.chameleon.get_audio_board().get_jack_plugger()
 
-        finally:
-            jack_plugger.unplug()
-            time.sleep(self.DELAY_AFTER_UNPLUGGING)
+        jack_plugger.plug()
+        audio_test_utils.check_plugged_nodes(self.facade,
+                                             self.get_expected_nodes(True))
 
-        audio_test_utils.check_plugged_nodes(
-                audio_facade, expected_plugged_nodes_without_audio_jack)
+        jack_plugger.unplug()
+        audio_test_utils.check_plugged_nodes(self.facade,
+                                             self.get_expected_nodes(False))