bluetooth: Add new dbus api tests and add some additional checks am: b161b7f5ad am: 8bb58945e8 am: 39d7535293 am: bf45571c62

Change-Id: I4ade37eb8b7ae3cda2975b829ee18ccdf471677d
diff --git a/server/cros/bluetooth/bluetooth_dbus_api_tests.py b/server/cros/bluetooth/bluetooth_dbus_api_tests.py
index b9c8a4b..bebb035 100644
--- a/server/cros/bluetooth/bluetooth_dbus_api_tests.py
+++ b/server/cros/bluetooth/bluetooth_dbus_api_tests.py
@@ -1,4 +1,4 @@
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# 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.
 
@@ -13,12 +13,19 @@
 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  ={
+DBUS_ERRORS  = {
     'InProgress' : 'org.bluez.Error.InProgress: Operation already in progress',
     'NotReady' : 'org.bluez.Error.NotReady: Resource Not Ready',
     'Failed': {
-        'discovery' : 'org.bluez.Error.Failed: No discovery started'}}
+        'discovery_start' : 'org.bluez.Error.Failed: No discovery started',
+        'discovery_unpause' : 'org.bluez.Error.Failed: Discovery not paused'
+              }
+               }
 
 
 class BluetoothDBusAPITests(bluetooth_adapter_tests.BluetoothAdapterTests):
@@ -56,6 +63,46 @@
         else:
             return True
 
