# Copyright 2020 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. """Bluetooth DBus API tests.""" from __future__ import absolute_import import logging import common from autotest_lib.server.cros.bluetooth import bluetooth_adapter_tests # Assigning local names for some frequently used long method names. method_name = bluetooth_adapter_tests.method_name _test_retry_and_log = bluetooth_adapter_tests.test_retry_and_log DEFAULT_START_DELAY_SECS = 2 DEFAULT_HOLD_INTERVAL = 10 DEFAULT_HOLD_TIMEOUT = 60 # String representation of DBus exceptions DBUS_ERRORS = { 'InProgress' : 'org.bluez.Error.InProgress: Operation already in progress', 'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready', 'Failed': { 'discovery_start' : 'org.bluez.Error.Failed: No discovery started', 'discovery_unpause' : 'org.bluez.Error.Failed: Discovery not paused' } } class BluetoothDBusAPITests(bluetooth_adapter_tests.BluetoothAdapterTests): """Bluetooth DBus API Test These test verifies return values and functionality of various Bluetooth DBus APIs. It tests both success and failures cases of each API. It checks the following - Expected return value - Expected exceptions for negative cases - Expected change in Dbus variables - TODO Expected change in (hci) state of the adapter """ def _reset_state(self): """ Reset adapter to a known state. These tests changes adapter state. This function resets the adapter to known state @returns True if reset was successful False otherwise """ logging.debug("resetting state of the adapter") power_off = self._wait_till_power_off() power_on = self._wait_till_power_on() not_discovering = self._wait_till_discovery_stops() reset_results = {'power_off' : power_off, 'power_on' : power_on, 'not_discovering' : not_discovering} if not all(reset_results.values()): logging.error('_reset_state failed %s',reset_results) return False else: return True def _compare_error(self, actual, expected): """ Helper function to compare error and log. """ if expected == actual: return True else: logging.debug("Expected error is %s Actual error is %s",expected, actual) return False def _get_hci_state(self, msg=''): """ get state of bluetooth controller. """ hci_state = self.log_flags(msg, self.get_dev_info()[3]) logging.debug("hci_state is %s", hci_state) return hci_state def _wait_till_hci_state_inquiry(self): """ Wait till adapter is in INQUIRY state. @return: True if adapter does INQUIRY before timeout, False otherwise """ return self._wait_for_condition( lambda: 'INQUIRY' in self._get_hci_state('Expecting INQUIRY'), method_name(), start_delay = DEFAULT_START_DELAY_SECS) def _wait_till_hci_state_no_inquiry_holds(self): """ Wait till adapter does not enter INQUIRY for a period of time @return : True if adapter is not in INQUIRY for a period of time before timeout. Otherwise False. """ return self._wait_till_condition_holds( lambda: 'INQUIRY' not in self._get_hci_state('Expecting NOINQUIRY'), method_name(), hold_interval = DEFAULT_HOLD_INTERVAL, timeout = DEFAULT_HOLD_TIMEOUT, start_delay = DEFAULT_START_DELAY_SECS) def _wait_till_discovery_stops(self, stop_discovery=True): """stop discovery if specified and wait for discovery to stop @params: stop_discovery: Specifies whether stop_discovery should be executed @returns: True if discovery is stopped """ if stop_discovery: self.bluetooth_facade.stop_discovery() is_not_discovering = self._wait_for_condition( lambda: not self.bluetooth_facade.is_discovering(), method_name()) return is_not_discovering def _wait_till_discovery_starts(self, start_discovery=True): """start discovery if specified and wait for discovery to start @params: start_discovery: Specifies whether start_discovery should be executed @returns: True if discovery is started """ if start_discovery: self.bluetooth_facade.start_discovery() is_discovering = self._wait_for_condition( self.bluetooth_facade.is_discovering, method_name()) return is_discovering def _wait_till_power_off(self): """power off the adapter and wait for it to be powered off @returns: True if adapter can be powered off """ power_off = self.bluetooth_facade.set_powered(False) is_powered_off = self._wait_for_condition( lambda: not self.bluetooth_facade.is_powered_on(), method_name()) return is_powered_off def _wait_till_power_on(self): """power on the adapter and wait for it to be powered on @returns: True if adapter can be powered on """ power_on = self.bluetooth_facade.set_powered(True) is_powered_on = self._wait_for_condition( self.bluetooth_facade.is_powered_on, method_name()) return is_powered_on ######################################################################## # dbus call : start_discovery # ##################################################### # Positive cases # Case 1 # preconditions: Adapter powered on AND # Currently not discovering # result: Success ###################################################### # Negative cases # # Case 1 # preconditions: Adapter powered off # result: Failure # error : NotReady # # Case 2 # precondition: Adapter power on AND # Currently discovering # result: Failure # error: Inprogress ######################################################################### @_test_retry_and_log(False) def test_dbus_start_discovery_success(self): """ Test success case of start_discovery call. """ reset = self._reset_state() is_power_on = self._wait_till_power_on() is_not_discovering = self._wait_till_discovery_stops() start_discovery, error = self.bluetooth_facade.start_discovery() is_discovering = self._wait_till_discovery_starts(start_discovery=False) inquiry_state = self._wait_till_hci_state_inquiry() self.results = {'reset' : reset, 'is_power_on' : is_power_on, 'is_not_discovering': is_not_discovering, 'start_discovery' : start_discovery, 'is_discovering': is_discovering, 'inquiry_state' : inquiry_state } return all(self.results.values()) @_test_retry_and_log(False) def test_dbus_start_discovery_fail_discovery_in_progress(self): """ Test Failure case of start_discovery call. start discovery when discovery is in progress and confirm it fails with 'org.bluez.Error.InProgress: Operation already in progress'. """ reset = self._reset_state() is_discovering = self._wait_till_discovery_starts() start_discovery, error = self.bluetooth_facade.start_discovery() self.results = {'reset' : reset, 'is_discovering' : is_discovering, 'start_discovery_failed' : not start_discovery, 'error_matches' : self._compare_error(error, DBUS_ERRORS['InProgress']) } return all(self.results.values()) @_test_retry_and_log(False) def test_dbus_start_discovery_fail_power_off(self): """ Test Failure case of start_discovery call. start discovery when adapter is turned off and confirm it fails with 'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready'. """ reset = self._reset_state() is_power_off = self._wait_till_power_off() start_discovery, error = self.bluetooth_facade.start_discovery() is_power_on = self._wait_till_power_on() self.results = {'reset' : reset, 'power_off' : is_power_off, 'start_discovery_failed' : not start_discovery, 'error_matches' : self._compare_error(error, DBUS_ERRORS['NotReady']), 'power_on' : is_power_on} return all(self.results.values()) ######################################################################## # dbus call : stop_discovery # ##################################################### # Positive cases # Case 1 # preconditions: Adapter powered on AND # Currently discovering # result: Success ##################################################### # Negative cases # # Case 1 # preconditions: Adapter powered off # result: Failure # error : NotReady # # Case 2 # precondition: Adapter power on AND # Currently not discovering # result: Failure # error: Failed # #TODO #Case 3 org.bluez.Error.NotAuthorized ######################################################################### @_test_retry_and_log(False) def test_dbus_stop_discovery_success(self): """ Test success case of stop_discovery call. """ reset = self._reset_state() is_power_on = self._wait_till_power_on() is_discovering = self._wait_till_discovery_starts() stop_discovery, error = self.bluetooth_facade.stop_discovery() is_not_discovering = self._wait_till_discovery_stops( stop_discovery=False) self._wait_till_hci_state_no_inquiry_holds() self.results = {'reset' : reset, 'is_power_on' : is_power_on, 'is_discovering': is_discovering, 'stop_discovery' : stop_discovery, 'is_not_discovering' : is_not_discovering} return all(self.results.values()) @_test_retry_and_log(False) def test_dbus_stop_discovery_fail_discovery_not_in_progress(self): """ Test Failure case of stop_discovery call. stop discovery when discovery is not in progress and confirm it fails with 'org.bluez.Error.Failed: No discovery started'. """ reset = self._reset_state() is_not_discovering = self._wait_till_discovery_stops() stop_discovery, error = self.bluetooth_facade.stop_discovery() still_not_discovering = self._wait_till_discovery_stops( stop_discovery=False) self.results = { 'reset' : reset, 'is_not_discovering' : is_not_discovering, 'stop_discovery_failed' : not stop_discovery, 'error_matches' : self._compare_error(error, DBUS_ERRORS['Failed']['discovery_start']), 'still_not_discovering': still_not_discovering} return all(self.results.values()) @_test_retry_and_log(False) def test_dbus_stop_discovery_fail_power_off(self): """ Test Failure case of stop_discovery call. stop discovery when adapter is turned off and confirm it fails with 'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready'. """ reset = self._reset_state() is_power_off = self._wait_till_power_off() stop_discovery, error = self.bluetooth_facade.stop_discovery() is_power_on = self._wait_till_power_on() self.results = {'reset' : reset, 'is_power_off' : is_power_off, 'stop_discovery_failed' : not stop_discovery, 'error_matches' : self._compare_error(error, DBUS_ERRORS['NotReady']), 'is_power_on' : is_power_on} return all(self.results.values()) ######################################################################## # dbus call: get_suppported_capabilities # arguments: None # returns : The dictionary is following the format # {capability : value}, where: # # string capability: The supported capability under # discussion. # variant value: A more detailed description of # the capability. ##################################################### # Positive cases # Case 1 # Precondition: Adapter Powered on # results: Result dictionary returned # # Case 2 # Precondition: Adapter Powered Off # result : Result dictionary returned ################################################################################ @_test_retry_and_log(False) def test_dbus_get_supported_capabilities_success(self): """ Test success case of get_supported_capabilities call. """ reset = self._reset_state() is_power_on = self._wait_till_power_on() capabilities, error = self.bluetooth_facade.get_supported_capabilities() logging.debug('supported capabilities is %s', capabilities) self.results = {'reset' : reset, 'is_power_on' : is_power_on, 'get_supported_capabilities': error is None } return all(self.results.values()) @_test_retry_and_log(False) def test_dbus_get_supported_capabilities_success_power_off(self): """ Test success case of get_supported_capabilities call. Call get_supported_capabilities call with adapter powered off and confirm that it succeeds """ reset = self._reset_state() is_power_off = self._wait_till_power_off() capabilities, error = self.bluetooth_facade.get_supported_capabilities() logging.debug('supported capabilities is %s', capabilities) self.results = {'reset' : reset, 'is_power_off' : is_power_off, 'get_supported_capabilities': error is None, } return all(self.results.values())