bluetooth: Add peer wake stress tests

Add stress tests for peer wake as well. This can be helpful to really
exercise the wake up logic and make sure it is stable during bringup and
stability testing.

BUG=b:168246035
TEST=Ran stress tests on Hatch

Change-Id: I5eb3542b3a03d49127648ca73654926145bb2470
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2415234
Tested-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Auto-Submit: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: Daniel Winkler <danielwinkler@google.com>
Commit-Queue: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
diff --git a/server/site_tests/bluetooth_AdapterSRSanity/bluetooth_AdapterSRSanity.py b/server/site_tests/bluetooth_AdapterSRSanity/bluetooth_AdapterSRSanity.py
index f22694d..115e487 100644
--- a/server/site_tests/bluetooth_AdapterSRSanity/bluetooth_AdapterSRSanity.py
+++ b/server/site_tests/bluetooth_AdapterSRSanity/bluetooth_AdapterSRSanity.py
@@ -209,17 +209,20 @@
     # Wake from suspend tests
     # ---------------------------------------------------------------
 
-    def run_peer_wakeup_device(self, device_type, device, device_test=None):
+    def run_peer_wakeup_device(self,
+                               device_type,
+                               device,
+                               device_test=None,
+                               iterations=1):
         """ Uses paired peer device to wake the device from suspend.
 
         @param device_type: the device type (used to determine if it's LE)
         @param device: the meta device with the paired device
         @param device_test: What to test to run after waking and connecting the
                             adapter/host
+        @param iterations: Number of suspend + peer wake loops to run
         """
         boot_id = self.host.get_boot_id()
-        suspend = self.suspend_async(
-            suspend_time=EXPECT_PEER_WAKE_SUSPEND_SEC, expect_bt_wake=True)
 
         # Clear wake before testing
         self.test_adapter_set_wake_disabled()
@@ -241,38 +244,47 @@
             else:
                 time.sleep(PROFILE_CONNECT_WAIT)
 
-            # Wait until powerd marks adapter as wake enabled
-            self.test_adapter_wake_enabled()
+            for it in xrange(iterations):
+                logging.info(
+                        'Running iteration {}/{} of suspend peer wake'.format(
+                                it + 1, iterations))
 
-            # Trigger suspend, asynchronously trigger wake and wait for resume
-            self.test_suspend_and_wait_for_sleep(
-                suspend, sleep_timeout=5)
+                # Start a new suspend instance
+                suspend = self.suspend_async(
+                        suspend_time=EXPECT_PEER_WAKE_SUSPEND_SEC,
+                        expect_bt_wake=True)
 
-            adapter_address = self.bluetooth_facade.address
+                # Wait until powerd marks adapter as wake enabled
+                self.test_adapter_wake_enabled()
 
-            # Trigger peer wakeup
-            peer_wake = self.device_connect_async(device_type,
-                                                  device,
-                                                  adapter_address,
-                                                  delay_wake=5)
-            peer_wake.start()
+                # Trigger suspend, asynchronously wake and wait for resume
+                self.test_suspend_and_wait_for_sleep(suspend, sleep_timeout=5)
 
-            # Expect a quick resume. If a timeout occurs, test fails. Since we
-            # delay sending the wake signal, we should accomodate that in our
-            # expected timeout.
-            self.test_wait_for_resume(
-                boot_id, suspend, resume_timeout=SUSPEND_SEC + 5,
-                fail_on_timeout=True)
+                # Trigger peer wakeup
+                adapter_address = self.bluetooth_facade.address
+                peer_wake = self.device_connect_async(device_type,
+                                                      device,
+                                                      adapter_address,
+                                                      delay_wake=5)
+                peer_wake.start()
 
-            # Finish peer wake process
-            peer_wake.join()
+                # Expect a quick resume. If a timeout occurs, test fails. Since
+                # we delay sending the wake signal, we should accommodate that
+                # in our expected timeout.
+                self.test_wait_for_resume(boot_id,
+                                          suspend,
+                                          resume_timeout=SUSPEND_SEC + 5,
+                                          fail_on_timeout=True)
 
-            # Make sure we're actually connected
-            self.test_device_is_connected(device.address)
-            self.test_hid_device_created(device.address)
+                # Finish peer wake process
+                peer_wake.join()
 
-            if device_test is not None:
-                device_test(device)
+                # Make sure we're actually connected
+                self.test_device_is_connected(device.address)
+                self.test_hid_device_created(device.address)
+
+                if device_test is not None:
+                    device_test(device)
 
         finally:
             self.test_remove_pairing(device.address)
@@ -302,6 +314,34 @@
         self.run_peer_wakeup_device(
             'BLE_MOUSE', device, device_test=self.test_mouse_left_click)
 
