1# Lint as: python2, python3 2# Copyright 2020 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"""Bluetooth DBus API tests.""" 7 8from __future__ import absolute_import 9 10import logging 11 12import common 13from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests 14 15# Assigning local names for some frequently used long method names. 16method_name = bluetooth_adapter_tests.method_name 17_test_retry_and_log = bluetooth_adapter_tests.test_retry_and_log 18 19DEFAULT_START_DELAY_SECS = 2 20DEFAULT_HOLD_INTERVAL = 10 21DEFAULT_HOLD_TIMEOUT = 60 22 23# String representation of DBus exceptions 24DBUS_ERRORS = { 25 'InProgress' : 'org.bluez.Error.InProgress: Operation already in progress', 26 'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready', 27 'Failed': { 28 'discovery_start' : 'org.bluez.Error.Failed: No discovery started', 29 'discovery_unpause' : 'org.bluez.Error.Failed: Discovery not paused' 30 } 31 } 32 33 34class BluetoothDBusAPITests(bluetooth_adapter_tests.BluetoothAdapterTests): 35 """Bluetooth DBus API Test 36 37 These test verifies return values and functionality of various Bluetooth 38 DBus APIs. It tests both success and failures cases of each API. It 39 checks the following 40 - Expected return value 41 - Expected exceptions for negative cases 42 - Expected change in Dbus variables 43 - TODO Expected change in (hci) state of the adapter 44 """ 45 46 def _reset_state(self): 47 """ Reset adapter to a known state. 48 These tests changes adapter state. This function resets the adapter 49 to known state 50 51 @returns True if reset was successful False otherwise 52 53 """ 54 logging.debug("resetting state of the adapter") 55 power_off = self._wait_till_power_off() 56 power_on = self._wait_till_power_on() 57 not_discovering = self._wait_till_discovery_stops() 58 reset_results = {'power_off' : power_off, 59 'power_on' : power_on, 60 'not_discovering' : not_discovering} 61 if not all(reset_results.values()): 62 logging.error('_reset_state failed %s',reset_results) 63 return False 64 else: 65 return True 66 67 def _compare_error(self, actual, expected): 68 """ Helper function to compare error and log. """ 69 if expected in actual: 70 return True 71 else: 72 logging.debug("Expected error is %s Actual error is %s",expected, 73 actual) 74 return False 75 76 def _get_hci_state(self, msg=''): 77 """ get state of bluetooth controller. """ 78 hci_state = self.log_flags(msg, self.get_dev_info()[3]) 79 logging.debug("hci_state is %s", hci_state) 80 return hci_state 81 82 def _wait_till_hci_state_inquiry(self): 83 """ Wait till adapter is in INQUIRY state. 84 85 @return: True if adapter does INQUIRY before timeout, False otherwise 86 """ 87 return self._wait_for_condition( 88 lambda: 'INQUIRY' in self._get_hci_state('Expecting INQUIRY'), 89 method_name(), 90 start_delay = DEFAULT_START_DELAY_SECS) 91 92 def _wait_till_hci_state_no_inquiry_holds(self): 93 """ Wait till adapter does not enter INQUIRY for a period of time 94 95 @return : True if adapter is not in INQUIRY for a period of time before 96 timeout. Otherwise False. 97 """ 98 return self._wait_till_condition_holds( 99 lambda: 'INQUIRY' not in self._get_hci_state('Expecting NOINQUIRY'), 100 method_name(), 101 hold_interval = DEFAULT_HOLD_INTERVAL, 102 timeout = DEFAULT_HOLD_TIMEOUT, 103 start_delay = DEFAULT_START_DELAY_SECS) 104 105 106 107 def _wait_till_discovery_stops(self, stop_discovery=True): 108 """stop discovery if specified and wait for discovery to stop 109 110 @params: stop_discovery: Specifies whether stop_discovery should be 111 executed 112 @returns: True if discovery is stopped 113 """ 114 if stop_discovery: 115 self.bluetooth_facade.stop_discovery() 116 is_not_discovering = self._wait_for_condition( 117 lambda: not self.bluetooth_facade.is_discovering(), 118 method_name()) 119 return is_not_discovering 120 121 def _wait_till_discovery_starts(self, start_discovery=True): 122 """start discovery if specified and wait for discovery to start 123 124 @params: start_discovery: Specifies whether start_discovery should be 125 executed 126 @returns: True if discovery is started 127 """ 128 129 if start_discovery: 130 self.bluetooth_facade.start_discovery() 131 is_discovering = self._wait_for_condition( 132 self.bluetooth_facade.is_discovering, method_name()) 133 return is_discovering 134 135 def _wait_till_power_off(self): 136 """power off the adapter and wait for it to be powered off 137 138 @returns: True if adapter can be powered off 139 """ 140 141 power_off = self.bluetooth_facade.set_powered(False) 142 is_powered_off = self._wait_for_condition( 143 lambda: not self.bluetooth_facade.is_powered_on(), 144 method_name()) 145 return is_powered_off 146 147 def _wait_till_power_on(self): 148 """power on the adapter and wait for it to be powered on 149 150 @returns: True if adapter can be powered on 151 """ 152 power_on = self.bluetooth_facade.set_powered(True) 153 is_powered_on = self._wait_for_condition( 154 self.bluetooth_facade.is_powered_on, method_name()) 155 return is_powered_on 156 157 158######################################################################## 159# dbus call : start_discovery 160# 161##################################################### 162# Positive cases 163# Case 1 164# preconditions: Adapter powered on AND 165# Currently not discovering 166# result: Success 167###################################################### 168# Negative cases 169# 170# Case 1 171# preconditions: Adapter powered off 172# result: Failure 173# error : NotReady 174# 175# Case 2 176# precondition: Adapter power on AND 177# Currently discovering 178# result: Failure 179# error: Inprogress 180######################################################################### 181 182 @_test_retry_and_log(False) 183 def test_dbus_start_discovery_success(self): 184 """ Test success case of start_discovery call. """ 185 reset = self._reset_state() 186 is_power_on = self._wait_till_power_on() 187 is_not_discovering = self._wait_till_discovery_stops() 188 189 start_discovery, error = self.bluetooth_facade.start_discovery() 190 191 is_discovering = self._wait_till_discovery_starts(start_discovery=False) 192 inquiry_state = self._wait_till_hci_state_inquiry() 193 194 self.results = {'reset' : reset, 195 'is_power_on' : is_power_on, 196 'is_not_discovering': is_not_discovering, 197 'start_discovery' : start_discovery, 198 'is_discovering': is_discovering, 199 'inquiry_state' : inquiry_state 200 } 201 return all(self.results.values()) 202 203 @_test_retry_and_log(False) 204 def test_dbus_start_discovery_fail_discovery_in_progress(self): 205 """ Test Failure case of start_discovery call. 206 207 start discovery when discovery is in progress and confirm it fails with 208 'org.bluez.Error.InProgress: Operation already in progress'. 209 """ 210 reset = self._reset_state() 211 is_discovering = self._wait_till_discovery_starts() 212 213 start_discovery, error = self.bluetooth_facade.start_discovery() 214 215 216 self.results = {'reset' : reset, 217 'is_discovering' : is_discovering, 218 'start_discovery_failed' : not start_discovery, 219 'error_matches' : self._compare_error(error, 220 DBUS_ERRORS['InProgress']) 221 } 222 return all(self.results.values()) 223 224 @_test_retry_and_log(False) 225 def test_dbus_start_discovery_fail_power_off(self): 226 """ Test Failure case of start_discovery call. 227 228 start discovery when adapter is turned off and confirm it fails with 229 'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready'. 230 """ 231 reset = self._reset_state() 232 is_power_off = self._wait_till_power_off() 233 234 start_discovery, error = self.bluetooth_facade.start_discovery() 235 236 is_power_on = self._wait_till_power_on() 237 self.results = {'reset' : reset, 238 'power_off' : is_power_off, 239 'start_discovery_failed' : not start_discovery, 240 'error_matches' : self._compare_error(error, 241 DBUS_ERRORS['NotReady']), 242 'power_on' : is_power_on} 243 return all(self.results.values()) 244 245 246######################################################################## 247# dbus call : stop_discovery 248# 249##################################################### 250# Positive cases 251# Case 1 252# preconditions: Adapter powered on AND 253# Currently discovering 254# result: Success 255##################################################### 256# Negative cases 257# 258# Case 1 259# preconditions: Adapter powered off 260# result: Failure 261# error : NotReady 262# 263# Case 2 264# precondition: Adapter power on AND 265# Currently not discovering 266# result: Failure 267# error: Failed 268# 269#TODO 270#Case 3 org.bluez.Error.NotAuthorized 271######################################################################### 272 273 @_test_retry_and_log(False) 274 def test_dbus_stop_discovery_success(self): 275 """ Test success case of stop_discovery call. """ 276 reset = self._reset_state() 277 is_power_on = self._wait_till_power_on() 278 is_discovering = self._wait_till_discovery_starts() 279 280 stop_discovery, error = self.bluetooth_facade.stop_discovery() 281 is_not_discovering = self._wait_till_discovery_stops( 282 stop_discovery=False) 283 self._wait_till_hci_state_no_inquiry_holds() 284 self.results = {'reset' : reset, 285 'is_power_on' : is_power_on, 286 'is_discovering': is_discovering, 287 'stop_discovery' : stop_discovery, 288 'is_not_discovering' : is_not_discovering} 289 return all(self.results.values()) 290 291 @_test_retry_and_log(False) 292 def test_dbus_stop_discovery_fail_discovery_not_in_progress(self): 293 """ Test Failure case of stop_discovery call. 294 295 stop discovery when discovery is not in progress and confirm it fails 296 with 'org.bluez.Error.Failed: No discovery started'. 297 """ 298 reset = self._reset_state() 299 is_not_discovering = self._wait_till_discovery_stops() 300 301 stop_discovery, error = self.bluetooth_facade.stop_discovery() 302 303 still_not_discovering = self._wait_till_discovery_stops( 304 stop_discovery=False) 305 306 self.results = { 307 'reset' : reset, 308 'is_not_discovering' : is_not_discovering, 309 'stop_discovery_failed' : not stop_discovery, 310 'error_matches' : self._compare_error(error, 311 DBUS_ERRORS['Failed']['discovery_start']), 312 'still_not_discovering': still_not_discovering} 313 return all(self.results.values()) 314 315 @_test_retry_and_log(False) 316 def test_dbus_stop_discovery_fail_power_off(self): 317 """ Test Failure case of stop_discovery call. 318 319 stop discovery when adapter is turned off and confirm it fails with 320 'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready'. 321 """ 322 reset = self._reset_state() 323 is_power_off = self._wait_till_power_off() 324 325 stop_discovery, error = self.bluetooth_facade.stop_discovery() 326 327 is_power_on = self._wait_till_power_on() 328 self.results = {'reset' : reset, 329 'is_power_off' : is_power_off, 330 'stop_discovery_failed' : not stop_discovery, 331 'error_matches' : self._compare_error(error, 332 DBUS_ERRORS['NotReady']), 333 'is_power_on' : is_power_on} 334 return all(self.results.values()) 335 336 337######################################################################## 338# dbus call: get_suppported_capabilities 339# arguments: None 340# returns : The dictionary is following the format 341# {capability : value}, where: 342# 343# string capability: The supported capability under 344# discussion. 345# variant value: A more detailed description of 346# the capability. 347##################################################### 348# Positive cases 349# Case 1 350# Precondition: Adapter Powered on 351# results: Result dictionary returned 352# 353# Case 2 354# Precondition: Adapter Powered Off 355# result : Result dictionary returned 356################################################################################ 357 358 @_test_retry_and_log(False) 359 def test_dbus_get_supported_capabilities_success(self): 360 """ Test success case of get_supported_capabilities call. """ 361 reset = self._reset_state() 362 is_power_on = self._wait_till_power_on() 363 364 capabilities, error = self.bluetooth_facade.get_supported_capabilities() 365 logging.debug('supported capabilities is %s', capabilities) 366 367 self.results = {'reset' : reset, 368 'is_power_on' : is_power_on, 369 'get_supported_capabilities': error is None 370 } 371 return all(self.results.values()) 372 373 @_test_retry_and_log(False) 374 def test_dbus_get_supported_capabilities_success_power_off(self): 375 """ Test success case of get_supported_capabilities call. 376 Call get_supported_capabilities call with adapter powered off and 377 confirm that it succeeds 378 """ 379 380 reset = self._reset_state() 381 is_power_off = self._wait_till_power_off() 382 383 capabilities, error = self.bluetooth_facade.get_supported_capabilities() 384 logging.debug('supported capabilities is %s', capabilities) 385 386 self.results = {'reset' : reset, 387 'is_power_off' : is_power_off, 388 'get_supported_capabilities': error is None, 389 } 390 return all(self.results.values()) 391