/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0/ * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acsdkBluetooth/BasicDeviceConnectionRule.h" #include "acsdkBluetooth/Bluetooth.h" #include "acsdkBluetooth/BluetoothNotifier.h" #include "acsdkBluetooth/DeviceConnectionRulesAdapter.h" #include "acsdkBluetooth/SQLiteBluetoothStorage.h" namespace alexaClientSDK { namespace acsdkBluetooth { namespace test { using namespace avsCommon::avs; using namespace avsCommon::avs::attachment; using namespace avsCommon::avs::attachment::test; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::sdkInterfaces::bluetooth; using namespace avsCommon::sdkInterfaces::bluetooth::services; using namespace avsCommon::sdkInterfaces::bluetooth::test; using namespace avsCommon::sdkInterfaces::bluetooth::services::test; using namespace avsCommon::sdkInterfaces::test; using namespace avsCommon::utils; using namespace avsCommon::utils::bluetooth; using namespace avsCommon::utils::configuration; using namespace avsCommon::utils::json; using namespace avsCommon::utils::mediaPlayer::test; using namespace ::testing; using namespace rapidjson; /// String to identify log entries originating from this file. static const std::string TAG{"BluetoothTest"}; /** * Create a LogEntry using this file's TAG and the specified event string. * * @param The event string for this @c LogEntry. */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) /// Test Bluetooth device mac address 1. static const std::string TEST_BLUETOOTH_DEVICE_MAC = "01:23:45:67:89:ab"; /// Test Bluetooth device friendly name 1. static const std::string TEST_BLUETOOTH_FRIENDLY_NAME = "test_friendly_name_1"; /// Test Bluetooth device uuid 1. static const std::string TEST_BLUETOOTH_UUID = "650f973b-c2ab-4c6e-bff4-3788cd521340"; /// Test Bluetooth device mac address 2. static const std::string TEST_BLUETOOTH_DEVICE_MAC_2 = "11:23:45:67:89:ab"; /// Test Bluetooth device friendly name 2. static const std::string TEST_BLUETOOTH_FRIENDLY_NAME_2 = "test_friendly_name_2"; /// Test Bluetooth device uuid 2. static const std::string TEST_BLUETOOTH_UUID_2 = "650f973b-c2ab-4c6e-bff4-3788cd521341"; /// Test Bluetooth device mac address 3. static const std::string TEST_BLUETOOTH_DEVICE_MAC_3 = "21:23:45:67:89:ab"; /// Test Bluetooth device friendly name 3. static const std::string TEST_BLUETOOTH_FRIENDLY_NAME_3 = "test_friendly_name_3"; /// Test Bluetooth device uuid 3. static const std::string TEST_BLUETOOTH_UUID_3 = "650f973b-c2ab-4c6e-bff4-3788cd521342"; /// Test Database file name. Can be changed if there are conflicts. static const std::string TEST_DATABASE = "BluetoothCATest.db"; // clang-format off static const std::string BLUETOOTH_JSON = R"( { "bluetooth" : { "databaseFilePath":")" + TEST_DATABASE + R"(" } } )"; // clang-format on /// Error message for when the file already exists. static const std::string FILE_EXISTS_ERROR = "Database File " + TEST_DATABASE + " already exists."; /// Namespace of Bluetooth. static const std::string NAMESPACE_BLUETOOTH = "Bluetooth"; /// THe Bluetooth state portion of the Context. static const NamespaceAndName BLUETOOTH_STATE{NAMESPACE_BLUETOOTH, "BluetoothState"}; /// JSON key for the event section of a message. static const std::string MESSAGE_EVENT_KEY = "event"; /// JSON key for the header section of a message. static const std::string MESSAGE_HEADER_KEY = "header"; /// JSON key for the name section of a message. static const std::string MESSAGE_NAME_KEY = "name"; /// JSON key for the payload section of a message. static const std::string PAYLOAD_KEY = "payload"; /// JSON key for the requester section of a message. static const std::string REQUESTER_KEY = "requester"; /// JSON value for the cloud requester. static const std::string CLOUD_REQUESTER_VALUE = "CLOUD"; /// JSON value for the device requester. static const std::string DEVICE_REQUESTER_VALUE = "DEVICE"; /// ConnectByDevice directive. static const std::string CONNECT_BY_DEVICE_IDS_DIRECTIVE = "ConnectByDeviceIds"; /// ConnectByProfile directive. static const std::string CONNECT_BY_PROFILE_DIRECTIVE = "ConnectByProfile"; /// PairDevice directive static const std::string PAIR_DEVICES_DIRECTIVE = "PairDevices"; /// UnpairDevice directive static const std::string UNPAIR_DEVICES_DIRECTIVE = "UnpairDevices"; /// DisconnectDevice directive static const std::string DISCONNECT_DEVICES_DIRECTIVE = "DisconnectDevices"; /// SetDeviceCategories directive static const std::string SET_DEVICE_CATEGORIES = "SetDeviceCategories"; /// Test message id. static const std::string TEST_MESSAGE_ID = "MessageId_Test"; /// Test message id. static const std::string TEST_MESSAGE_ID_2 = "MessageId_Test_2"; // clang-format off /// ConnectByDeviceIds payload. static const std::string TEST_CONNECT_BY_DEVICE_IDS_PAYLOAD = R"( { "devices" : [{ "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID + R"(", "friendlyName" :")" + TEST_BLUETOOTH_FRIENDLY_NAME + R"(" }, {"uniqueDeviceId":")" + TEST_BLUETOOTH_UUID_2 + R"(", "friendlyName" :")" + TEST_BLUETOOTH_FRIENDLY_NAME_2 + R"(" }] } )"; // clang-format on /// The @c ConnectByDeviceIdSucceeded event name. static const std::string CONNECT_BY_DEVICE_IDS_SUCCEEDED = "ConnectByDeviceIdsSucceeded"; /// The @c ConnectByProfileSucceeded event name. static const std::string CONNECT_BY_PROFILE_SUCCEEDED = "ConnectByProfileSucceeded"; /// The @c ConnectByProfileFailed event name. static const std::string CONNECT_BY_PROFILE_FAILED = "ConnectByProfileFailed"; /// The @c PairDeviceSucceeded event name. static const std::string PAIR_DEVICES_SUCCEEDED = "PairDevicesSucceeded"; /// The @c UnpairDeviceSucceeded event name. static const std::string UNPAIR_DEVICES_SUCCEEDED = "UnpairDevicesSucceeded"; /// The @c SetDeviceCategoriesSucceeded event name. static const std::string SET_DEVICE_CATEGORIES_SUCCEEDED = "SetDeviceCategoriesSucceeded"; /// The @c DisconnectDeviceSucceeded event name. static const std::string DISCONNECT_DEVICES_SUCCEEDED = "DisconnectDevicesSucceeded"; /// The @c ScanDevicesUpdated event name. static const std::string SCAN_DEVICES_REPORT = "ScanDevicesReport"; /// The @c StreamingStarted event name. static const std::string STREAMING_STARTED = "StreamingStarted"; /// The @c StreamingEnded event name. static const std::string STREAMING_ENDED = "StreamingEnded"; /// Test unmatched profile name. static const std::string TEST_UNMATCHED_PROFILE_NAME = "HFP"; /// Test matched profile name. static const std::string TEST_MATCHED_PROFILE_NAME = "AVRCP"; /// Test profile version. static const std::string TEST_PROFILE_VERSION = "1"; // clang-format off /// ConnectByDeviceProfile payload 1. static const std::string TEST_CONNECT_BY_PROFILE_PAYLOAD_1 = R"( { "profile" : { "name":")" + TEST_UNMATCHED_PROFILE_NAME + R"(", "version" :")" + TEST_PROFILE_VERSION + R"(" } } )"; // clang-format on // clang-format off /// ConnectByDeviceProfile payload 2 static const std::string TEST_CONNECT_BY_PROFILE_PAYLOAD_2 = R"( { "profile" : { "name":")" + TEST_MATCHED_PROFILE_NAME + R"(", "version" :")" + TEST_PROFILE_VERSION + R"(" } } )"; // clang-format on // clang-format off /// PairDevices payload static const std::string TEST_PAIR_DEVICES_PAYLOAD = R"( { "devices" : [{ "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID + R"(" }, { "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID_2 + R"(" }] } )"; // clang-format on // clang-format off /// UnpairDevices payload static const std::string TEST_UNPAIR_DEVICES_PAYLOAD = R"( { "devices" : [{ "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID + R"(" }, { "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID_2 + R"(" }] } )"; // clang-format on // clang-format off /// DisconnectDevices payload static const std::string TEST_DISCONNECT_DEVICES_PAYLOAD = R"( { "devices" : [{ "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID + R"(" }, { "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID_2 + R"(" }] } )"; // clang-format on // clang-format off /// SetDeviceCategories payload static const std::string TEST_SET_DEVICE_CATEGORIES_PAYLOAD = R"( { "devices" : [{ "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID + R"(", "deviceCategory": "PHONE" }, { "uniqueDeviceId":")" + TEST_BLUETOOTH_UUID_2 + R"(", "deviceCategory": "GADGET" }] } )"; // clang-format on // clang-format off /// Mock Context static const std::string MOCK_CONTEXT = R"( { "context": [{ "header": { "namespace": "Bluetooth", "name": "BluetoothState" }, "payload": { "alexaDevice": { "friendlyName": "{{STRING}}" }, "pairedDevices": [{ "uniqueDeviceId": "{{STRING}}", "friendlyName": "{{STRING}}", "supportedProfiles": [{ "name": "{{STRING}}", "version": "{{STRING}}" }] }], "activeDevice": { "uniqueDeviceId": "{{STRING}}", "friendlyName": "{{STRING}}", "supportedProfiles": [{ "name": "{{STRING}}", "version": "{{STRING}}" }], "streaming": "{{STRING}}" } } }] } )"; /// Wait timeout. static std::chrono::milliseconds WAIT_TIMEOUT_MS(1000); // Delay to let events happen / threads catch up static std::chrono::milliseconds EVENT_PROCESS_DELAY_MS(500); /** * Checks whether a file exists in the file system. * * @param file The file name. * @return Whether the file exists. */ static bool fileExists(const std::string& file) { std::ifstream dbFile(file); return dbFile.good(); } class BluetoothTest: public ::testing::Test { public: void SetUp() override; void TearDown() override; /// @c Bluetooth to test std::shared_ptr m_Bluetooth; /// @c ContextManager to provide state and update state. std::shared_ptr m_mockContextManager; /// @c FocusManager to request focus to the CONTENT channel. acsdkManufactory::Annotated< avsCommon::sdkInterfaces::AudioFocusAnnotation, avsCommon::sdkInterfaces::FocusManagerInterface> m_annotatedFocusManager; /// The mock focus manager, wrapped by m_annotatedFocusManager. Keeping a reference to this mock is /// required in order to set expectations on it. std::shared_ptr m_mockFocusManager; /// A message sender used to send events to AVS. std::shared_ptr m_mockMessageSender; /// An exception sender used to send exception encountered events to AVS. std::shared_ptr m_mockExceptionSender; /// The storage component for @c Bluetooth. std::shared_ptr m_bluetoothStorage; /// Player to send the audio to. std::shared_ptr m_mockBluetoothMediaPlayer; /// A bus to abstract Bluetooth stack specific messages. std::shared_ptr m_eventBus; /// Object that will track the CustomerDataHandler. std::shared_ptr> m_customerDataManager; /// @c BluetoothHostController to create @c MockBluetoothDeviceManager std::shared_ptr m_mockBluetoothHostController; /// The list of discovered devices to create @c MockBluetoothDeviceManager std::list> m_mockDiscoveredBluetoothDevices; /// The mock @c ApplicationAudioPipelineFactoryInterface. std::shared_ptr m_mockAudioPipelineFactory; /// An endpoint capabilities registrar with which to register the Bluetooth CA. acsdkManufactory::Annotated< avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, avsCommon::sdkInterfaces::endpoints::EndpointCapabilitiesRegistrarInterface> m_mockEndpointCapabilitiesRegistrar; /// An object that provides the Bluetooth device connection rules. std::shared_ptr m_connectionRulesProvider; /// Object to notify the Bluetooth CA when to shut down. std::shared_ptr m_shutdownNotifier; /// Bluetooth devices used to test the Bluetooth CA connection logic. std::shared_ptr m_mockBluetoothDevice1; std::shared_ptr m_mockBluetoothDevice2; std::shared_ptr m_mockBluetoothDevice3; /// Bluetooth device connection rules. /// Bluetooth device connection rule for DeviceCategory::REMOTE_CONTROL. std::shared_ptr m_remoteControlConnectionRule; /// Bluetooth device connection rule for DeviceCategory::GADGET. std::shared_ptr m_gadgetConnectionRule; /// A manager to take care of Bluetooth devices. std::unique_ptr m_mockDeviceManager; /// A directive handler result to send the result to. std::unique_ptr m_mockDirectiveHandlerResult; /// An observer to be notified of the Bluetooth connection change. std::shared_ptr m_mockBluetoothDeviceObserver; /// A @c ChannelVolumeInterface object to control volume std::shared_ptr m_mockChannelVolumeInterface; /// A @c BluetoothNotifierInterface to notify observers. std::shared_ptr m_bluetoothNotifier; /// Condition variable to wake on a message being sent. std::condition_variable m_messageSentTrigger; /// Expected messages. std::map m_messages; /// Function to wait for @c m_wakeSetCompleteFuture to be set. void wakeOnSetCompleted(); /** * Get the request event name. * * @param request The @c MessageRequest to verify. * @return The event name. */ std::string getRequestName(std::shared_ptr request); /** * Verify that the message name matches the expected name. * * @param request The @c MessageRequest to verify. * @param expectedName The expected name to find in the json header. * @return true if the message name matched the expect name. */ bool verifyMessage(std::shared_ptr request, std::string expectedName); /** * Verify that the messages sent matches the ordered list of events. * * @param request The @c MessageRequest to verify. * @param trigger The function to trigger sending the messages. * @return true if the messages sent matches the ordered list of events. */ bool verifyMessagesSentInOrder(std::vector orderedEvents, std::function trigger); /** * Verify that the messages sent matches the count. * * @param request The @c MessageRequest to verify. * @param messages The Map expected messages. */ void verifyMessagesCount(std::shared_ptr request, std::map* messages); /// A Constructor which initializes the promises and futures needed for the test class. BluetoothTest() : m_wakeSetCompletedPromise{}, m_wakeSetCompletedFuture{m_wakeSetCompletedPromise.get_future()} { } protected: /// Promise to synchronize directive handling through SetCompleted. std::promise m_wakeSetCompletedPromise; /// Future to synchronize directive handling through SetCompleted. std::future m_wakeSetCompletedFuture; }; void BluetoothTest::SetUp() { m_mockContextManager = std::make_shared>(); m_mockMessageSender = std::make_shared>(); m_mockExceptionSender = std::make_shared>(); m_bluetoothNotifier = std::make_shared(); m_shutdownNotifier = std::make_shared>(); m_mockAudioPipelineFactory = std::make_shared(); auto registrar = std::make_shared>(); m_mockEndpointCapabilitiesRegistrar = acsdkManufactory::Annotated< avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, avsCommon::sdkInterfaces::endpoints::EndpointCapabilitiesRegistrarInterface>( registrar); m_mockFocusManager = std::make_shared>(); m_annotatedFocusManager = acsdkManufactory:: Annotated( m_mockFocusManager); m_eventBus = std::make_shared(); m_mockBluetoothHostController = std::make_shared>(); m_mockDirectiveHandlerResult = std::unique_ptr(new MockDirectiveHandlerResult); m_mockBluetoothDeviceObserver = std::make_shared>(); m_mockBluetoothMediaPlayer = MockMediaPlayer::create(); m_customerDataManager = std::make_shared>(); m_bluetoothNotifier->addObserver(m_mockBluetoothDeviceObserver); /* * Create Mock Devices. */ auto metaData =MockBluetoothDevice::MetaData(Optional(), Optional(), MockBluetoothDevice::MetaData::UNDEFINED_CLASS_VALUE, Optional(), Optional()); auto a2dpSink = std::make_shared>(std::make_shared("")); auto avrcpTarget = std::make_shared>(std::make_shared("")); std::vector> services = {a2dpSink, avrcpTarget}; m_mockBluetoothDevice1 = std::make_shared>( TEST_BLUETOOTH_DEVICE_MAC, TEST_BLUETOOTH_FRIENDLY_NAME, metaData, services); m_mockDiscoveredBluetoothDevices.push_back(m_mockBluetoothDevice1); auto metaData2 =MockBluetoothDevice::MetaData(Optional(), Optional(), MockBluetoothDevice::MetaData::UNDEFINED_CLASS_VALUE, Optional(), Optional()); auto hid = std::make_shared>(std::make_shared("")); auto spp = std::make_shared>(std::make_shared("")); auto a2dpSource = std::make_shared>(std::make_shared("")); std::vector> services2 = {spp, hid, a2dpSource}; m_mockBluetoothDevice2 = std::make_shared>( TEST_BLUETOOTH_DEVICE_MAC_2, TEST_BLUETOOTH_FRIENDLY_NAME_2, metaData2, services2); m_mockDiscoveredBluetoothDevices.push_back(m_mockBluetoothDevice2); auto metaData3 =MockBluetoothDevice::MetaData(Optional(), Optional(), MockBluetoothDevice::MetaData::UNDEFINED_CLASS_VALUE, Optional(), Optional()); std::vector> services3 = {a2dpSink}; m_mockBluetoothDevice3 = std::make_shared>( TEST_BLUETOOTH_DEVICE_MAC_3, TEST_BLUETOOTH_FRIENDLY_NAME_3, metaData3, services3); m_mockDiscoveredBluetoothDevices.push_back(m_mockBluetoothDevice3); /* * Create mock device connection rules. */ std::set remoteCategory{DeviceCategory::REMOTE_CONTROL}; std::set remoteDependentProfiles{HIDInterface::UUID, SPPInterface::UUID}; m_remoteControlConnectionRule = std::make_shared>(remoteCategory, remoteDependentProfiles); std::set gadgetCategory{DeviceCategory::GADGET}; std::set gadgetDependentProfiles{HIDInterface::UUID, SPPInterface::UUID}; m_gadgetConnectionRule = std::make_shared>(gadgetCategory, gadgetDependentProfiles); /* * GadgetConnectionRule: * 1) No need to explicitly disconnect/connect device. * 2) No devices needed to disconnect when a new device with DeviceCategory::GADGET connects. */ m_gadgetConnectionRule->setExplicitlyConnect(false); m_gadgetConnectionRule->setExplicitlyDisconnect(true); /* * RemoteControlConnectionRule: * 1) No need to explicitly disconnect/connect device. * 2) Devices with DeviceCategory::REMOTE_CONTROL needed to disconnect when a new device with * DeviceCategory::REMOTE_CONTROL connects. */ m_remoteControlConnectionRule->setExplicitlyConnect(false); m_remoteControlConnectionRule->setExplicitlyDisconnect(false); std::unordered_set> mockConnectionRules = {m_remoteControlConnectionRule, m_gadgetConnectionRule, BasicDeviceConnectionRule::create()}; m_connectionRulesProvider = std::make_shared(mockConnectionRules); /** * create MockChannelVolumeInterface for ducking. */ m_mockChannelVolumeInterface = std::make_shared(); m_mockChannelVolumeInterface->DelegateToReal(); /** * Set up expected calls to injected objects. */ EXPECT_CALL( *(m_mockAudioPipelineFactory.get()), createApplicationMediaInterfaces(acsdkBluetooth::BLUETOOTH_MEDIA_PLAYER_NAME, _, _, _, _, _)) .WillRepeatedly(Return(std::make_shared( m_mockBluetoothMediaPlayer, nullptr, nullptr, nullptr, m_mockChannelVolumeInterface))); EXPECT_CALL( *(registrar.get()), withCapability(A&>(), _)) .WillRepeatedly(ReturnRef( *(std::make_shared()).get())); /* * Generate a Bluetooth database for testing. * Ensure the db file does not exist already. We don't want to overwrite anything. */ if (fileExists(TEST_DATABASE)) { ADD_FAILURE() << FILE_EXISTS_ERROR; exit(1); } auto json = std::shared_ptr(new std::stringstream()); *json << BLUETOOTH_JSON; std::vector> jsonStream; jsonStream.push_back(json); ConfigurationNode::initialize(jsonStream); m_bluetoothStorage = SQLiteBluetoothStorage::create(ConfigurationNode::getRoot()); ASSERT_TRUE(m_bluetoothStorage->createDatabase()); // Insert the test device data into the test database m_bluetoothStorage->insertByMac(TEST_BLUETOOTH_DEVICE_MAC, TEST_BLUETOOTH_UUID, true); m_bluetoothStorage->insertByMac(TEST_BLUETOOTH_DEVICE_MAC_2, TEST_BLUETOOTH_UUID_2, true); m_bluetoothStorage->insertByMac(TEST_BLUETOOTH_DEVICE_MAC_3, TEST_BLUETOOTH_UUID_3, true); m_bluetoothStorage->close(); m_Bluetooth = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); ASSERT_THAT(m_Bluetooth, NotNull()); } void BluetoothTest::TearDown() { m_bluetoothNotifier->removeObserver(m_mockBluetoothDeviceObserver); if (m_Bluetooth) { m_Bluetooth->shutdown(); } m_mockBluetoothMediaPlayer->shutdown(); if (fileExists(TEST_DATABASE)) { remove(TEST_DATABASE.c_str()); } m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::UNKNOWN)); m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_2, deviceCategoryToString(DeviceCategory::UNKNOWN)); m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_3, deviceCategoryToString(DeviceCategory::UNKNOWN)); } void BluetoothTest::wakeOnSetCompleted() { m_wakeSetCompletedPromise.set_value(); } std::string BluetoothTest::getRequestName(std::shared_ptr request) { rapidjson::Document document; document.Parse(request->getJsonContent().c_str()); EXPECT_FALSE(document.HasParseError()); auto event = document.FindMember(MESSAGE_EVENT_KEY); EXPECT_NE(event, document.MemberEnd()); auto header = event->value.FindMember(MESSAGE_HEADER_KEY); EXPECT_NE(header, event->value.MemberEnd()); auto payload = event->value.FindMember(PAYLOAD_KEY); EXPECT_NE(payload, event->value.MemberEnd()); std::string requestName; jsonUtils::retrieveValue(header->value, MESSAGE_NAME_KEY, &requestName); return requestName; } bool BluetoothTest::verifyMessage(std::shared_ptr request, std::string expectedName) { return getRequestName(request) == expectedName; } bool BluetoothTest::verifyMessagesSentInOrder(std::vector orderedEvents, std::function trigger) { size_t curIndex = 0; std::mutex waitMutex; EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) .Times(AtLeast(1)) .WillRepeatedly( Invoke([this, orderedEvents, &curIndex](std::shared_ptr request) { if (curIndex < orderedEvents.size()) { if (verifyMessage(request, orderedEvents.at(curIndex))) { if (curIndex < orderedEvents.size()) { curIndex++; } } } m_messageSentTrigger.notify_one(); })); trigger(); bool result; { std::unique_lock lock(waitMutex); result = m_messageSentTrigger.wait_for(lock, WAIT_TIMEOUT_MS, [orderedEvents, &curIndex] { if (curIndex == orderedEvents.size()) { return true; } return false; }); } return result; } void BluetoothTest::verifyMessagesCount(std::shared_ptr request, std::map* messages) { std::string requestName = getRequestName(request); if (messages->find(requestName) != messages->end()) { messages->at(requestName) += 1; } } /// Test that create() returns a nullptr if called with invalid arguments. TEST_F(BluetoothTest, test_createBTWithNullParams) { // Create Bluetooth CapabilityAgent with null @c ContextManager. auto bluetooth1 = Bluetooth::createBluetoothCapabilityAgent( nullptr, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth1, IsNull()); // Create Bluetooth CapabilityAgent with null @c FocusManager auto bluetooth2 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, nullptr, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth2, IsNull()); // Create Bluetooth CapabilityAgent with null @c MessageSender auto bluetooth3 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, nullptr, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth3, IsNull()); // Create Bluetooth CapabilityAgent with null @c ExceptionEncounterSender auto bluetooth4 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, nullptr, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth4, IsNull()); // Create Bluetooth CapabilityAgent with null @c BluetoothStorage auto bluetooth5 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, nullptr, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth5, IsNull()); // Create Bluetooth CapabilityAgent with null @c DeviceManager auto bluetooth6 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, nullptr, m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth6, IsNull()); // Create Bluetooth CapabilityAgent with null @c BluetoothEventBus auto bluetooth7 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), nullptr, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth7, IsNull()); // Create Bluetooth CapabilityAgent with null @c ApplicationAudioPipelineFactoryInterface auto bluetooth8 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, nullptr, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth8, IsNull()); // Create Bluetooth CapabilityAgent with null @c EndpointCapabilitiesRegistrar. auto bluetooth9 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, nullptr, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth9, IsNull()); // Create Bluetooth CapabilityAgent with null @c BluetoothNotifier. auto bluetooth10 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, nullptr); EXPECT_THAT(bluetooth10, IsNull()); // Create Bluetooth CapabilityAgent with null @c ShutdownNotifier. auto bluetooth11 = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, nullptr, m_mockEndpointCapabilitiesRegistrar, m_connectionRulesProvider, nullptr, m_bluetoothNotifier); EXPECT_THAT(bluetooth11, IsNull()); } /** * Test that create() returns a nullptr if called with an invalid set of device connection rules. * Fail due to re-defined device category. */ TEST_F(BluetoothTest, test_createBTWithDuplicateDeviceCategoriesInConnectionRules) { std::set categories1{DeviceCategory::REMOTE_CONTROL}; std::set categories2{DeviceCategory::REMOTE_CONTROL, DeviceCategory::GADGET}; std::set dependentProfiles{HIDInterface::UUID, SPPInterface::UUID}; auto mockDeviceConnectionRule1 = std::make_shared(categories1, dependentProfiles); auto mockDeviceConnectionRule2 = std::make_shared(categories2, dependentProfiles); std::unordered_set> enabledRules = {mockDeviceConnectionRule1, mockDeviceConnectionRule2}; auto connectionRulesProvider = std::make_shared(enabledRules); auto bluetooth = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, connectionRulesProvider, nullptr, m_bluetoothNotifier); ASSERT_THAT(bluetooth, IsNull()); } /** * Test that create() returns a nullptr if called with an invalid set of device connection rules. * Fail due to lack of dependent profiles defined in the device connection rule. */ TEST_F(BluetoothTest, test_createBTWithLackOfProfilesInConnectionRules) { std::set categories{DeviceCategory::REMOTE_CONTROL}; std::set dependentProfiles{HIDInterface::UUID}; auto mockDeviceConnectionRule = std::make_shared(categories, dependentProfiles); std::unordered_set> enabledRules = {mockDeviceConnectionRule}; auto connectionRulesProvider = std::make_shared(enabledRules); auto bluetooth = Bluetooth::createBluetoothCapabilityAgent( m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_bluetoothStorage, avsCommon::utils::memory::make_unique>( m_mockBluetoothHostController, m_mockDiscoveredBluetoothDevices, m_eventBus), m_eventBus, m_customerDataManager, m_mockAudioPipelineFactory, m_annotatedFocusManager, m_shutdownNotifier, m_mockEndpointCapabilitiesRegistrar, connectionRulesProvider, nullptr, m_bluetoothNotifier); ASSERT_THAT(bluetooth, IsNull()); } /** * Test call to handle ConnectByDeviceIds directive with two matched A2DP device UUIDs. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. Both of them belong to * DeviceCategory::UNKNOWN. Both devices are in disconnected state. * Use case: * A ConnectByDeviceIds directive is sent down, whose payload contains the mockBluetoothDevice1 and * mockBluetoothDevice2 UUIDs. * Expected result: * 1) Only one device which connects later(m_mockBluetoothDevice1) will be connected eventually even * though both of them were connected successfully. However, The earlier connected device(m_mockBluetoothDevice2 * in this case) will be disconnected due to @c BasicDeviceConnectionRule. * 2) The observer should be notified device connection twice and disconnection once. * 3) Two @c ConnectByDeviceIdsSucceed events, both of which have CLOUD as @c Requester, and one * @c DisconnectDevicesSucceed event, should be sent to cloud. */ TEST_F(BluetoothTest, test_handleConnectByDeviceIdsDirectiveWithTwoA2DPDevices) { EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceConnected(_)).Times(Exactly(2)); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceDisconnected(_)).Times(Exactly(1)); EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) .Times(1) .WillOnce(InvokeWithoutArgs(this, &BluetoothTest::wakeOnSetCompleted)); EXPECT_CALL(*m_mockContextManager, setState(BLUETOOTH_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(3)); std::vector events = {CONNECT_BY_DEVICE_IDS_SUCCEEDED, CONNECT_BY_DEVICE_IDS_SUCCEEDED, DISCONNECT_DEVICES_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Create ConnectedByDeviceId Directive. auto attachmentManager = std::make_shared>(); auto avsMessageHeader = std::make_shared(NAMESPACE_BLUETOOTH, CONNECT_BY_DEVICE_IDS_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_CONNECT_BY_DEVICE_IDS_PAYLOAD, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); /* * Mimic the @c DeviceStateChangedEvent which should happen after device connection status changes. * In order to guarantee that all @c DeviceStateChangedEvent happen after the corresponding device connection * status changes, force the test to wait EVENT_PROCESS_DELAY_MS. * * TODO: Add send event to @c BluetoothEventBus within @c MockBluetoothDevice. */ std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::CONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::DISCONNECTED)); })); // Verify the connection result. ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); ASSERT_FALSE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle ConnectByDeviceIds directive with two matched device UUIDs with different device categories. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. m_mockBluetoothDevice1 * belongs to DeviceCategory::PHONE, m_mockBluetoothDevice2 belongs to DeviceCategory::GADGET. Both devices are * in disconnect state. * Use case: * A ConnectByDeviceIds directive is sent down, whose payload contains the mockBluetoothDevice1 and * mockBluetoothDevice2. * Expected result: * 1)Both devices should be connected successfully. * 2)The observer should be notified deivce connection twice. * 3)Two @c ConnectByDeviceIdsSucceed events, both of which have CLOUD as @c Requester,should be sent to cloud. */ TEST_F(BluetoothTest, test_handleConnectByDeviceIdsDirectiveWithOnePhoneOneGadget) { m_mockBluetoothDevice1->disconnect(); m_mockBluetoothDevice2->disconnect(); // Initially, all devices are stored as DeviceCategory::UNKNOWN. Need to manually update device category in order // to test. m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::PHONE)); m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_2, deviceCategoryToString(DeviceCategory::GADGET)); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceConnected(_)).Times(Exactly(2)); EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) .Times(1) .WillOnce(InvokeWithoutArgs(this, &BluetoothTest::wakeOnSetCompleted)); EXPECT_CALL(*m_mockContextManager, setState(BLUETOOTH_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(2)); // Verify the @c ConnectByDeviceIdsSucceed event. std::vector events = {CONNECT_BY_DEVICE_IDS_SUCCEEDED, CONNECT_BY_DEVICE_IDS_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Create ConnectedByDeviceId Directive. auto attachmentManager = std::make_shared>(); auto avsMessageHeader = std::make_shared(NAMESPACE_BLUETOOTH, CONNECT_BY_DEVICE_IDS_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_CONNECT_BY_DEVICE_IDS_PAYLOAD, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::CONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); })); // Verify the connection result. ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); ASSERT_TRUE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle ConnectByDeviceProfile directive with an unmatched profile name. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. * m_mockBluetoothDevice1 has @c A2DPSinkInterface and @c AVRCPTargetInterface services. * m_mockBluetoothDevice2 has @c HIDInterface and @c SPPInterface services. * Use case: * A @c ConnectByProfile directive is sent down, which contains @c HFPInterface as a profile. * Expected result: * 1) m_mockBluetoothDevice1 and m_mockBluetoothDevice2 should not be connected successfully. * 2) Because no device is connected, the observer should not be notified. * 3) One @c ConnectByDeviceProfileFailed event should be sent to cloud. */ TEST_F(BluetoothTest, test_handleConnectByProfileWithUnmatchedProfileName) { /* * Because We're only connecting devices that have been previously connected and currently paired. * Therefore, assume m_mockBluetoothDevice1 and m_mockBluetoothDevice2 are currently paired. */ m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice2->pair(); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceConnected(_)).Times(Exactly(0)); EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) .Times(1) .WillOnce(InvokeWithoutArgs(this, &BluetoothTest::wakeOnSetCompleted)); EXPECT_CALL(*m_mockContextManager, setState(BLUETOOTH_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(1)); std::vector events = {CONNECT_BY_PROFILE_FAILED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Create ConnectedByDeviceId Directive. auto attachmentManager = std::make_shared>(); auto avsMessageHeader = std::make_shared(NAMESPACE_BLUETOOTH, CONNECT_BY_PROFILE_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_CONNECT_BY_PROFILE_PAYLOAD_1, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); })); ASSERT_FALSE(m_mockBluetoothDevice1->isConnected()); ASSERT_FALSE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle ConnectByDeviceProfile directive with a matched profile. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. * m_mockBluetoothDevice1 has @c A2DPSinkInterface and @c AVRCPTargetInterface services. * m_mockBluetoothDevice2 has @c HIDInterface and @c SPPInterface services. * Use case: * A @c ConnectByProfile directive is sent down, which contains @c AVRCPTargetInterface as a profile. * Expected result: * 1) m_mockBluetoothDevice1 should be connected successfully. * 2) m_mockBluetoothDevice2 should not be connected. * 3) Because m_mockBluetoothDevice is connected, the observer should be notificed once. * 4) One @c ConnectByDeviceProfileSucceeded event should be sent to cloud. */ TEST_F(BluetoothTest, test_handleConnectByProfileWithMatchedProfileName) { std::mutex waitMutex; std::unique_lock waitLock(waitMutex); // Assume devices are paired previously. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice2->pair(); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceConnected(_)).Times(Exactly(1)); EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) .Times(1) .WillOnce(InvokeWithoutArgs(this, &BluetoothTest::wakeOnSetCompleted)); std::vector events = {CONNECT_BY_PROFILE_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Create ConnectedByDeviceId Directive. auto attachmentManager = std::make_shared>(); auto avsMessageHeader = std::make_shared(NAMESPACE_BLUETOOTH, CONNECT_BY_PROFILE_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_CONNECT_BY_PROFILE_PAYLOAD_2, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); })); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); ASSERT_FALSE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle the local connect() method. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1. The device belongs to * DeviceCategory::UNKNOWN. The device is in disconnected state. * Use case: * A local connect() call is made with the MAC address of m_mockBluetoothDevice1. * Expected result: * 1) m_mockBluetoothDevice1 connects. * 2) The observer should be notified of device connection once. * 3) One @c ConnectByDeviceIdsSucceed event. */ TEST_F(BluetoothTest, test_handleConnectLocal) { EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceConnected(_)).Times(Exactly(1)); EXPECT_CALL(*m_mockContextManager, setState(BLUETOOTH_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(1)); std::vector events = {CONNECT_BY_DEVICE_IDS_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Call connect() m_Bluetooth->connect(TEST_BLUETOOTH_DEVICE_MAC); /* * Mimic the @c DeviceStateChangedEvent which should happen after device connection status changes. * In order to guarantee that all @c DeviceStateChangedEvent happen after the corresponding device connection * status changes, force the test to wait EVENT_PROCESS_DELAY_MS. */ std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); })); // Verify the connection result. ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); } /** * Test call to handle PairDevices directive with matched device UUIDs. * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. * m_mockBluetoothDevice1 belongs to DeviceCategory::PHONE, which follows @c BasicDeviceConnectionRule. * m_mockBluetoothDevice2 belongs to DeviceCategory::GADGET, which follows @c GadgetConnectionRule. * Use case: * A @c PairDevices directive is sent down, whose payload contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. * Expected result: * 1) Both devices should be paired successfully. * 2) Based on the connection rule (m_mockBluetoothDevice1 follows @c BasicDeviceConnectionRule, * m_mockBluetoothDevice2 follows @c GadgetConnectionRule), m_mockBluetoothDevice1 should be connected successfully. * 3) A sequence of {PAIR_DEVICES_SUCCEEDED, CONNECT_BY_DEVICE_IDS_SUCCEEDED, PAIR_DEVICES_SUCCEEDED) * should be sent to cloud. * a. {PAIR_DEVICES_SUCCEEDED, CONNECT_BY_DEVICE_IDS_SUCCEEDED} are sent when Bluetooth CapabilityAgent tries to pair * m_mockBluetoothDevice1. * b. {PAIR_DEVICES_SUCCEEDED} is sent when Bluetooth CapabilityAgent tries to pair m_mockBluetoothDevice2. */ TEST_F(BluetoothTest, DISABLED_test_handlePairDevices) { // Initially, all devices are stored as DeviceCategory::UNKNOWN. Need to manually update device category in order // to test. m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::PHONE)); m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_2, deviceCategoryToString(DeviceCategory::GADGET)); m_messages.insert({PAIR_DEVICES_SUCCEEDED, 0}); m_messages.insert({CONNECT_BY_DEVICE_IDS_SUCCEEDED, 0}); std::mutex waitMutex; EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) .Times(1) .WillOnce(InvokeWithoutArgs(this, &BluetoothTest::wakeOnSetCompleted)); EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) .Times(AtLeast(1)) .WillRepeatedly(Invoke([this](std::shared_ptr request) { verifyMessagesCount(request, &m_messages); m_messageSentTrigger.notify_one(); })); // Create PairDevices Directive. auto attachmentManager = std::make_shared>(); auto avsMessageHeader = std::make_shared(NAMESPACE_BLUETOOTH, PAIR_DEVICES_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_PAIR_DEVICES_PAYLOAD, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::PAIRED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::PAIRED)); std::unique_lock lock(waitMutex); bool result; result = m_messageSentTrigger.wait_for(lock, WAIT_TIMEOUT_MS, [this] { for(auto message : m_messages) { if (message.first == PAIR_DEVICES_SUCCEEDED) { if (message.second != 2) { return false; } } else if (message.first == CONNECT_BY_DEVICE_IDS_SUCCEEDED) { if (message.second != 1) { return false; } } } return true; }); ASSERT_TRUE(result); // Verify the pair result. ASSERT_TRUE(m_mockBluetoothDevice1->isPaired()); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); ASSERT_TRUE(m_mockBluetoothDevice2->isPaired()); ASSERT_FALSE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle the local pair() method. * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1. * m_mockBluetoothDevice1 belongs to DeviceCategory::PHONE, which follows @c BasicDeviceConnectionRule. * Use case: * A local pair() call is made with the MAC address of m_mockBluetoothDevice1. * Expected result: * 1) m_mockBluetoothDevice1 pairs and connects. * 2) The observer should be notified of device connection once. * 3) One @c PairDevicesSucceeded and one @c ConnectByDeviceIdsSucceed event. */ TEST_F(BluetoothTest, test_handlePairLocal) { // Initially, all devices are stored as DeviceCategory::UNKNOWN. Need to manually update device category in order // to test. m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::PHONE)); std::vector events = {PAIR_DEVICES_SUCCEEDED, CONNECT_BY_DEVICE_IDS_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Call pair() m_Bluetooth->pair(TEST_BLUETOOTH_DEVICE_MAC); /* * Mimic the @c DeviceStateChangedEvent which should happen after device connection status changes. * In order to guarantee that all @c DeviceStateChangedEvent happen after the corresponding device connection * status changes, force the test to wait EVENT_PROCESS_DELAY_MS. */ std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS * 5)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::PAIRED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); })); // Verify the pair result. ASSERT_TRUE(m_mockBluetoothDevice1->isPaired()); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); } /** * Test call to handle UnpairDevices directive with matched device UUIDs. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. * m_mockBluetoothDevice1 belongs to DeviceCategory::PHONE, whcih follows @c BasicDeviceConnectionRule. * m_mockBluetoothDevice2 belongs to DeviceCategory::GADGET, which follows @c GadgetConnectionRule. * Both devices are in paired and connected state. * Use case: * First a @c PairDevices directive is sent down, whose payload contains m_mockBluetoothDevice1 * and m_mockBluetoothDevice2. * Then a @c UnpairDevices directive is sent down, whose payload contains m_mockBluetoothDevice1 * and m_mockBluetoothDevice2. * Expected result: * 1) Both m_mockBluetoothDevice1 and m_mockBluetoothDevice2 should be unpaired successfully. * 2) Based on the connection rule, both m_mockBluetoothDevice1 and m_mockBluetoothDevice2 should be diconnected * successfully. * 3) Events: * A sequence of {DISCONNECT_DEVICES_SUCCEEDED, UNPAIR_DEVICES_SUCCEEDED, DISCONNECT_DEVICES_SUCCEEDED, * UNPAIR_DEVICES_SUCCEEDED} should be sent to cloud: * a. {DISCONNECT_DEVICES_SUCCEEDED, UNPAIR_DEVICES_SUCCEEDED} are sent when Bluetooth CapabilityAgents tries to unpair * m_mockBluetoothDevice1. * b. {DISCONNECT_DEVICES_SUCCEEDED, UNPAIR_DEVICES_SUCCEEDED} are sent when Bluetooth CapabilityAgents tries to unpair * m_mockBluetoothDevice2. */ TEST_F(BluetoothTest, test_handleUnpairDevices) { // Initially, all devices are stored as DeviceCategory::UNKNOWN. Need to manually update device category in order // to test. m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::PHONE)); m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_2, deviceCategoryToString(DeviceCategory::GADGET)); // Assume all devices are paired and connected before. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice1->connect(); m_mockBluetoothDevice2->pair(); m_mockBluetoothDevice2->connect(); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); ASSERT_TRUE(m_mockBluetoothDevice2->isConnected()); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceDisconnected(_)).Times(Exactly(2)); EXPECT_CALL(*(m_mockDirectiveHandlerResult.get()), setCompleted()) .Times(1) .WillOnce(InvokeWithoutArgs(this, &BluetoothTest::wakeOnSetCompleted)); std::vector events = {DISCONNECT_DEVICES_SUCCEEDED, UNPAIR_DEVICES_SUCCEEDED, DISCONNECT_DEVICES_SUCCEEDED, UNPAIR_DEVICES_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { auto attachmentManager = std::make_shared>(); auto avsMessageHeader = std::make_shared(NAMESPACE_BLUETOOTH, UNPAIR_DEVICES_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_UNPAIR_DEVICES_PAYLOAD, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::DISCONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::UNPAIRED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::DISCONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::UNPAIRED)); })); // Verify the Unpair result. ASSERT_FALSE(m_mockBluetoothDevice1->isPaired()); ASSERT_FALSE(m_mockBluetoothDevice1->isConnected()); ASSERT_FALSE(m_mockBluetoothDevice2->isPaired()); ASSERT_FALSE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle the local unpair() method. * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1. * m_mockBluetoothDevice1 belongs to DeviceCategory::PHONE, which follows @c BasicDeviceConnectionRule. * Use case: * A local unpair() call is made with the MAC address of m_mockBluetoothDevice1. * Expected result: * 1) m_mockBluetoothDevice1 disconnects and unpairs. * 2) The observer should be notified of device disconnection once. * 3) One @c DisconnectDevicesSucceeded and one @c UnpairDevicesSucceeded event. */ TEST_F(BluetoothTest, test_handleUnpairLocal) { // Initially, all devices are stored as DeviceCategory::UNKNOWN. Need to manually update device category in order // to test. m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::PHONE)); // Assume all devices are paired and connected before. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice1->connect(); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); std::vector events = {DISCONNECT_DEVICES_SUCCEEDED, UNPAIR_DEVICES_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Call unpair() m_Bluetooth->unpair(TEST_BLUETOOTH_DEVICE_MAC); /* * Mimic the @c DeviceStateChangedEvent which should happen after device connection status changes. * In order to guarantee that all @c DeviceStateChangedEvent happen after the corresponding device connection * status changes, force the test to wait EVENT_PROCESS_DELAY_MS. */ std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::DISCONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::UNPAIRED)); })); // Verify the unpair result. ASSERT_FALSE(m_mockBluetoothDevice1->isPaired()); ASSERT_FALSE(m_mockBluetoothDevice1->isConnected()); } /** * Test call to handle DisconnectDevices directive with matched device UUIDs. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. * Both devices are in paired and connected state. * Use case: * A @c DisconnectDevices directive is sent down, whose payload contains m_mockBluetoothDevice1 * and m_mockBluetoothDevice2. * Expected result: * 1) Both m_mockBluetoothDevice1 and m_mockBluetoothDevice2 should be disconnected successfully. * 2) The observer should be notified the device disconnection once. * 3) Events: * A sequnce of {DISCONNECT_DEVICES_SUCCEEDED, DISCONNECT_DEVICES_SUCCEEDED} should be sent to cloud. One for * m_mockBluetoothDevice1, one for m_mockBluetoothDevice2. */ TEST_F(BluetoothTest, test_handleDisconnectDevices) { // Assume all devices are paired and connected before. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice1->connect(); m_mockBluetoothDevice2->pair(); m_mockBluetoothDevice2->connect(); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); ASSERT_TRUE(m_mockBluetoothDevice2->isConnected()); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceDisconnected(_)).Times(Exactly(2)); std::vector events = {DISCONNECT_DEVICES_SUCCEEDED, DISCONNECT_DEVICES_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { auto attachmentManager = std::make_shared>(); auto avsMessageHeader= std::make_shared(NAMESPACE_BLUETOOTH, DISCONNECT_DEVICES_DIRECTIVE, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_DISCONNECT_DEVICES_PAYLOAD, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedPromise = std::promise(); m_wakeSetCompletedFuture = m_wakeSetCompletedPromise.get_future(); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::DISCONNECTED)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::DISCONNECTED)); })); ASSERT_FALSE(m_mockBluetoothDevice1->isConnected()); ASSERT_FALSE(m_mockBluetoothDevice2->isConnected()); } /** * Test call to handle the local disconnect() method. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1. The device belongs to * DeviceCategory::UNKNOWN. The device is in connected state. * Use case: * A local disconnect() call is made with the MAC address of m_mockBluetoothDevice1. * Expected result: * 1) m_mockBluetoothDevice1 disconnects. * 2) The observer should be notified of device disconnection once. * 3) One @c DisconnectDevicesSucceeded event. */ TEST_F(BluetoothTest, test_handleDisconnectLocal) { // Assume all devices are paired and connected before. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice1->connect(); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); EXPECT_CALL(*m_mockBluetoothDeviceObserver, onActiveDeviceDisconnected(_)).Times(Exactly(1)); EXPECT_CALL(*m_mockContextManager, setState(BLUETOOTH_STATE, _, StateRefreshPolicy::NEVER, _)).Times(Exactly(1)); std::vector events = {DISCONNECT_DEVICES_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { // Call disconnect() m_Bluetooth->disconnect(TEST_BLUETOOTH_DEVICE_MAC); /* * Mimic the @c DeviceStateChangedEvent which should happen after device connection status changes. * In order to guarantee that all @c DeviceStateChangedEvent happen after the corresponding device connection * status changes, force the test to wait EVENT_PROCESS_DELAY_MS. */ std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::DISCONNECTED)); })); // Verify the disconnection result. ASSERT_FALSE(m_mockBluetoothDevice1->isConnected()); } /** * Test call to handle SetDeviceCategories directive with matched device UUID. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. Both of them belong to * DeviceCategory::UNKNOWN * Use case: * A @c SetDeviceCategories directive is sent down, whose payload contains m_mockBluetoothDevice1 (need to be set * to DeviceCategory::PHONE) and m_mockBluetoothDevice2 (need to be set to DeviceCategory::GADGET). * Expected result: * 1) Both devices are set the assigned device category successfully. * 2) Events: * A sequnce of {SET_DEVICE_CATEGORIES_SUCCEEDED, SET_DEVICE_CATEGORIES_SUCCEEDED} should be sent to cloud. One for * m_mockBluetoothDevice1, one for m_mockBluetoothDevice2. */ TEST_F(BluetoothTest, test_handleSetDeviceCategories) { std::vector events = {SET_DEVICE_CATEGORIES_SUCCEEDED}; ASSERT_TRUE(verifyMessagesSentInOrder(events, [this]() { auto attachmentManager = std::make_shared>(); auto avsMessageHeader= std::make_shared(NAMESPACE_BLUETOOTH, SET_DEVICE_CATEGORIES, TEST_MESSAGE_ID); std::shared_ptr directive = AVSDirective::create("", avsMessageHeader, TEST_SET_DEVICE_CATEGORIES_PAYLOAD, attachmentManager, ""); // cast to DirectiveHandlerInterface so the compiler can find the correct preHandleDirective signature std::shared_ptr agentAsDirectiveHandler = m_Bluetooth; agentAsDirectiveHandler->preHandleDirective(directive, std::move(m_mockDirectiveHandlerResult)); agentAsDirectiveHandler->handleDirective(TEST_MESSAGE_ID); m_wakeSetCompletedPromise = std::promise(); m_wakeSetCompletedFuture = m_wakeSetCompletedPromise.get_future(); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); m_Bluetooth->onContextAvailable(MOCK_CONTEXT); })); std::string category1; std::string category2; m_bluetoothStorage->getCategory(TEST_BLUETOOTH_UUID, &category1); m_bluetoothStorage->getCategory(TEST_BLUETOOTH_UUID_2, &category2); ASSERT_EQ(deviceCategoryToString(DeviceCategory::PHONE), category1); ASSERT_EQ(deviceCategoryToString(DeviceCategory::GADGET), category2); } TEST_F(BluetoothTest, test_contentDucksUponReceivingBackgroundFocus) { // Assume all devices are paired and connected before. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice1->connect(); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); // change streaming state to ACTIVE m_eventBus->sendEvent(MediaStreamingStateChangedEvent(avsCommon::utils::bluetooth::MediaStreamingState::ACTIVE, avsCommon::utils::bluetooth::A2DPRole::SOURCE, m_mockBluetoothDevice1)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); // ensure that music is not stopped when ducking. EXPECT_CALL(*m_mockBluetoothMediaPlayer, stop(_)).Times(0); EXPECT_CALL(*m_mockChannelVolumeInterface, startDucking()).Times(1); m_Bluetooth->onFocusChanged(avsCommon::avs::FocusState::BACKGROUND, avsCommon::avs::MixingBehavior::MAY_DUCK); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); } TEST_F(BluetoothTest, test_contentUnducksUponReceivingForegroundOrNoneFocus) { // Assume all devices are paired and connected before. m_mockBluetoothDevice1->pair(); m_mockBluetoothDevice1->connect(); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); ASSERT_TRUE(m_mockBluetoothDevice1->isConnected()); // change streaming state to ACTIVE. m_eventBus->sendEvent(MediaStreamingStateChangedEvent(avsCommon::utils::bluetooth::MediaStreamingState::ACTIVE, avsCommon::utils::bluetooth::A2DPRole::SOURCE, m_mockBluetoothDevice1)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); // ensure that music is not stopped when ducking upon receiving background focus. EXPECT_CALL(*m_mockBluetoothMediaPlayer, stop(_)).Times(0); EXPECT_CALL(*m_mockChannelVolumeInterface, startDucking()).Times(1); m_Bluetooth->onFocusChanged(avsCommon::avs::FocusState::BACKGROUND, avsCommon::avs::MixingBehavior::MAY_DUCK); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); // upon receiving foreground focus , content must be unducked. EXPECT_CALL(*m_mockChannelVolumeInterface, stopDucking()).Times(1); m_Bluetooth->onFocusChanged(avsCommon::avs::FocusState::FOREGROUND, avsCommon::avs::MixingBehavior::PRIMARY); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); // upon receiving foreground focus , content must be unducked. EXPECT_CALL(*m_mockChannelVolumeInterface, stopDucking()).Times(1); m_Bluetooth->onFocusChanged(avsCommon::avs::FocusState::NONE, avsCommon::avs::MixingBehavior::MUST_STOP); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); } /** * Test streaming state change of multiple device connections. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice1 and m_mockBluetoothDevice2. m_mockBluetoothDevice1 belongs to * DeviceCategory::AUDIO_VIDEO. m_mockBluetoothDevice2 belongs to DeviceCategory::PHONE. * Use case: * m_mockBluetoothDevice1 initiates connection and starts streaming audio. * Then m_mockBluetoothDevice2 initiates connection. * Expected Result: * 1) m_mockBluetoothDevice1 should connect. A @c StreamingStarted event should be sent. * 2) When m_mockBluetoothDevice2 connects, m_mockBluetoothDevice1 should be disconnected. A @c StreamingEnded event * should be sent. */ TEST_F(BluetoothTest, test_streamingStateChange) { // Initially, all devices are stored as DeviceCategory::UNKNOWN. Need to manually update device category in order // to test. m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID, deviceCategoryToString(DeviceCategory::AUDIO_VIDEO)); m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_2, deviceCategoryToString(DeviceCategory::PHONE)); EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) .Times(AtLeast(1)) .WillOnce(Invoke([this](std::shared_ptr request) { verifyMessage(request, STREAMING_STARTED); })) .WillOnce(Invoke([this](std::shared_ptr request) { verifyMessage(request, STREAMING_ENDED); })); // m_mockBluetoothDevice1 initiates connection and starts streaming media. m_mockBluetoothDevice1->connect(); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice1, DeviceState::CONNECTED)); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); m_eventBus->sendEvent(MediaStreamingStateChangedEvent( MediaStreamingState::ACTIVE, A2DPRole::SOURCE, m_mockBluetoothDevice1)); std::this_thread::sleep_for(std::chrono::milliseconds(EVENT_PROCESS_DELAY_MS)); // m_mockBluetoothDevice2 initiates connection. m_mockBluetoothDevice2->connect(); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice2, DeviceState::CONNECTED)); } /** * Test focus state change of barge-in scenario. * * Assumption: * A @c DeviceManager contains m_mockBluetoothDevice3, which belongs to DeviceCategory::PHONE. * Use case: * 1. m_mockBluetoothDevice3 initiates connection. Stream audio from m_mockBluetoothDevice3. * 2. A job(e.g, TTS) barge-in, which moves Bluetooth to background. * Exepected Result: * 1. m_mockBluetoothDevice3 should connect and start streaming audio. It should acquire focus. * 2. m_mockBluetoothDevice3 should pause streaming audio. However, it should NOT release focus. */ TEST_F(BluetoothTest, test_focusStateChange) { m_bluetoothStorage->updateByCategory(TEST_BLUETOOTH_UUID_3, deviceCategoryToString(DeviceCategory::PHONE)); EXPECT_CALL(*m_mockFocusManager, acquireChannel(_, _)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*m_mockBluetoothMediaPlayer, play(_)).Times(1).WillOnce(Return(true)); EXPECT_CALL(*m_mockFocusManager, releaseChannel(_, _)).Times(0); EXPECT_CALL(*m_mockBluetoothMediaPlayer, stop(_)).Times(1).WillOnce(Return(true)); m_mockBluetoothDevice3->connect(); m_eventBus->sendEvent(DeviceStateChangedEvent(m_mockBluetoothDevice3, DeviceState::CONNECTED)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); m_eventBus->sendEvent(MediaStreamingStateChangedEvent( MediaStreamingState::ACTIVE, A2DPRole::SINK, m_mockBluetoothDevice3)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); m_Bluetooth->onFocusChanged(avsCommon::avs::FocusState::FOREGROUND, avsCommon::avs::MixingBehavior::PRIMARY); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); m_Bluetooth->onPlaybackStarted(m_mockBluetoothMediaPlayer->getCurrentSourceId(), {std::chrono::milliseconds(0)}); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); // The other job barges in, which moves Bluetooth to background. m_Bluetooth->onFocusChanged(avsCommon::avs::FocusState::BACKGROUND, avsCommon::avs::MixingBehavior::MUST_STOP); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); m_eventBus->sendEvent(MediaStreamingStateChangedEvent( MediaStreamingState::IDLE, A2DPRole::SINK, m_mockBluetoothDevice3)); m_wakeSetCompletedFuture.wait_for(WAIT_TIMEOUT_MS); } } // namespace test } // namespace acsdkBluetooth } // namespace alexaClientSDK