+    # TODO(b/151332866) - Bob can't wake from suspend due to wrong power/wakeup
+    # TODO(b/150897528) - Dru is powered down during suspend, won't wake up
+    @test_wrapper('Peer wakeup Classic HID',
+                  devices={'MOUSE': 1},
+                  skip_models=TABLET_MODELS + ['bob', 'dru'],
+                  skip_chipsets=['Realtek-RTL8822C-USB'])
+    def sr_peer_wake_classic_hid_stress(self):
+        """ Use classic HID device to wake from suspend. """
+        device = self.devices['MOUSE'][0]
+        self.run_peer_wakeup_device('MOUSE',
+                                    device,
+                                    device_test=self.test_mouse_left_click,
+                                    iterations=STRESS_ITERATIONS)
+
+    # TODO(b/151332866) - Bob can't wake from suspend due to wrong power/wakeup
+    # TODO(b/150897528) - Dru is powered down during suspend, won't wake up
+    @test_wrapper('Peer wakeup LE HID',
+                  devices={'BLE_MOUSE': 1},
+                  skip_models=TABLET_MODELS + ['bob', 'dru'],
+                  skip_chipsets=['Realtek-RTL8822C-USB'])
+    def sr_peer_wake_le_hid_stress(self):
+        """ Use LE HID device to wake from suspend. """
+        device = self.devices['BLE_MOUSE'][0]
+        self.run_peer_wakeup_device('BLE_MOUSE',
+                                    device,
+                                    device_test=self.test_mouse_left_click,
+                                    iterations=STRESS_ITERATIONS)
+
     @test_wrapper('Peer wakeup with A2DP should fail')
     def sr_peer_wake_a2dp_should_fail(self):
         """ Use A2DP device to wake from suspend and fail. """
diff --git a/server/site_tests/bluetooth_AdapterSRSanity/control.sr_peer_wake_classic_hid_stress b/server/site_tests/bluetooth_AdapterSRSanity/control.sr_peer_wake_classic_hid_stress
new file mode 100644
index 0000000..0f1505d
--- /dev/null
+++ b/server/site_tests/bluetooth_AdapterSRSanity/control.sr_peer_wake_classic_hid_stress
@@ -0,0 +1,29 @@
+# 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.
+
+from autotest_lib.server import utils
+from autotest_lib.server.cros.bluetooth import bluetooth_test
+
+AUTHOR = 'chromeos-bluetooth'
+NAME = 'bluetooth_AdapterSRSanity.sr_peer_wake_classic_hid_stress'
+PURPOSE = ('Single run of a test')
+CRITERIA = 'Pass test'
+ATTRIBUTES = ''
+TIME = 'MEDIUM' # ~8 mins on Hatch
+TEST_CATEGORY = 'Functional'
+TEST_CLASS = 'bluetooth'
+TEST_TYPE = 'server'
+DEPENDENCIES = 'bluetooth, working_bluetooth_btpeer:1'
+
+DOC = """ Single run of a Suspend-Resume sanity testcase. """
+
+args_dict = utils.args_to_dict(args)
+btpeer_args = hosts.CrosHost.get_btpeer_arguments(args_dict)
+
+def run(machine):
+    host = hosts.create_host(machine, btpeer_args=btpeer_args)
+    job.run_test('bluetooth_AdapterSRSanity', host=host, num_iterations=1,
+                 test_name=NAME.split('.')[1])
+
+parallel_simple(run, machines)
diff --git a/server/site_tests/bluetooth_AdapterSRSanity/control.sr_peer_wake_le_hid_stress b/server/site_tests/bluetooth_AdapterSRSanity/control.sr_peer_wake_le_hid_stress
new file mode 100644
index 0000000..65e303f
--- /dev/null
+++ b/server/site_tests/bluetooth_AdapterSRSanity/control.sr_peer_wake_le_hid_stress
@@ -0,0 +1,29 @@
+# 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.
+
+from autotest_lib.server import utils
+from autotest_lib.server.cros.bluetooth import bluetooth_test
+
+AUTHOR = 'chromeos-bluetooth'
+NAME = 'bluetooth_AdapterSRSanity.sr_peer_wake_le_hid_stress'
+PURPOSE = ('Single run of a test')
+CRITERIA = 'Pass test'
+ATTRIBUTES = ''
+TIME = 'MEDIUM' # ~8 mins on Hatch
+TEST_CATEGORY = 'Functional'
+TEST_CLASS = 'bluetooth'
+TEST_TYPE = 'server'
+DEPENDENCIES = 'bluetooth, working_bluetooth_btpeer:1'
+
+DOC = """ Single run of a Suspend-Resume sanity testcase. """
+
+args_dict = utils.args_to_dict(args)
+btpeer_args = hosts.CrosHost.get_btpeer_arguments(args_dict)
+
+def run(machine):
+    host = hosts.create_host(machine, btpeer_args=btpeer_args)
+    job.run_test('bluetooth_AdapterSRSanity', host=host, num_iterations=1,
+                 test_name=NAME.split('.')[1])
+
+parallel_simple(run, machines)