1# Copyright 2019 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"""Server side bluetooth adapter stress tests involving power consumption.""" 6 7import logging 8import multiprocessing 9import time 10 11from autotest_lib.client.common_lib import error 12from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests 13from autotest_lib.server.cros.multimedia import remote_facade_factory 14 15 16test_case_log = bluetooth_adapter_tests.test_case_log 17 18 19class bluetooth_AdapterPowerMeasure( 20 bluetooth_adapter_tests.BluetoothAdapterTests): 21 """Server side bluetooth adapter power consumption test.""" 22 23 # --------------------------------------------------------------- 24 # Definitions of all test cases 25 # --------------------------------------------------------------- 26 27 @test_case_log 28 def test_case_suspend_power_measurement(self, host, max_power_mw, 29 suspend_time_secs, 30 resume_network_timeout_secs=60): 31 """Test Case: measure the Bluetooth chip power consumption on suspend""" 32 33 def print_debug_count(): 34 """Print the debug message about count values.""" 35 logging.debug('count_fail_to_sleep: %d', self.count_fail_to_sleep) 36 logging.debug('count_fail_to_resume: %d', self.count_fail_to_resume) 37 logging.debug('count_system_resume_prematurely: %d', 38 self.count_system_resume_prematurely) 39 logging.debug('count_success: %d', self.count_success) 40 41 def action_suspend(): 42 """Calls the host method suspend.""" 43 host.suspend(suspend_time=suspend_time_secs, 44 allow_early_resume=True) 45 46 boot_id = host.get_boot_id() 47 proc = multiprocessing.Process(target=action_suspend) 48 proc.daemon = True 49 start_time = time.time() 50 proc.start() 51 52 # Block waiting until the system has suspended. 53 try: 54 host.test_wait_for_sleep(suspend_time_secs) 55 except Exception as e: 56 logging.error('host.test_wait_for_sleep failed: %s', e) 57 self.count_fail_to_sleep += 1 58 print_debug_count() 59 # Skip this time since the system failed to suspend. 60 proc.join() 61 return 62 63 # Test the Bluetooth chip power consumption. 64 if self.test_power_consumption(max_power_mw): 65 self.count_success += 1 66 67 # Block waiting until the system has resumed. 68 try: 69 host.test_wait_for_resume( 70 boot_id, suspend_time_secs + resume_network_timeout_secs) 71 except Exception as e: 72 logging.error('host.test_wait_for_resume failed: %s', e) 73 self.count_fail_to_resume += 1 74 75 # If the system resumes prematurely, do not conduct the test in 76 # this iteration. 77 actual_suspend_time_secs = time.time() - start_time 78 if actual_suspend_time_secs < suspend_time_secs: 79 logging.error('actual suspension time %f is less than expected %f', 80 actual_suspend_time_secs, suspend_time_secs) 81 self.count_system_resume_prematurely += 1 82 83 print_debug_count() 84 proc.join() 85 86 87 def initialize_servod(self): 88 """Peform initialize for servod task.""" 89 self.count_fail_to_sleep = 0 90 self.count_fail_to_resume = 0 91 self.count_system_resume_prematurely = 0 92 self.count_success = 0 93 94 # When the autotest restarts ui, chrome would issue some Bluetooth 95 # commands which may prevent the system from suspending properly. 96 # Hence, let's stop ui for now. 97 self.host.run_short('stop ui') 98 99 board = self.host.get_board().split(':')[1] 100 logging.info('board: %s', board) 101 102 # TODO: figure out a way to support other boards. 103 if board != 'kukui': 104 raise error.TestError('Only kukui is supported for now.') 105 106 # self.device is a pure XMLRPC server running as chameleond 107 # on the chameleon host. We need to enable Servod. 108 if not self.device.EnableServod(board): 109 raise error.TestError('Failed to enable Servod.') 110 111 # Start the Servod process on the chameleon host. 112 if not self.device.servod.Start(): 113 raise error.TestError('Failed to start Servod on chameleon host.') 114 115 116 def cleanup_servod(self): 117 """Peform cleanup for servod.""" 118 if not self.device.servod.Stop(): 119 logging.error('Failed to stop Servod on chameleon host.') 120 121 self.host.run_short('start ui') 122 123 logging.info('count_fail_to_sleep: %d', self.count_fail_to_sleep) 124 logging.info('count_fail_to_resume: %d', self.count_fail_to_resume) 125 logging.info('count_system_resume_prematurely: %d', 126 self.count_system_resume_prematurely) 127 logging.info('count_success: %d', self.count_success) 128 129 130 def run_once(self, host, max_power_mw=3, device_type='BLUETOOTH_BASE', 131 num_iterations=1, suspend_time_secs=30, 132 test_category='suspension'): 133 """Running Bluetooth adapter power consumption autotest during system 134 suspension. 135 136 @param host: the DUT host. 137 @param max_power_mw: max power allowed in milli-watt 138 @param device_type: the device type emulated by the chameleon host 139 @param num_iterations: number of times to perform the tests. 140 @param suspend_time_secs: the system suspension duration in seconds 141 @param test_category: the test category 142 143 """ 144 145 self.host = host 146 factory = remote_facade_factory.RemoteFacadeFactory(host, 147 disable_arc=True) 148 self.bluetooth_facade = factory.create_bluetooth_hid_facade() 149 150 self.check_chameleon() 151 self.device = self.get_device(device_type) 152 self.initialize_servod() 153 self.test_power_on_adapter() 154 self.test_bluetoothd_running() 155 156 for i in xrange(1, num_iterations + 1): 157 logging.info('Starting iteration: %d / %d', i, num_iterations) 158 159 if test_category == 'suspension': 160 self.test_case_suspend_power_measurement(host, max_power_mw, 161 suspend_time_secs) 162 else: 163 logging.error('Do not support the test category: %s', 164 test_category) 165 166 if device_type == 'BLUETOOTH_BASE': 167 self.cleanup_servod() 168 169 if self.count_success == 0: 170 raise error.TestError('System failed to suspend/resume.') 171 172 if self.fails: 173 raise error.TestFail(self.fails) 174