/* * 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 "Settings/MockSettingEventSender.h" #include "Settings/MockDeviceSettingStorage.h" namespace alexaClientSDK { namespace settings { namespace test { using namespace testing; using namespace storage::test; using namespace avsCommon::utils; using namespace avsCommon::sdkInterfaces::test; /// A dummy setting metadata. const SettingEventMetadata METADATA = {"namespace", "ChangedName", "ReportName", "setting"}; /// Constant representing a valid database value. static const std::string DB_VALUE = R"("db-value")"; /// Constant representing a default value. static const std::string DEFAULT_VALUE = R"("default-value")"; /// Constant representing a valid new value. static const std::string NEW_VALUE = R"("new-value")"; /// Empty string used to represent invalid value by the protocol. static const std::string INVALID_VALUE = ""; /// The database key to be used by the protocol given the @c METADATA object. static const std::string key = METADATA.eventNamespace + "::" + METADATA.settingName; /// The timeout used throughout the tests. static const auto TEST_TIMEOUT = std::chrono::seconds(5); /** * Object that allow us to mock the callback functions. */ class MockCallbacks { public: MOCK_METHOD0(applyChange, std::pair()); MOCK_METHOD0(revertChange, std::string()); MOCK_METHOD1(applyDbChange, std::pair(const std::string& dbValue)); MOCK_METHOD1(notifyObservers, void(SettingNotifications)); }; /** * The test class. */ class SharedAVSSettingProtocolTest : public Test { protected: /** * Create protocol object and dependencies mocks. */ void SetUp() override; /** * A helper method to change the value of the setting. * * @param value The string value of the setting. * @param isLocal True means the setting changed is done locally, otherwise, it is a cloud-driven change. */ void modifySetting(std::string value, bool isLocal); /** * A helper method to test multiple value changes of the setting. * */ void testMultipleChanges(bool isLocal); /// Mock event sender. std::shared_ptr m_senderMock; /// Mock setting storage std::shared_ptr m_storageMock; /// Pointer to a mock protocol. std::unique_ptr m_protocol; /// Mock callbacks. StrictMock m_callbacksMock; /// Mock connection manager; std::shared_ptr m_mockConnectionManager; }; void SharedAVSSettingProtocolTest::SetUp() { m_senderMock = std::make_shared>(); m_storageMock = std::make_shared>(); m_mockConnectionManager = std::make_shared>(); m_protocol = SharedAVSSettingProtocol::create(METADATA, m_senderMock, m_storageMock, m_mockConnectionManager); } void SharedAVSSettingProtocolTest::modifySetting(std::string value, bool isLocal) { auto executeSet = [value] { return std::make_pair(true, value); }; auto revertChange = [] { return INVALID_VALUE; }; auto notifyObservers = [](SettingNotifications notification) {}; if (isLocal) { m_protocol->localChange(executeSet, revertChange, notifyObservers); } else { m_protocol->avsChange(executeSet, revertChange, notifyObservers); } } void SharedAVSSettingProtocolTest::testMultipleChanges(bool isLocal) { // The number of setting value changes. const int numValuesToSet = 10; // Event notification that all the setting changes has been requested. WaitEvent lastValueSet; // Event notification when the AVS event for the last setting change has been sent. WaitEvent lastValueSentEvent; // Make all storage operations successful. EXPECT_CALL(*m_storageMock, storeSetting(key, _, _)).WillRepeatedly(Return(true)); EXPECT_CALL(*m_storageMock, updateSettingStatus(_, _)).WillRepeatedly(Return(true)); // It is expected that one of the intermediate values may be sent or it may not be sent at all, since they are // replaced by the last value. if (isLocal) { EXPECT_CALL(*m_senderMock, sendChangedEvent(_)) .Times(Between(0, 1)) .WillRepeatedly(InvokeWithoutArgs([&lastValueSet] { // Simulate a delay in sending the event by blocking until all setting changes has been set. // This cause any pending events to merge. lastValueSet.wait(TEST_TIMEOUT); std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); } else { EXPECT_CALL(*m_senderMock, sendReportEvent(_)) .Times(Between(0, 1)) .WillRepeatedly(InvokeWithoutArgs([&lastValueSet] { // Simulate a delay in sending the event by blocking until all setting changes has been set. // This cause any pending events to merge. lastValueSet.wait(TEST_TIMEOUT); std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); } // Last value should be sent. if (isLocal) { EXPECT_CALL(*m_senderMock, sendChangedEvent(std::to_string(numValuesToSet))) .WillOnce(InvokeWithoutArgs([&lastValueSentEvent] { lastValueSentEvent.wakeUp(); std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); } else { EXPECT_CALL(*m_senderMock, sendReportEvent(std::to_string(numValuesToSet))) .WillOnce(InvokeWithoutArgs([&lastValueSentEvent] { lastValueSentEvent.wakeUp(); std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); } // Change the setting value multiple times. for (int i = 1; i <= numValuesToSet; i++) { modifySetting(std::to_string(i), isLocal); } lastValueSet.wakeUp(); // This expectation verifies the merging action. EXPECT_TRUE(lastValueSentEvent.wait(TEST_TIMEOUT)); // Below is an extra sanity test to verify that we can still send another event after sending a merged event. // Send another value and see if it still can be sent. const int newValue = numValuesToSet + 1; WaitEvent sentEvent; if (isLocal) { EXPECT_CALL(*m_senderMock, sendChangedEvent(std::to_string(newValue))).WillOnce(InvokeWithoutArgs([&sentEvent] { sentEvent.wakeUp(); std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); } else { EXPECT_CALL(*m_senderMock, sendReportEvent(std::to_string(newValue))).WillOnce(InvokeWithoutArgs([&sentEvent] { sentEvent.wakeUp(); std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); } modifySetting(std::to_string(newValue), isLocal); EXPECT_TRUE(sentEvent.wait(TEST_TIMEOUT)); } /// Test create with null event sender. TEST_F(SharedAVSSettingProtocolTest, test_nullEventSender) { ASSERT_FALSE(SharedAVSSettingProtocol::create(METADATA, nullptr, m_storageMock, m_mockConnectionManager)); } /// Test create with null storage. TEST_F(SharedAVSSettingProtocolTest, test_nullStorage) { ASSERT_FALSE(SharedAVSSettingProtocol::create(METADATA, m_senderMock, nullptr, m_mockConnectionManager)); } /// Test restore when value is not available in the database. TEST_F(SharedAVSSettingProtocolTest, test_restoreValueNotAvailable) { WaitEvent settingsUpdatedEvent; EXPECT_CALL(*m_storageMock, loadSetting(key)).WillOnce(Return(std::make_pair(SettingStatus::NOT_AVAILABLE, ""))); EXPECT_CALL(*m_storageMock, storeSetting(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(*m_storageMock, updateSettingStatus(_, _)).WillOnce(InvokeWithoutArgs([&settingsUpdatedEvent] { settingsUpdatedEvent.wakeUp(); return true; })); EXPECT_CALL(m_callbacksMock, applyDbChange(INVALID_VALUE)).WillOnce(Return(std::make_pair(true, DEFAULT_VALUE))); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE)); EXPECT_CALL(*m_senderMock, sendChangedEvent(_)).WillOnce(InvokeWithoutArgs([&]() { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); m_protocol->restoreValue( std::bind(&MockCallbacks::applyDbChange, &m_callbacksMock, std::placeholders::_1), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); settingsUpdatedEvent.wait(TEST_TIMEOUT); } /// Test restore when value is not available in the database for a setting that use cloud authoritative default value. TEST_F(SharedAVSSettingProtocolTest, test_restoreValueNotAvailableCloudAuthoritative) { WaitEvent settingsUpdatedEvent; EXPECT_CALL(*m_storageMock, loadSetting(key)).WillOnce(Return(std::make_pair(SettingStatus::NOT_AVAILABLE, ""))); EXPECT_CALL(*m_storageMock, storeSetting(_, _, _)).WillOnce(Return(true)); EXPECT_CALL(*m_storageMock, updateSettingStatus(_, _)).WillOnce(InvokeWithoutArgs([&settingsUpdatedEvent] { settingsUpdatedEvent.wakeUp(); return true; })); EXPECT_CALL(m_callbacksMock, applyDbChange(INVALID_VALUE)).WillOnce(Return(std::make_pair(true, DEFAULT_VALUE))); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE)); EXPECT_CALL(*m_senderMock, sendReportEvent(_)).WillOnce(InvokeWithoutArgs([&]() { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); auto protocol = SharedAVSSettingProtocol::create(METADATA, m_senderMock, m_storageMock, m_mockConnectionManager, true); protocol->restoreValue( std::bind(&MockCallbacks::applyDbChange, &m_callbacksMock, std::placeholders::_1), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); settingsUpdatedEvent.wait(TEST_TIMEOUT); } /// Test restore when the value is available and synchronized. TEST_F(SharedAVSSettingProtocolTest, test_restoreSynchronized) { EXPECT_CALL(*m_storageMock, loadSetting(key)) .WillOnce(Return(std::make_pair(SettingStatus::SYNCHRONIZED, DB_VALUE))); EXPECT_CALL(m_callbacksMock, applyDbChange(DB_VALUE)).WillOnce(Return(std::make_pair(true, DB_VALUE))); m_protocol->restoreValue( std::bind(&MockCallbacks::applyDbChange, &m_callbacksMock, std::placeholders::_1), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); } /// Test success flow for AVS request. TEST_F(SharedAVSSettingProtocolTest, test_AVSChangeRequest) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::AVS_CHANGE_IN_PROGRESS)) .WillOnce(Return(true)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE)); EXPECT_CALL(*m_senderMock, sendReportEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([&] { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); EXPECT_CALL(*m_storageMock, updateSettingStatus(key, SettingStatus::SYNCHRONIZED)) .WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); return true; })); m_protocol->avsChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test set value failed for AVS request. TEST_F(SharedAVSSettingProtocolTest, test_AVSChangeRequestSetFailed) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(false, DB_VALUE))); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_FAILED)); EXPECT_CALL(*m_senderMock, sendReportEvent(DB_VALUE)).WillOnce(InvokeWithoutArgs([&] { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); EXPECT_CALL(*m_storageMock, updateSettingStatus(key, SettingStatus::SYNCHRONIZED)) .WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); return true; })); m_protocol->avsChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test store value failed. Revert should be called and AVS notified of previous value. TEST_F(SharedAVSSettingProtocolTest, test_AVSChangeRequestStoreFailed) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::AVS_CHANGE_IN_PROGRESS)) .WillOnce(Return(false)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_FAILED)); EXPECT_CALL(m_callbacksMock, revertChange()).WillOnce(Return(DEFAULT_VALUE)); EXPECT_CALL(*m_senderMock, sendReportEvent(DEFAULT_VALUE)).WillOnce(InvokeWithoutArgs([&] { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); EXPECT_CALL(*m_storageMock, updateSettingStatus(key, SettingStatus::SYNCHRONIZED)) .WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); return true; })); m_protocol->avsChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test send event failed for avs request. TEST_F(SharedAVSSettingProtocolTest, test_AVSChangeRequestSendEventFailed) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::AVS_CHANGE_IN_PROGRESS)) .WillOnce(Return(true)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE)); EXPECT_CALL(*m_senderMock, sendReportEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); std::promise retPromise; retPromise.set_value(false); return retPromise.get_future(); })); m_protocol->avsChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test success flow for local request. TEST_F(SharedAVSSettingProtocolTest, test_localRequest) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::LOCAL_CHANGE_IN_PROGRESS)) .WillOnce(Return(true)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE)); EXPECT_CALL(*m_senderMock, sendChangedEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([&]() { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); EXPECT_CALL(*m_storageMock, updateSettingStatus(key, SettingStatus::SYNCHRONIZED)) .WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); return true; })); m_protocol->localChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test set value failed during local request. TEST_F(SharedAVSSettingProtocolTest, test_localRequestSetFailed) { InSequence inSequence; EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(false, DB_VALUE))); WaitEvent event; auto notifyObservers = [&event](SettingNotifications notification) { switch (notification) { case SettingNotifications::AVS_CHANGE_IN_PROGRESS: case SettingNotifications::LOCAL_CHANGE: case SettingNotifications::AVS_CHANGE: case SettingNotifications::AVS_CHANGE_FAILED: case SettingNotifications::LOCAL_CHANGE_CANCELLED: case SettingNotifications::AVS_CHANGE_CANCELLED: FAIL() << notification; break; case SettingNotifications::LOCAL_CHANGE_IN_PROGRESS: break; case SettingNotifications::LOCAL_CHANGE_FAILED: event.wakeUp(); break; } }; m_protocol->localChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), notifyObservers); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test store value to database failed for local request. TEST_F(SharedAVSSettingProtocolTest, test_localRequestStoreFailed) { InSequence inSequence; EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::LOCAL_CHANGE_IN_PROGRESS)) .WillOnce(Return(false)); EXPECT_CALL(m_callbacksMock, revertChange()).WillOnce(Return(DEFAULT_VALUE)); WaitEvent event; auto notifyObservers = [&event](SettingNotifications notification) { switch (notification) { case SettingNotifications::AVS_CHANGE_IN_PROGRESS: case SettingNotifications::LOCAL_CHANGE: case SettingNotifications::AVS_CHANGE: case SettingNotifications::AVS_CHANGE_FAILED: case SettingNotifications::LOCAL_CHANGE_CANCELLED: case SettingNotifications::AVS_CHANGE_CANCELLED: FAIL() << notification; break; case SettingNotifications::LOCAL_CHANGE_IN_PROGRESS: break; case SettingNotifications::LOCAL_CHANGE_FAILED: event.wakeUp(); break; } }; m_protocol->localChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), notifyObservers); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Test send changed event failed for local request. In this case, we should not update the database with SYNCHRONIZED. TEST_F(SharedAVSSettingProtocolTest, test_localRequestSendEventFailed) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::LOCAL_CHANGE_IN_PROGRESS)) .WillOnce(Return(true)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE)); EXPECT_CALL(*m_senderMock, sendChangedEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); std::promise retPromise; retPromise.set_value(false); return retPromise.get_future(); })); m_protocol->localChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Verify that after network disconnect / reconnect will send out setting events that was changed locally while /// disconnected. TEST_F(SharedAVSSettingProtocolTest, test_localChangeSettingOfflineSynchronization) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::LOCAL_CHANGE_IN_PROGRESS)) .WillOnce(Return(true)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::LOCAL_CHANGE)); // Return false to simulate a disconnected network. EXPECT_CALL(*m_senderMock, sendChangedEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); std::promise retPromise; retPromise.set_value(false); return retPromise.get_future(); })); m_protocol->localChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); event.reset(); EXPECT_CALL(*m_storageMock, loadSetting(key)) .WillOnce(Return(std::make_pair(SettingStatus::LOCAL_CHANGE_IN_PROGRESS, NEW_VALUE))); EXPECT_CALL(*m_senderMock, sendChangedEvent(_)).WillOnce(InvokeWithoutArgs([] { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); EXPECT_CALL(*m_storageMock, updateSettingStatus(key, SettingStatus::SYNCHRONIZED)) .WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); return true; })); m_protocol->connectionStatusChangeCallback(true); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); event.reset(); } /// Verify that after network disconnect / reconnect will send out setting events that was changed through AVS /// directive. TEST_F(SharedAVSSettingProtocolTest, test_avsChangeSettingOfflineSynchronization) { WaitEvent event; InSequence inSequence; EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE_IN_PROGRESS)); EXPECT_CALL(m_callbacksMock, applyChange()).WillOnce(Return(std::make_pair(true, NEW_VALUE))); EXPECT_CALL(*m_storageMock, storeSetting(key, NEW_VALUE, SettingStatus::AVS_CHANGE_IN_PROGRESS)) .WillOnce(Return(true)); EXPECT_CALL(m_callbacksMock, notifyObservers(SettingNotifications::AVS_CHANGE)); // Return false to simulate a disconnected network. EXPECT_CALL(*m_senderMock, sendReportEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); std::promise retPromise; retPromise.set_value(false); return retPromise.get_future(); })); m_protocol->avsChange( std::bind(&MockCallbacks::applyChange, &m_callbacksMock), std::bind(&MockCallbacks::revertChange, &m_callbacksMock), std::bind(&MockCallbacks::notifyObservers, &m_callbacksMock, std::placeholders::_1)); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); event.reset(); EXPECT_CALL(*m_storageMock, loadSetting(key)) .WillOnce(Return(std::make_pair(SettingStatus::AVS_CHANGE_IN_PROGRESS, NEW_VALUE))); EXPECT_CALL(*m_senderMock, sendReportEvent(NEW_VALUE)).WillOnce(InvokeWithoutArgs([] { std::promise retPromise; retPromise.set_value(true); return retPromise.get_future(); })); EXPECT_CALL(*m_storageMock, updateSettingStatus(key, SettingStatus::SYNCHRONIZED)) .WillOnce(InvokeWithoutArgs([&event] { event.wakeUp(); return true; })); m_protocol->connectionStatusChangeCallback(true); EXPECT_TRUE(event.wait(TEST_TIMEOUT)); } /// Verify that multiple AVS setting changes will be merged. /// The setting will be changed multiple times immediately one after the other. /// It's expected that only at most 2 settings are sent, that is, at least 1 has been canceled and the last value sent /// is the latest one. TEST_F(SharedAVSSettingProtocolTest, test_multipleAVSChanges) { testMultipleChanges(false); } /// Verify that multiple local setting changes will be merged. /// The setting will be changed multiple times immediately one after the other. /// It's expected that only at most 2 settings are sent, that is, at least 1 has been canceled and the last value sent /// is the latest one. TEST_F(SharedAVSSettingProtocolTest, test_multipleLocalChanges) { testMultipleChanges(true); } } // namespace test } // namespace settings } // namespace alexaClientSDK