390 lines
14 KiB
Python
390 lines
14 KiB
Python
# 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())
|