1# Lint as: python2, python3 2# Copyright 2019 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Server side bluetooth adapter stress tests involving power consumption.""" 7 8import logging 9import multiprocessing 10import time 11 12from autotest_lib.client.common_lib import error 13from autotest_lib.server.cros.bluetooth.bluetooth_adapter_tests import ( 14 test_case_log) 15from autotest_lib.server.cros.bluetooth.bluetooth_adapter_quick_tests import ( 16 BluetoothAdapterQuickTests) 17 18 19class bluetooth_AdapterPowerMeasure(BluetoothAdapterQuickTests): 20 """Server side bluetooth adapter power consumption test.""" 21 22 test_wrapper = BluetoothAdapterQuickTests.quick_test_test_decorator 23 batch_wrapper = BluetoothAdapterQuickTests.quick_test_batch_decorator 24 25 26 def _check_legitimate_board(self): 27 """Check if this is a legitimate board to run the test. 28 29 Only a limited set of boards are supported for now, primarily 30 the kukui family of barods. 31 32 @raises: TestNAError if the board is not legitimate. 33 34 """ 35 board = self.host.get_board().split(':')[1] 36 if board not in ('kukui'): 37 raise error.TestNAError('%s not legitimate to run the test.' % 38 board) 39 40 41 def _initialize_servod(self, device): 42 """Perform initialize for servod task. 43 44 @param device: the peer device 45 46 @raises: TestError if not able to enable or start servo. 47 """ 48 self.count_fail_to_sleep = 0 49 self.count_fail_to_resume = 0 50 self.count_system_resume_prematurely = 0 51 self.count_success = 0 52 53 # When the autotest restarts ui, chrome would issue some Bluetooth 54 # commands which may prevent the system from suspending properly. 55 # Hence, let's stop ui for now. 56 self.host.run_short('stop ui') 57 58 board = self.host.get_board().split(':')[1] 59 logging.info('board: %s', board) 60 61 # TODO(b/152737849): figure out a way to support more boards. 62 self._check_legitimate_board() 63 64 # device is a pure XMLRPC server running as chameleond 65 # on the bluetooth peer. We need to enable Servod. 66 if not device.EnableServod(board): 67 raise error.TestError('Failed to enable Servod.') 68 69 # Start the Servod process on the bluetooth peer. 70 if not device.servod.Start(): 71 raise error.TestError('Failed to start Servod on bluetooth peer.') 72 73 74 def _cleanup_servod(self, device): 75 """Perform cleanup for servod. 76 77 @param device: the peer device 78 """ 79 if not device.servod.Stop(): 80 logging.error('Failed to stop Servod on bluetooth peer.') 81 82 self.host.run_short('start ui') 83 84 logging.info('count_fail_to_sleep: %d', self.count_fail_to_sleep) 85 logging.info('count_fail_to_resume: %d', self.count_fail_to_resume) 86 logging.info('count_system_resume_prematurely: %d', 87 self.count_system_resume_prematurely) 88 logging.info('count_success: %d', self.count_success) 89 90 91 # --------------------------------------------------------------- 92 # Definitions of test cases 93 # --------------------------------------------------------------- 94 95 @test_case_log 96 def test_case_suspend_power_measurement(self, host, device, max_power_mw, 97 suspend_time_secs, 98 resume_network_timeout_secs=60): 99 """Test Case: measure the Bluetooth chip power consumption on suspend""" 100 101 def print_debug_count(): 102 """Print the debug message about count values.""" 103 logging.debug('count_fail_to_sleep: %d', self.count_fail_to_sleep) 104 logging.debug('count_fail_to_resume: %d', self.count_fail_to_resume) 105 logging.debug('count_system_resume_prematurely: %d', 106 self.count_system_resume_prematurely) 107 logging.debug('count_success: %d', self.count_success) 108 109 def action_suspend(): 110 """Calls the host method suspend.""" 111 host.suspend(suspend_time=suspend_time_secs, 112 allow_early_resume=True) 113 114 boot_id = host.get_boot_id() 115 proc = multiprocessing.Process(target=action_suspend) 116 proc.daemon = True 117 start_time = time.time() 118 proc.start() 119 120 # Block waiting until the system has suspended. 121 try: 122 host.test_wait_for_sleep(suspend_time_secs) 123 except Exception as e: 124 logging.error('host.test_wait_for_sleep failed: %s', e) 125 self.count_fail_to_sleep += 1 126 print_debug_count() 127 # Skip this time since the system failed to suspend. 128 proc.join() 129 return 130 131 # Test the Bluetooth chip power consumption. 132 if self.test_power_consumption(device, max_power_mw): 133 self.count_success += 1 134 135 # Block waiting until the system has resumed. 136 try: 137 host.test_wait_for_resume( 138 boot_id, suspend_time_secs + resume_network_timeout_secs) 139 except Exception as e: 140 logging.error('host.test_wait_for_resume failed: %s', e) 141 self.count_fail_to_resume += 1 142 143 # If the system resumes prematurely, do not conduct the test in 144 # this iteration. 145 actual_suspend_time_secs = time.time() - start_time 146 if actual_suspend_time_secs < suspend_time_secs: 147 logging.error('actual suspension time %f is less than expected %f', 148 actual_suspend_time_secs, suspend_time_secs) 149 self.count_system_resume_prematurely += 1 150 151 print_debug_count() 152 proc.join() 153 154 if self.count_success == 0: 155 raise error.TestError('System failed to suspend/resume.') 156 157 158 # --------------------------------------------------------------- 159 # Definitions of test wrapper tests and batch wrapper tests. 160 # --------------------------------------------------------------- 161 162 163 @test_wrapper('Power measurement test', devices={'BLUETOOTH_BASE':1}) 164 def pw_measurement_suspension_test(self): 165 """power measurement test during system suspension.""" 166 device = self.devices['BLUETOOTH_BASE'][0] 167 self._initialize_servod(device) 168 self.test_power_on_adapter() 169 self.test_bluetoothd_running() 170 self.test_case_suspend_power_measurement(self.host, device, 171 self.max_power_mw, 172 self.suspend_time_secs) 173 self._cleanup_servod(device) 174 175 176 @batch_wrapper('Bluetooth Power Measurement Health Tests') 177 def pw_health_batch_run(self, num_iterations=1, test_name=None): 178 """Run bluetooth power measurement health test batch or a specific test. 179 180 @param num_iterations: how many iterations to run 181 @param test_name: specific test to run otherwise None to run the 182 whole batch 183 """ 184 self.pw_measurement_suspension_test() 185 186 187 def run_once(self, 188 host, 189 num_iterations=1, 190 args_dict=None, 191 test_name=None, 192 max_power_mw=3, 193 suspend_time_secs=30, 194 flag='Quick Health'): 195 """Running Bluetooth adapter power consumption autotest during system 196 suspension. 197 198 @param host: the DUT host. 199 @param num_iterations: number of times to perform the tests. 200 @param test_name: the test to run, or None for all tests 201 @param max_power_mw: max power allowed in milli-watt 202 @param suspend_time_secs: the system suspension duration in seconds 203 204 """ 205 self.host = host 206 self.max_power_mw = max_power_mw 207 self.suspend_time_secs = suspend_time_secs 208 209 self.quick_test_init(host, 210 use_btpeer=True, 211 flag=flag, 212 args_dict=args_dict) 213 self.pw_health_batch_run(num_iterations, test_name) 214 self.quick_test_cleanup() 215