+    def _compare_error(self, expected, actual):
+        """ 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
 
@@ -141,11 +188,14 @@
         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
+                        'is_discovering': is_discovering,
+                        'inquiry_state' : inquiry_state
                         }
         return all(self.results.values())
 
@@ -161,10 +211,13 @@
 
         start_discovery, error =  self.bluetooth_facade.start_discovery()
 
+
         self.results = {'reset' : reset,
                         'is_discovering' : is_discovering,
                         'start_discovery_failed' : not start_discovery,
-                        'error_matches' : error == DBUS_ERRORS['InProgress']}
+                        'error_matches' : self._compare_error(error,
+                                                    DBUS_ERRORS['InProgress'])
+        }
         return all(self.results.values())
 
     @_test_retry_and_log(False)
@@ -183,7 +236,8 @@
         self.results = {'reset' : reset,
                         'power_off' : is_power_off,
                         'start_discovery_failed' : not start_discovery,
-                        'error_matches' : error == DBUS_ERRORS['NotReady'],
+                        'error_matches' : self._compare_error(error,
+                                                    DBUS_ERRORS['NotReady']),
                         'power_on' : is_power_on}
         return all(self.results.values())
 
@@ -225,6 +279,7 @@
         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,
@@ -251,7 +306,8 @@
             'reset' : reset,
             'is_not_discovering' : is_not_discovering,
             'stop_discovery_failed' : not stop_discovery,
-            'error_matches' : error == DBUS_ERRORS['Failed']['discovery'],
+            'error_matches' : self._compare_error(error,
+                                DBUS_ERRORS['Failed']['discovery_start']),
             'still_not_discovering': still_not_discovering}
         return all(self.results.values())
 
@@ -271,7 +327,8 @@
         self.results = {'reset' : reset,
                         'is_power_off' : is_power_off,
                         'stop_discovery_failed' : not stop_discovery,
-                        'error_matches' : error == DBUS_ERRORS['NotReady'],
+                        'error_matches' : self._compare_error(error,
+                                                    DBUS_ERRORS['NotReady']),
                         'is_power_on' : is_power_on}
         return all(self.results.values())
 
@@ -315,13 +372,15 @@
         """ Test success case of pause_discovery call. """
         reset = self._reset_state()
         is_discovering = self._wait_till_discovery_starts()
+        self._wait_till_hci_state_inquiry()
+
         pause_discovery, error = self.bluetooth_facade.pause_discovery(False)
 
-        #TODO: Confirm discovery is paused by check the state of the adapter
-
+        no_inquiry = self._wait_till_hci_state_no_inquiry_holds()
         self.results = {'reset' : reset,
                         'is_discovering': is_discovering,
                         'pause_discovery' : pause_discovery,
+                        'no_inquiry' : no_inquiry
                         }
         return all(self.results.values())
 
@@ -334,12 +393,12 @@
 
         pause_discovery, error = self.bluetooth_facade.pause_discovery(False)
 
-        #TODO: Confirm discovery is paused by check the state of the adapter
-
+        no_inquiry = self._wait_till_hci_state_no_inquiry_holds()
         self.results = {'reset' : reset,
                         'is_power_on' : is_power_on,
                         'is_not_discovering': is_not_discovering,
                         'pause_discovery' : pause_discovery,
+                        'no_inquiry' : no_inquiry
                         }
         return all(self.results.values())
 
@@ -362,7 +421,8 @@
         self.results = {'reset' : reset,
                         'is_power_off' : is_power_off,
                         'pause_discovery_failed' : not pause_discovery,
-                        'error_matches' : error == DBUS_ERRORS['NotReady'],
+                        'error_matches' : self._compare_error(error,
+                                                    DBUS_ERRORS['NotReady']),
                         'is_power_on' : is_power_on,
                         'discovery_started' : discovery_started
                        }
@@ -381,17 +441,146 @@
         pause_discovery, _ = self.bluetooth_facade.pause_discovery()
 
         pause_discovery_again, error = self.bluetooth_facade.pause_discovery()
-        #TODO: Confirm discovery is paused by check the state of the adapter
+
+        no_inquiry = self._wait_till_hci_state_no_inquiry_holds()
 
         self.results = {'reset' : reset,
                         'is_power_on' : is_power_on,
                         'is_discovering': is_discovering,
                         'pause_discovery' : pause_discovery,
                         'pause_discovery_failed' : not pause_discovery_again,
-                        'error_matches' : error == DBUS_ERRORS['InProgress'],
+                        'error_matches' : self._compare_error(error,
+                                                    DBUS_ERRORS['InProgress']),
+                        'no_inquiry' : no_inquiry,
                         }
         return all(self.results.values())
 
+########################################################################
+# dbus call: unpause_discovery
+# arguments: boolean system_suspend_resume
+# returns : True/False
+# Notes: 1: argument system_suspend_resume is ignored in the code
+#        2: pause/unpause state is not reflected in Discovering state
+#####################################################
+# Positive cases
+# Case 1
+# preconditions: Adapter powered on AND
+#                Discovery started and Discovery currently paused
+# Argument: [True|False]
+######################################################
+# Negative cases
+#
+# result: Success
+# Case 1
+# preconditions: Adapter powered on AND
+#                Discovery currently not paused
+# Argument: [True|False]
+# result: Failed
+#
+# Case 2
+# preconditions: Adapter powered off
+# result: Failure
+# error : NotReady
+#
+# Case 3
+# precondition: Adapter powered on AND
+#               Discovery paused
+# result: Failure
+# error: Busy
+#########################################################################
+    @_test_retry_and_log(False)
+    def test_dbus_unpause_discovery_success(self):
+        """ Test success case of unpause_discovery call. """
+        reset = self._reset_state()
+        is_discovering = self._wait_till_discovery_starts()
+        pause_discovery, _ = self.bluetooth_facade.pause_discovery()
+        no_inquiry_after_pause = self._wait_till_hci_state_no_inquiry_holds()
+
+        unpause_discovery, error = self.bluetooth_facade.unpause_discovery()
+
+        inquiry_after_unpause = self._wait_till_hci_state_inquiry()
+        self.results = {'reset' : reset,
+                        'is_discovering': is_discovering,
+                        'pause_discovery' : pause_discovery,
+                        'no_inquiry_after_pause' : no_inquiry_after_pause,
+                        'unpause_discovery' : unpause_discovery,
+                        'error' : error is None,
+                        'inquiry_after_unpause' : inquiry_after_unpause
+                        }
+        return all(self.results.values())
+
+    @_test_retry_and_log(False)
+    def test_dbus_unpause_discovery_fail_without_pause(self):
+        """ Test failure case of unpause_discovery call.
+
+        Call unpause_discovery without calling pause_discovery and check it will
+        fail with  org.bluez.Error.Failed: Discovery not paused'
+        """
+        reset = self._reset_state()
+        is_discovering = self._wait_till_discovery_starts()
+
+        unpause_discovery, error = self.bluetooth_facade.unpause_discovery()
+
+        inquiry_after_unpause = self._wait_till_hci_state_inquiry()
+        self.results = {'reset' : reset,
+                        'is_discovering': is_discovering,
+                        'unpause_discovery_fails' : not unpause_discovery,
+                        'error' : self._compare_error(error,
+                                    DBUS_ERRORS['Failed']['discovery_unpause']),
+                        'inquiry_after_unpause' : inquiry_after_unpause
+                        }
+        return all(self.results.values())
+
+    @_test_retry_and_log(False)
+    def test_dbus_unpause_discovery_fail_power_off(self):
+        """ Test Failure case of unpause_discovery call.
+
+        unpause discovery when adapter is turned off and confirm it fails with
+         'org.bluez.Error.Failed: Discovery not paused'
+
+        """
+        reset = self._reset_state()
+        is_power_off = self._wait_till_power_off()
+
+        unpause_discovery, error = self.bluetooth_facade.unpause_discovery()
+
+        self.results = {'reset' : reset,
+                        'is_power_off' : is_power_off,
+                        'unpause_discovery_failed' : not unpause_discovery,
+                        'error_matches' : self._compare_error(error,
+                                    DBUS_ERRORS['Failed']['discovery_unpause']),
+
+                       }
+        return all(self.results.values())
+
+
+    @_test_retry_and_log(False)
+    def test_dbus_unpause_discovery_fail_already_unpaused(self):
+        """ Test Failure case of unpause_discovery call.
+
+        Call unpause discovery twice and make sure second call fails
+        with 'org.bluez.Error.InProgress: Operation already in progress'.
+        """
+        reset = self._reset_state()
+        is_discovering = self._wait_till_discovery_starts()
+        pause_discovery, error = self.bluetooth_facade.pause_discovery()
+        unpause_discovery, _ = self.bluetooth_facade.unpause_discovery()
+
+        unpause_again, error = self.bluetooth_facade.unpause_discovery()
+
+        inquiry_after_unpause = self._wait_till_hci_state_inquiry()
+
+        self.results = {
+            'reset' : reset,
+            'is_discovering': is_discovering,
+            'pause_discovery' : pause_discovery,
+            'unpause_discovery' : unpause_discovery,
+            'unpause_again_failed': not unpause_again,
+            'error_matches' : self._compare_error(error,
+                                    DBUS_ERRORS['Failed']['discovery_unpause']),
+            'inquiry_after_unpause':inquiry_after_unpause
+        }
+        return all(self.results.values())
 
 ########################################################################
 # dbus call: get_suppported_capabilities
diff --git a/server/site_tests/bluetooth_AdapterSASanity/bluetooth_AdapterSASanity.py b/server/site_tests/bluetooth_AdapterSASanity/bluetooth_AdapterSASanity.py
index 235d036..83fd880 100644
--- a/server/site_tests/bluetooth_AdapterSASanity/bluetooth_AdapterSASanity.py
+++ b/server/site_tests/bluetooth_AdapterSASanity/bluetooth_AdapterSASanity.py
@@ -195,13 +195,21 @@
         self.test_dbus_start_discovery_success()
         self.test_dbus_start_discovery_fail_discovery_in_progress()
         self.test_dbus_start_discovery_fail_power_off()
+
         self.test_dbus_stop_discovery_success()
         self.test_dbus_stop_discovery_fail_discovery_not_in_progress()
         self.test_dbus_stop_discovery_fail_power_off()
+
         self.test_dbus_pause_discovery_success()
         self.test_dbus_pause_discovery_success_no_discovery_in_progress()
         self.test_dbus_pause_discovery_fail_already_paused()
         self.test_dbus_pause_discovery_fail_power_off()
+
+        self.test_dbus_unpause_discovery_success()
+        self.test_dbus_unpause_discovery_fail_without_pause()
+        self.test_dbus_unpause_discovery_fail_power_off()
+        self.test_dbus_unpause_discovery_fail_already_unpaused()
+
         self.test_dbus_get_supported_capabilities_success()
         self.test_dbus_get_supported_capabilities_success_power_off()