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